def Pall_mass_density_at_odd_prime(self, p): """ Returns the local representation density of a form (for representing itself) defined over `ZZ`, at some prime `p>2`. REFERENCES: Pall's article "The Weight of a Genus of Positive n-ary Quadratic Forms" appearing in Proc. Symp. Pure Math. VIII (1965), pp95--105. INPUT: `p` -- a prime number > 2. OUTPUT: a rational number. EXAMPLES:: sage: Q = QuadraticForm(ZZ, 3, [1,0,0,1,0,1]) sage: Q.Pall_mass_density_at_odd_prime(3) [(0, Quadratic form in 3 variables over Integer Ring with coefficients: [ 1 0 0 ] [ * 1 0 ] [ * * 1 ])] [(0, 3, 8)] [8/9] 8/9 8/9 """ ## Check that p is a positive prime -- unnecessary since it's done implicitly in the next step. =) if p<=2: raise TypeError, "Oops! We need p to be a prime > 2." ## Step 1: Obtain a p-adic (diagonal) local normal form, and ## compute the invariants for each Jordan block. jordan_list = self.jordan_blocks_by_scale_and_unimodular(p) modified_jordan_list = [(a, Q.dim(), Q.det()) for (a,Q) in jordan_list] ## List of pairs (scale, det) #print jordan_list #print modified_jordan_list ## Step 2: Compute the list of local masses for each Jordan block jordan_mass_list = [] for (s,n,d) in modified_jordan_list: generic_factor = prod([1 - p**(-2*j) for j in range(1, floor((n-1)/2)+1)]) #print "generic factor: ", generic_factor if (n % 2 == 0): m = n/2 generic_factor *= (1 + legendre_symbol(((-1)**m) * d, p) * p**(-m)) #print "jordan_mass: ", generic_factor jordan_mass_list = jordan_mass_list + [generic_factor] ## Step 3: Compute the local mass $\al_p$ at p. MJL = modified_jordan_list s = len(modified_jordan_list) M = [sum([MJL[j][1] for j in range(i, s)]) for i in range(s-1)] ## Note: It's s-1 since we don't need the last M. #print "M = ", M nu = sum([M[i] * MJL[i][0] * MJL[i][1] for i in range(s-1)]) - ZZ(sum([J[0] * J[1] * (J[1]-1) for J in MJL]))/ZZ(2) p_mass = prod(jordan_mass_list) p_mass *= 2**(s-1) * p**nu print jordan_list, MJL, jordan_mass_list, p_mass ## Return the result return p_mass
def least_quadratic_nonresidue(p): """ Returns the smallest positive integer quadratic non-residue in Z/pZ for primes p>2. EXAMPLES:: sage: least_quadratic_nonresidue(5) 2 sage: [least_quadratic_nonresidue(p) for p in prime_range(3,100)] [2, 2, 3, 2, 2, 3, 2, 5, 2, 3, 2, 3, 2, 5, 2, 2, 2, 2, 7, 5, 3, 2, 3, 5] TESTS: Raises an error if input is a positive composite integer. :: sage: least_quadratic_nonresidue(20) Traceback (most recent call last): ... ValueError: Oops! p must be a prime number > 2. Raises an error if input is 2. This is because every integer is a quadratic residue modulo 2. :: sage: least_quadratic_nonresidue(2) Traceback (most recent call last): ... ValueError: Oops! There are no quadratic non-residues in Z/2Z. """ from sage.functions.all import floor p1 = abs(p) ## Deal with the prime p = 2 and |p| <= 1. if p1 == 2: raise ValueError("Oops! There are no quadratic non-residues in Z/2Z.") if p1 < 2: raise ValueError("Oops! p must be a prime number > 2.") ## Find the smallest non-residue mod p ## For 7/8 of primes the answer is 2, 3 or 5: if p%8 in (3,5): return ZZ(2) if p%12 in (5,7): return ZZ(3) if p%5 in (2,3): return ZZ(5) ## default case (first needed for p=71): if not p.is_prime(): raise ValueError("Oops! p must be a prime number > 2.") from sage.misc.misc import xsrange for r in xsrange(7,p): if legendre_symbol(r, p) == -1: return ZZ(r)
def Pall_mass_density_at_odd_prime(self, p): """ Returns the local representation density of a form (for representing itself) defined over `ZZ`, at some prime `p>2`. REFERENCES: Pall's article "The Weight of a Genus of Positive n-ary Quadratic Forms" appearing in Proc. Symp. Pure Math. VIII (1965), pp95--105. INPUT: `p` -- a prime number > 2. OUTPUT: a rational number. EXAMPLES:: sage: Q = QuadraticForm(ZZ, 3, [1,0,0,1,0,1]) sage: Q.Pall_mass_density_at_odd_prime(3) [(0, Quadratic form in 3 variables over Integer Ring with coefficients: [ 1 0 0 ] [ * 1 0 ] [ * * 1 ])] [(0, 3, 8)] [8/9] 8/9 8/9 """ ## Check that p is a positive prime -- unnecessary since it's done implicitly in the next step. =) if p <= 2: raise TypeError("Oops! We need p to be a prime > 2.") ## Step 1: Obtain a p-adic (diagonal) local normal form, and ## compute the invariants for each Jordan block. jordan_list = self.jordan_blocks_by_scale_and_unimodular(p) modified_jordan_list = [(a, Q.dim(), Q.det()) for (a, Q) in jordan_list ] ## List of pairs (scale, det) #print jordan_list #print modified_jordan_list ## Step 2: Compute the list of local masses for each Jordan block jordan_mass_list = [] for (s, n, d) in modified_jordan_list: generic_factor = prod( [1 - p**(-2 * j) for j in range(1, floor((n - 1) / 2) + 1)]) #print "generic factor: ", generic_factor if (n % 2 == 0): m = n / 2 generic_factor *= (1 + legendre_symbol(((-1)**m) * d, p) * p**(-m)) #print "jordan_mass: ", generic_factor jordan_mass_list = jordan_mass_list + [generic_factor] ## Step 3: Compute the local mass $\al_p$ at p. MJL = modified_jordan_list s = len(modified_jordan_list) M = [sum([MJL[j][1] for j in range(i, s)]) for i in range(s - 1) ] ## Note: It's s-1 since we don't need the last M. #print "M = ", M nu = sum([M[i] * MJL[i][0] * MJL[i][1] for i in range(s - 1) ]) - ZZ(sum([J[0] * J[1] * (J[1] - 1) for J in MJL])) / ZZ(2) p_mass = prod(jordan_mass_list) p_mass *= 2**(s - 1) * p**nu print jordan_list, MJL, jordan_mass_list, p_mass ## Return the result return p_mass
def _next_good_prime(p, R, qq, patience, qqold): """ Find the next prime `\\ell` which is good by ``qq`` but not by ``qqold``, 1 mod ``p``, and for which ``b^2+4*c`` is a square mod `\\ell`, for the sequence ``R`` if it is possible in runtime patience. INPUT: - ``p`` -- a prime - ``R`` -- an object in the class ``BinaryRecurrenceSequence`` - ``qq`` -- a perfect power - ``patience`` -- a real number - ``qqold`` -- a perfect power less than or equal to ``qq`` OUTPUT: - A prime `\\ell` such that `\\ell` is 1 mod ``p``, ``b^2+4*c`` is a square mod `\\ell` and the period of `\\ell` has ``goodness`` by ``qq`` but not ``qqold``, if patience has not be surpased. Otherwise ``False``. EXAMPLES:: sage: R = BinaryRecurrenceSequence(1,1) sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,1,100,1) #ran out of patience to search for good primes False sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,2,100,1) 29 sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,2,100,2) #ran out of patience, as qqold == qq, so no primes work False """ #We are looking for pth powers in R. #Our primes must be good by qq, but not qqold. #We only allow patience number of iterations to find a good prime. #The variable _ell for R keeps track of the last "good" prime returned #that was not found from the dictionary _PGoodness #First, we check to see if we have already computed the goodness of a prime that fits #our requirement of being good by qq but not by qqold. This is stored in the _PGoodness #dictionary. #Then if we have, we return the smallest such prime and delete it from the list. If not, we #search through patience number of primes R._ell to find one good by qq but not qqold. If it is #not good by either qqold or qq, then we add this prime to R._PGoodness under its goodness. #Possible_Primes keeps track of possible primes satisfying our goodness requirements we might return Possible_Primes = [] #check to see if anything in R._PGoodness fits our goodness requirements for j in R._PGoodness: if (qqold < j <= qq) and len(R._PGoodness[j]): Possible_Primes.append(R._PGoodness[j][0]) #If we found good primes, we take the smallest if Possible_Primes != []: q = min(Possible_Primes) n = _goodness(q, R, p) del R._PGoodness[n][ 0] #if we are going to use it, then we delete it from R._PGoodness return q #If nothing is already stored in R._PGoodness, we start (from where we left off at R._ell) checking #for good primes. We only tolerate patience number of tries before giving up. else: i = 0 while i < patience: i += 1 R._ell = next_prime(R._ell) #we require that R._ell is 1 mod p, so that p divides the order of the multiplicative #group mod R._ell, so that not all elements of GF(R._ell) are pth powers. if R._ell % p == 1: #requiring that b^2 + 4c is a square in GF(R._ell) ensures that the period mod R._ell #divides R._ell - 1 if legendre_symbol(R.b**2 + 4 * R.c, R._ell) == 1: N = _goodness(R._ell, R, p) #proceed only if R._ell statisfies the goodness requirements if qqold < N <= qq: return R._ell #if we do not use the prime, we store it in R._PGoodness else: if N in R._PGoodness: R._PGoodness[N].append(R._ell) else: R._PGoodness[N] = [R._ell] return False
def has_equivalent_Jordan_decomposition_at_prime(self, other, p): """ Determines if the given quadratic form has a Jordan decomposition equivalent to that of self. INPUT: a QuadraticForm OUTPUT: boolean EXAMPLES:: sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 1, 0, 3]) sage: Q2 = QuadraticForm(ZZ, 3, [1, 0, 0, 2, -2, 6]) sage: Q3 = QuadraticForm(ZZ, 3, [1, 0, 0, 1, 0, 11]) sage: [Q1.level(), Q2.level(), Q3.level()] [44, 44, 44] sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q2,2) False sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q2,11) False sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q3,2) False sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q3,11) True sage: Q2.has_equivalent_Jordan_decomposition_at_prime(Q3,2) True sage: Q2.has_equivalent_Jordan_decomposition_at_prime(Q3,11) False """ ## Sanity Checks #if not isinstance(other, QuadraticForm): if not isinstance(other, type(self)): raise TypeError( "Oops! The first argument must be of type QuadraticForm.") if not is_prime(p): raise TypeError("Oops! The second argument must be a prime number.") ## Get the relevant local normal forms quickly self_jordan = self.jordan_blocks_by_scale_and_unimodular(p, safe_flag=False) other_jordan = other.jordan_blocks_by_scale_and_unimodular(p, safe_flag=False) ## DIAGNOSTIC #print "self_jordan = ", self_jordan #print "other_jordan = ", other_jordan ## Check for the same number of Jordan components if len(self_jordan) != len(other_jordan): return False ## Deal with odd primes: Check that the Jordan component scales, dimensions, and discriminants are the same if p != 2: for i in range(len(self_jordan)): if (self_jordan[i][0] != other_jordan[i][0]) \ or (self_jordan[i][1].dim() != other_jordan[i][1].dim()) \ or (legendre_symbol(self_jordan[i][1].det() * other_jordan[i][1].det(), p) != 1): return False ## All tests passed for an odd prime. return True ## For p = 2: Check that all Jordan Invariants are the same. elif p == 2: ## Useful definition t = len(self_jordan) ## Define t = Number of Jordan components ## Check that all Jordan Invariants are the same (scale, dim, and norm) for i in range(t): if (self_jordan[i][0] != other_jordan[i][0]) \ or (self_jordan[i][1].dim() != other_jordan[i][1].dim()) \ or (valuation(GCD(self_jordan[i][1].coefficients()), p) != valuation(GCD(other_jordan[i][1].coefficients()), p)): return False ## DIAGNOSTIC #print "Passed the Jordan invariant test." ## Use O'Meara's isometry test 93:29 on p277. ## ------------------------------------------ ## List of norms, scales, and dimensions for each i scale_list = [ZZ(2)**self_jordan[i][0] for i in range(t)] norm_list = [ ZZ(2)**(self_jordan[i][0] + valuation(GCD(self_jordan[i][1].coefficients()), 2)) for i in range(t) ] dim_list = [(self_jordan[i][1].dim()) for i in range(t)] ## List of Hessian determinants and Hasse invariants for each Jordan (sub)chain ## (Note: This is not the same as O'Meara's Gram determinants, but ratios are the same!) -- NOT SO GOOD... ## But it matters in condition (ii), so we multiply all by 2 (instead of dividing by 2 since only square-factors matter, and it's easier.) j = 0 self_chain_det_list = [ self_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j]) ] other_chain_det_list = [ other_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j]) ] self_hasse_chain_list = [ self_jordan[j][1].scale_by_factor( ZZ(2)**self_jordan[j][0]).hasse_invariant__OMeara(2) ] other_hasse_chain_list = [ other_jordan[j][1].scale_by_factor( ZZ(2)**other_jordan[j][0]).hasse_invariant__OMeara(2) ] for j in range(1, t): self_chain_det_list.append(self_chain_det_list[j - 1] * self_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])) other_chain_det_list.append(other_chain_det_list[j - 1] * other_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])) self_hasse_chain_list.append(self_hasse_chain_list[j-1] \ * hilbert_symbol(self_chain_det_list[j-1], self_jordan[j][1].Gram_det(), 2) \ * self_jordan[j][1].hasse_invariant__OMeara(2)) other_hasse_chain_list.append(other_hasse_chain_list[j-1] \ * hilbert_symbol(other_chain_det_list[j-1], other_jordan[j][1].Gram_det(), 2) \ * other_jordan[j][1].hasse_invariant__OMeara(2)) ## SANITY CHECK -- check that the scale powers are strictly increasing for i in range(1, len(scale_list)): if scale_list[i - 1] >= scale_list[i]: raise RuntimeError( "Oops! There is something wrong with the Jordan Decomposition -- the given scales are not strictly increasing!" ) ## DIAGNOSTIC #print "scale_list = ", scale_list #print "norm_list = ", norm_list #print "dim_list = ", dim_list #print #print "self_chain_det_list = ", self_chain_det_list #print "other_chain_det_list = ", other_chain_det_list #print "self_hasse_chain_list = ", self_hasse_chain_list #print "other_hasse_chain_det_list = ", other_hasse_chain_list ## Test O'Meara's two conditions for i in range(t - 1): ## Condition (i): Check that their (unit) ratio is a square (but it suffices to check at most mod 8). modulus = norm_list[i] * norm_list[i + 1] / (scale_list[i]**2) if modulus > 8: modulus = 8 if (modulus > 1) and (( (self_chain_det_list[i] / other_chain_det_list[i]) % modulus) != 1): #print "Failed when i =", i, " in condition 1." return False ## Check O'Meara's condition (ii) when appropriate if norm_list[i + 1] % (4 * norm_list[i]) == 0: if self_hasse_chain_list[i] * hilbert_symbol(norm_list[i] * other_chain_det_list[i], -self_chain_det_list[i], 2) \ != other_hasse_chain_list[i] * hilbert_symbol(norm_list[i], -other_chain_det_list[i], 2): ## Nipp conditions #print "Failed when i =", i, " in condition 2." return False ## All tests passed for the prime 2. return True else: raise TypeError("Oops! This should not have happened.")
def _exceptionals(E, L, patience=1000): r""" Determine which primes in L are exceptional for E, using Proposition 19 of Section 2.8 of Serre's ``Proprietes Galoisiennes des Points d'Ordre Fini des Courbes Elliptiques'' [Serre72]. INPUT: - ``E`` - EllipticCurve - over a number field. - ``L`` - list - a list of prime numbers. - ``patience`` - int (a bound on the number of traces of Frobenius to use while trying to prove surjectivity). OUTPUT: list - The list of all primes l in L for which the mod l image might fail to be surjective. EXAMPLES:: sage: K = NumberField(x**2 - 29, 'a'); a = K.gen() sage: E = EllipticCurve([1, 0, ((5 + a)/2)**2, 0, 0]) sage: sage.schemes.elliptic_curves.gal_reps_number_field._exceptionals(E, [29, 31]) [29] """ E = _over_numberfield(E) K = E.base_field() output = [] L = list(set(L)) # Remove duplicates from L. for l in L: if l == 2: # c.f. Section 5.3(a) of [Serre72]. if (E.j_invariant() - 1728).is_square(): output.append(2) elif not E.division_polynomial(2).is_irreducible(): output.append(2) elif l == 3: # c.f. Section 5.3(b) of [Serre72]. if K(-3).is_square(): output.append(3) elif not (K['x'].gen()**3 - E.j_invariant()).is_irreducible(): output.append(3) elif not E.division_polynomial(3).is_irreducible(): output.append(3) elif (K.discriminant() % l) == 0: if not K['x'](cyclotomic_polynomial(l)).is_irreducible(): # I.E. if the action on lth roots of unity is not surjective # (We want this since as a Galois module, \wedge^2 E[l] # is isomorphic to the lth roots of unity.) output.append(l) for l in output: L.remove(l) if 2 in L: L.remove(2) if 3 in L: L.remove(3) # If the image is not surjective, then it is contained in one of the # maximal subgroups. So, we start by creating a dictionary between primes # l in L and possible maximal subgroups in which the mod l image could # be contained. This information is stored as a triple whose elements # are True/False according to whether the mod l image could be contained # in: # 0. A Borel or normalizer of split Cartan subgroup. # 1. A nonsplit Cartan subgroup or its normalizer. # 2. An exceptional subgroup of GL_2. D = {} for l in L: D[l] = [True, True, True] for P in K.primes_of_degree_one_iter(): try: trace = E.change_ring(P.residue_field()).trace_of_frobenius() except ArithmeticError: # Bad reduction at P. continue patience -= 1 determinant = P.norm() discriminant = trace**2 - 4 * determinant unexc = [] # Primes we discover are unexceptional go here. for l in D.iterkeys(): tr = GF(l)(trace) det = GF(l)(determinant) disc = GF(l)(discriminant) if tr == 0: # I.E. if Frob_P could be contained in the normalizer of # a Cartan subgroup, but not in the Cartan subgroup. continue if disc == 0: # I.E. If the matrix might be non-diagonalizable over F_{p^2}. continue if legendre_symbol(disc, l) == 1: # If the matrix is diagonalizable over F_p, it can't be # contained in a non-split Cartan subgroup. Since we've # gotten rid of the case where it is contained in the # of a nonsplit Cartan subgroup but not the Cartan subgroup, D[l][1] = False else: # If the matrix is not diagonalizable over F_p, it can't # be contained Borel subgroup. D[l][0] = False if det != 0: # c.f. [Serre72], Section 2.8, Prop. 19 u = trace**2 / det if u not in (1, 2, 4) and u**2 - 3 * u + 1 != 0: D[l][2] = False if D[l] == [False, False, False]: unexc.append(l) for l in unexc: D.pop(l) unexc = [] if (D == {}) or (patience == 0): break for l in D.iterkeys(): output.append(l) output.sort() return output
def _next_good_prime(p, R, qq, patience, qqold): """ Find the next prime `\\ell` which is good by ``qq`` but not by ``qqold``, 1 mod ``p``, and for which ``b^2+4*c`` is a square mod `\\ell`, for the sequence ``R`` if it is possible in runtime patience. INPUT: - ``p`` -- a prime - ``R`` -- an object in the class ``BinaryRecurrenceSequence`` - ``qq`` -- a perfect power - ``patience`` -- a real number - ``qqold`` -- a perfect power less than or equal to ``qq`` OUTPUT: - A prime `\\ell` such that `\\ell` is 1 mod ``p``, ``b^2+4*c`` is a square mod `\\ell` and the period of `\\ell` has ``goodness`` by ``qq`` but not ``qqold``, if patience has not be surpased. Otherwise ``False``. EXAMPLES:: sage: R = BinaryRecurrenceSequence(1,1) sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,1,100,1) #ran out of patience to search for good primes False sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,2,100,1) 29 sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,2,100,2) #ran out of patience, as qqold == qq, so no primes work False """ #We are looking for pth powers in R. #Our primes must be good by qq, but not qqold. #We only allow patience number of iterations to find a good prime. #The variable _ell for R keeps track of the last "good" prime returned #that was not found from the dictionary _PGoodness #First, we check to see if we have already computed the goodness of a prime that fits #our requirement of being good by qq but not by qqold. This is stored in the _PGoodness #dictionary. #Then if we have, we return the smallest such prime and delete it from the list. If not, we #search through patience number of primes R._ell to find one good by qq but not qqold. If it is #not good by either qqold or qq, then we add this prime to R._PGoodness under its goodness. #Possible_Primes keeps track of possible primes satisfying our goodness requirements we might return Possible_Primes = [] #check to see if anything in R._PGoodness fits our goodness requirements for j in R._PGoodness: if (qqold < j <= qq) and len(R._PGoodness[j]): Possible_Primes.append(R._PGoodness[j][0]) #If we found good primes, we take the smallest if Possible_Primes != []: q = min(Possible_Primes) n = _goodness(q, R, p) del R._PGoodness[n][0] #if we are going to use it, then we delete it from R._PGoodness return q #If nothing is already stored in R._PGoodness, we start (from where we left off at R._ell) checking #for good primes. We only tolerate patience number of tries before giving up. else: i = 0 while i < patience: i += 1 R._ell = next_prime(R._ell) #we require that R._ell is 1 mod p, so that p divides the order of the multiplicative #group mod R._ell, so that not all elements of GF(R._ell) are pth powers. if R._ell % p == 1: #requiring that b^2 + 4c is a square in GF(R._ell) ensures that the period mod R._ell #divides R._ell - 1 if legendre_symbol(R.b**2+4*R.c, R._ell) == 1: N = _goodness(R._ell, R, p) #proceed only if R._ell statisfies the goodness requirements if qqold < N <= qq: return R._ell #if we do not use the prime, we store it in R._PGoodness else: if N in R._PGoodness: R._PGoodness[N].append(R._ell) else : R._PGoodness[N] = [R._ell] return False
def _maybe_borels(E, L, patience=100): r""" Determine which primes in L might have an image contained in a Borel subgroup, using straight-forward checking of traces of Frobenius. .. NOTE: This function will sometimes return primes for which the image is not contained in a Borel subgroup. This issue cannot always be fixed by increasing patience as it may be a result of a failure of a local-global principle for isogenies. INPUT: - ``E`` - EllipticCurve - over a number field. - ``L`` - list - a list of prime numbers. - ``patience`` - int (a positive integer bounding the number of traces of Frobenius to use while trying to prove irreducibility). OUTPUT: list - The list of all primes `\ell` in L for which the mod `\ell` image might be contained in a Borel subgroup of `GL_2(\mathbf{F}_{\ell})`. EXAMPLES:: sage: E = EllipticCurve('11a1') # has a 5-isogeny sage: sage.schemes.elliptic_curves.gal_reps_number_field._maybe_borels(E,primes(40)) [5] Example to show that the output may contain primes where the representation is in fact reducible. Over `\QQ` the following is essentially the unique such example by [Sutherland12]_:: sage: E = EllipticCurve_from_j(2268945/128) sage: sage.schemes.elliptic_curves.gal_reps_number_field._maybe_borels(E, [7, 11]) [7] This curve does posess a 7-isogeny modulo every prime of good reduction, but has no rational 7-isogeny:: sage: E.isogenies_prime_degree(7) [] A number field example: sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve([1+i, -i, i, -399-240*i, 2627+2869*i]) sage: sage.schemes.elliptic_curves.gal_reps_number_field._maybe_borels(E, primes(20)) [2, 3] Here the curve really does possess isognies of degrees 2 and 3:: sage: [len(E.isogenies_prime_degree(l)) for l in [2,3]] [1, 1] """ E = _over_numberfield(E) K = E.base_field() L = list(set(L)) # Remove duplicates from L and makes a copy for output L.sort() include_2 = False if 2 in L: # c.f. Section 5.3(a) of [Serre72]. L.remove(2) include_2 = not E.division_polynomial(2).is_irreducible() for P in K.primes_of_degree_one_iter(): if not (L and patience): # stop if no primes are left, or # patience is exhausted break patience -= 1 # Check whether the Frobenius polynomial at P is irreducible # modulo each l, dropping l from the list if so. try: trace = E.change_ring(P.residue_field()).trace_of_frobenius() except ArithmeticError: # Bad reduction at P. continue determinant = P.norm() discriminant = trace ** 2 - 4 * determinant for l in L: if legendre_symbol(discriminant, l) == -1: L.remove(l) if include_2: L = [2] + L return L
def has_equivalent_Jordan_decomposition_at_prime(self, other, p): """ Determines if the given quadratic form has a Jordan decomposition equivalent to that of self. INPUT: a QuadraticForm OUTPUT: boolean EXAMPLES:: sage: Q1 = QuadraticForm(ZZ, 3, [1, 0, -1, 1, 0, 3]) sage: Q2 = QuadraticForm(ZZ, 3, [1, 0, 0, 2, -2, 6]) sage: Q3 = QuadraticForm(ZZ, 3, [1, 0, 0, 1, 0, 11]) sage: [Q1.level(), Q2.level(), Q3.level()] [44, 44, 44] sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q2,2) False sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q2,11) False sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q3,2) False sage: Q1.has_equivalent_Jordan_decomposition_at_prime(Q3,11) True sage: Q2.has_equivalent_Jordan_decomposition_at_prime(Q3,2) True sage: Q2.has_equivalent_Jordan_decomposition_at_prime(Q3,11) False """ ## Sanity Checks #if not isinstance(other, QuadraticForm): if type(other) != type(self): raise TypeError, "Oops! The first argument must be of type QuadraticForm." if not is_prime(p): raise TypeError, "Oops! The second argument must be a prime number." ## Get the relevant local normal forms quickly self_jordan = self.jordan_blocks_by_scale_and_unimodular(p, safe_flag= False) other_jordan = other.jordan_blocks_by_scale_and_unimodular(p, safe_flag=False) ## DIAGNOSTIC #print "self_jordan = ", self_jordan #print "other_jordan = ", other_jordan ## Check for the same number of Jordan components if len(self_jordan) != len(other_jordan): return False ## Deal with odd primes: Check that the Jordan component scales, dimensions, and discriminants are the same if p != 2: for i in range(len(self_jordan)): if (self_jordan[i][0] != other_jordan[i][0]) \ or (self_jordan[i][1].dim() != other_jordan[i][1].dim()) \ or (legendre_symbol(self_jordan[i][1].det() * other_jordan[i][1].det(), p) != 1): return False ## All tests passed for an odd prime. return True ## For p = 2: Check that all Jordan Invariants are the same. elif p == 2: ## Useful definition t = len(self_jordan) ## Define t = Number of Jordan components ## Check that all Jordan Invariants are the same (scale, dim, and norm) for i in range(t): if (self_jordan[i][0] != other_jordan[i][0]) \ or (self_jordan[i][1].dim() != other_jordan[i][1].dim()) \ or (valuation(GCD(self_jordan[i][1].coefficients()), p) != valuation(GCD(other_jordan[i][1].coefficients()), p)): return False ## DIAGNOSTIC #print "Passed the Jordan invariant test." ## Use O'Meara's isometry test 93:29 on p277. ## ------------------------------------------ ## List of norms, scales, and dimensions for each i scale_list = [ZZ(2)**self_jordan[i][0] for i in range(t)] norm_list = [ZZ(2)**(self_jordan[i][0] + valuation(GCD(self_jordan[i][1].coefficients()), 2)) for i in range(t)] dim_list = [(self_jordan[i][1].dim()) for i in range(t)] ## List of Hessian determinants and Hasse invariants for each Jordan (sub)chain ## (Note: This is not the same as O'Meara's Gram determinants, but ratios are the same!) -- NOT SO GOOD... ## But it matters in condition (ii), so we multiply all by 2 (instead of dividing by 2 since only square-factors matter, and it's easier.) j = 0 self_chain_det_list = [ self_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])] other_chain_det_list = [ other_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])] self_hasse_chain_list = [ self_jordan[j][1].scale_by_factor(ZZ(2)**self_jordan[j][0]).hasse_invariant__OMeara(2) ] other_hasse_chain_list = [ other_jordan[j][1].scale_by_factor(ZZ(2)**other_jordan[j][0]).hasse_invariant__OMeara(2) ] for j in range(1, t): self_chain_det_list.append(self_chain_det_list[j-1] * self_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])) other_chain_det_list.append(other_chain_det_list[j-1] * other_jordan[j][1].Gram_det() * (scale_list[j]**dim_list[j])) self_hasse_chain_list.append(self_hasse_chain_list[j-1] \ * hilbert_symbol(self_chain_det_list[j-1], self_jordan[j][1].Gram_det(), 2) \ * self_jordan[j][1].hasse_invariant__OMeara(2)) other_hasse_chain_list.append(other_hasse_chain_list[j-1] \ * hilbert_symbol(other_chain_det_list[j-1], other_jordan[j][1].Gram_det(), 2) \ * other_jordan[j][1].hasse_invariant__OMeara(2)) ## SANITY CHECK -- check that the scale powers are strictly increasing for i in range(1, len(scale_list)): if scale_list[i-1] >= scale_list[i]: raise RuntimeError, "Oops! There is something wrong with the Jordan Decomposition -- the given scales are not strictly increasing!" ## DIAGNOSTIC #print "scale_list = ", scale_list #print "norm_list = ", norm_list #print "dim_list = ", dim_list #print #print "self_chain_det_list = ", self_chain_det_list #print "other_chain_det_list = ", other_chain_det_list #print "self_hasse_chain_list = ", self_hasse_chain_list #print "other_hasse_chain_det_list = ", other_hasse_chain_list ## Test O'Meara's two conditions for i in range(t-1): ## Condition (i): Check that their (unit) ratio is a square (but it suffices to check at most mod 8). modulus = norm_list[i] * norm_list[i+1] / (scale_list[i] ** 2) if modulus > 8: modulus = 8 if (modulus > 1) and (((self_chain_det_list[i] / other_chain_det_list[i]) % modulus) != 1): #print "Failed when i =", i, " in condition 1." return False ## Check O'Meara's condition (ii) when appropriate if norm_list[i+1] % (4 * norm_list[i]) == 0: if self_hasse_chain_list[i] * hilbert_symbol(norm_list[i] * other_chain_det_list[i], -self_chain_det_list[i], 2) \ != other_hasse_chain_list[i] * hilbert_symbol(norm_list[i], -other_chain_det_list[i], 2): ## Nipp conditions #print "Failed when i =", i, " in condition 2." return False ## All tests passed for the prime 2. return True else: raise TypeError, "Oops! This should not have happened."
def _maybe_borels(E, L, patience=100): r""" Determine which primes in L might have an image contained in a Borel subgroup, using straight-forward checking of traces of Frobenius. .. NOTE: This function will sometimes return primes for which the image is not contained in a Borel subgroup. This issue cannot always be fixed by increasing patience as it may be a result of a failure of a local-global principle for isogenies. INPUT: - ``E`` - EllipticCurve - over a number field. - ``L`` - list - a list of prime numbers. - ``patience`` - int (a positive integer bounding the number of traces of Frobenius to use while trying to prove irreducibility). OUTPUT: list - The list of all primes `\ell` in L for which the mod `\ell` image might be contained in a Borel subgroup of `GL_2(\mathbf{F}_{\ell})`. EXAMPLES:: sage: E = EllipticCurve('11a1') # has a 5-isogeny sage: sage.schemes.elliptic_curves.gal_reps_number_field._maybe_borels(E,primes(40)) [5] Example to show that the output may contain primes where the representation is in fact reducible. Over `\QQ` the following is essentially the unique such example by [Sutherland12]_:: sage: E = EllipticCurve_from_j(2268945/128) sage: sage.schemes.elliptic_curves.gal_reps_number_field._maybe_borels(E, [7, 11]) [7] This curve does posess a 7-isogeny modulo every prime of good reduction, but has no rational 7-isogeny:: sage: E.isogenies_prime_degree(7) [] A number field example: sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve([1+i, -i, i, -399-240*i, 2627+2869*i]) sage: sage.schemes.elliptic_curves.gal_reps_number_field._maybe_borels(E, primes(20)) [2, 3] Here the curve really does possess isognies of degrees 2 and 3:: sage: [len(E.isogenies_prime_degree(l)) for l in [2,3]] [1, 1] """ E = _over_numberfield(E) K = E.base_field() L = list(set(L)) # Remove duplicates from L and makes a copy for output L.sort() include_2 = False if 2 in L: # c.f. Section 5.3(a) of [Serre72]. L.remove(2) include_2 = not E.division_polynomial(2).is_irreducible() for P in K.primes_of_degree_one_iter(): if not (L and patience): # stop if no primes are left, or # patience is exhausted break patience -= 1 # Check whether the Frobenius polynomial at P is irreducible # modulo each l, dropping l from the list if so. try: trace = E.change_ring(P.residue_field()).trace_of_frobenius() except ArithmeticError: # Bad reduction at P. continue determinant = P.norm() discriminant = trace**2 - 4 * determinant for l in L: if legendre_symbol(discriminant, l) == -1: L.remove(l) if include_2: L = [2] + L return L