def xi(self,p): """ Return the value of the genus characters Xi_p... which may be missing one character. We allow -1 as a prime. Reference: Dickson's "Studies in the Theory of Numbers" EXAMPLES:: sage: Q1 = QuadraticForm(ZZ, 3, [1, 1, 1, 14, 3, 14]) sage: Q2 = QuadraticForm(ZZ, 3, [2, -1, 0, 2, 0, 50]) sage: [Q1.omega(), Q2.omega()] [5, 5] sage: [Q1.hasse_invariant(5), Q2.hasse_invariant(5)] # equivalent over Q_5 [1, 1] sage: [Q1.xi(5), Q2.xi(5)] # not equivalent over Z_5 [1, -1] """ if self.dim() == 2 and self.disc() % p: raise ValueError("not a valid character") if self.dim() >= 3 and self.omega() % p: raise ValueError("not a valid character") if (p == -1) or (p == 2): return kronecker_symbol(p, self.basiclemma(2)) return kronecker_symbol(self.basiclemma(p), p)
def measure_experimental(self,a,n,prec,quadratic_twist=+1,alpha=[]): """ """ if quadratic_twist > 0: s = +1 else: s = -1 try: p, p_inv, alpha, alpha_inv, z, w, w_inv, f = self.__measure_data[(n,prec,s)] except (KeyError, AttributeError): if not hasattr(self, '__measure_data'): self.__measure_data = {} p = self._p z = 1/(alpha**n) w = p**(n-1) f = self.modular_symbol w_inv = ~w alpha_inv = ~alpha p_inv = p.parent()(1)/p R = alpha.parent() self.__measure_data[(n,prec,s)] = (R(p),R(p_inv),alpha,alpha_inv,z,R(w),R(w_inv),f) if quadratic_twist == 1: return z * f(a*p_inv*w_inv) - (z*alpha_inv) * f(a*w_inv) else: D = quadratic_twist chip = kronecker_symbol(D,p) if self._E.conductor() % p == 0: mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,abs(D))]) else: mu = chip**n * sum([kronecker_symbol(D,u) *(z * f(a/(p*w)+ZZ(u)/D) - chip *(z/alpha)* f(a/w+ZZ(u)/D)) for u in range(1,abs(D))]) return s*mu
def measure(self, a, n, prec, quadratic_twist=+1, user_alpha=[], outside_call=False): """ outside_call: if using this from outside the series computation """ if quadratic_twist > 0: s = +1 else: s = -1 try: p, alpha, z, w, f = self.__measure_data[(n, prec, s)] except (KeyError, AttributeError): if not hasattr(self, '__measure_data'): self.__measure_data = {} p = self._p alpha = user_alpha z = 1 / (alpha**n) w = p**(n - 1) f = self.modular_symbol self.__measure_data[(n, prec, s)] = (p, alpha, z, w, f) if outside_call: if (user_alpha == self.alpha()[0]): self._emb = 1 else: self._emb = 2 if alpha != user_alpha: alpha = user_alpha z = 1 / (alpha**n) f = self.modular_symbol self.__measure_data[(n, prec, s)] = (p, alpha, z, w, f) if quadratic_twist == 1: return z * f(a / (p * w)) - (z / alpha) * f(a / w) else: D = quadratic_twist chip = kronecker_symbol(D, p) if self._E.conductor() % p == 0: mu = chip**n * z * sum([ kronecker_symbol(D, u) * f(a / (p * w) + ZZ(u) / D) for u in range(1, abs(D)) ]) else: mu = chip**n * sum([ kronecker_symbol(D, u) * (z * f(a / (p * w) + ZZ(u) / D) - chip * (z / alpha) * f(a / w + ZZ(u) / D)) for u in range(1, abs(D)) ]) return s * mu
def QuadraticBernoulliNumber(k, d): r""" Compute `k`-th Bernoulli number for the primitive quadratic character associated to `\chi(x) = \left(\frac{d}{x}\right)`. EXAMPLES: Let us create a list of some odd negative fundamental discriminants:: sage: test_set = [d for d in range(-163, -3, 4) if is_fundamental_discriminant(d)] In general, we have `B_{1, \chi_d} = -2 h/w` for odd negative fundamental discriminants:: sage: all(QuadraticBernoulliNumber(1, d) == -len(BinaryQF_reduced_representatives(d)) for d in test_set) True REFERENCES: - [Iwa1972]_, pp 7-16. """ # Ensure the character is primitive d1 = fundamental_discriminant(d) f = abs(d1) # Make the (usual) k-th Bernoulli polynomial x = PolynomialRing(QQ, 'x').gen() bp = bernoulli_polynomial(x, k) # Make the k-th quadratic Bernoulli number total = sum([kronecker_symbol(d1, i) * bp(i/f) for i in range(f)]) total *= (f ** (k-1)) return total
def nu3(self): r""" Return the number of elliptic points of order 3 for this congruence subgroup `\Gamma_0(N)`. The number of these is given by a standard formula: 0 if `N` is divisible by 9 or any prime congruent to -1 mod 3, and otherwise `2^d` where d is the number of primes other than 3 dividing `N`. EXAMPLE:: sage: Gamma0(2).nu3() 0 sage: Gamma0(3).nu3() 1 sage: Gamma0(9).nu3() 0 sage: Gamma0(7).nu3() 2 sage: Gamma0(21).nu3() 2 sage: Gamma0(1729).nu3() 8 """ n = self.level() if (n % 9 == 0): return ZZ(0) return prod([ 1 + kronecker_symbol(-3, p) for p, _ in n.factor()])
def nu3(self): r""" Return the number of elliptic points of order 3 for this congruence subgroup `\Gamma_0(N)`. The number of these is given by a standard formula: 0 if `N` is divisible by 9 or any prime congruent to -1 mod 3, and otherwise `2^d` where d is the number of primes other than 3 dividing `N`. EXAMPLE:: sage: Gamma0(2).nu3() 0 sage: Gamma0(3).nu3() 1 sage: Gamma0(9).nu3() 0 sage: Gamma0(7).nu3() 2 sage: Gamma0(21).nu3() 2 sage: Gamma0(1729).nu3() 8 """ n = self.level() if (n % 9 == 0): return ZZ(0) return prod([1 + kronecker_symbol(-3, p) for p, _ in n.factor()])
def QuadraticBernoulliNumber(k, d): r""" Compute `k`-th Bernoulli number for the primitive quadratic character associated to `\chi(x) = \left(\frac{d}{x}\right)`. EXAMPLES: Let us create a list of some odd negative fundamental discriminants:: sage: test_set = [d for d in range(-163, -3, 4) if is_fundamental_discriminant(d)] In general, we have `B_{1, \chi_d} = -2 h/w` for odd negative fundamental discriminants:: sage: all(QuadraticBernoulliNumber(1, d) == -len(BinaryQF_reduced_representatives(d)) for d in test_set) True REFERENCES: - [Iwa1972]_, pp 7-16. """ # Ensure the character is primitive d1 = fundamental_discriminant(d) f = abs(d1) # Make the (usual) k-th Bernoulli polynomial x = PolynomialRing(QQ, 'x').gen() bp = bernoulli_polynomial(x, k) # Make the k-th quadratic Bernoulli number total = sum([kronecker_symbol(d1, i) * bp(i / f) for i in range(f)]) total *= (f**(k - 1)) return total
def quadratic_L_function__exact(n, d): r""" Returns the exact value of a quadratic twist of the Riemann Zeta function by `\chi_d(x) = \left(\frac{d}{x}\right)`. The input `n` must be a critical value. EXAMPLES:: sage: quadratic_L_function__exact(1, -4) 1/4*pi sage: quadratic_L_function__exact(-4, -4) 5/2 sage: quadratic_L_function__exact(2, 1) 1/6*pi^2 TESTS:: sage: quadratic_L_function__exact(2, -4) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. odd > 0 or even <= 0) REFERENCES: - [Iwa1972]_, pp 16-17, Special values of `L(1-n, \chi)` and `L(n, \chi)` - [IR1990]_ - [Was1997]_ """ from sage.all import SR, sqrt if n <= 0: return QuadraticBernoulliNumber(1 - n, d) / (n - 1) elif n >= 1: # Compute the kind of critical values (p10) if kronecker_symbol(fundamental_discriminant(d), -1) == 1: delta = 0 else: delta = 1 # Compute the positive special values (p17) if ((n - delta) % 2 == 0): f = abs(fundamental_discriminant(d)) if delta == 0: GS = sqrt(f) else: GS = I * sqrt(f) ans = SR(ZZ(-1)**(1 + (n - delta) / 2)) ans *= (2 * pi / f)**n ans *= GS # Evaluate the Gauss sum here! =0 ans *= QQ.one() / (2 * I**delta) ans *= QuadraticBernoulliNumber(n, d) / factorial(n) return ans else: if delta == 0: raise TypeError( "n must be a critical value (i.e. even > 0 or odd < 0)") if delta == 1: raise TypeError( "n must be a critical value (i.e. odd > 0 or even <= 0)")
def measure_experimental(self, a, n, prec, quadratic_twist=+1, alpha=[]): """ """ if quadratic_twist > 0: s = +1 else: s = -1 try: p, p_inv, alpha, alpha_inv, z, w, w_inv, f = self.__measure_data[( n, prec, s)] except (KeyError, AttributeError): if not hasattr(self, '__measure_data'): self.__measure_data = {} p = self._p z = 1 / (alpha**n) w = p**(n - 1) f = self.modular_symbol w_inv = ~w alpha_inv = ~alpha p_inv = p.parent()(1) / p R = alpha.parent() self.__measure_data[(n, prec, s)] = (R(p), R(p_inv), alpha, alpha_inv, z, R(w), R(w_inv), f) if quadratic_twist == 1: return z * f(a * p_inv * w_inv) - (z * alpha_inv) * f(a * w_inv) else: D = quadratic_twist chip = kronecker_symbol(D, p) if self._E.conductor() % p == 0: mu = chip**n * z * sum([ kronecker_symbol(D, u) * f(a / (p * w) + ZZ(u) / D) for u in range(1, abs(D)) ]) else: mu = chip**n * sum([ kronecker_symbol(D, u) * (z * f(a / (p * w) + ZZ(u) / D) - chip * (z / alpha) * f(a / w + ZZ(u) / D)) for u in range(1, abs(D)) ]) return s * mu
def quadratic_L_function__exact(n, d): r""" Returns the exact value of a quadratic twist of the Riemann Zeta function by `\chi_d(x) = \left(\frac{d}{x}\right)`. The input `n` must be a critical value. EXAMPLES:: sage: quadratic_L_function__exact(1, -4) 1/4*pi sage: quadratic_L_function__exact(-4, -4) 5/2 sage: quadratic_L_function__exact(2, 1) 1/6*pi^2 TESTS:: sage: quadratic_L_function__exact(2, -4) Traceback (most recent call last): ... TypeError: n must be a critical value (i.e. odd > 0 or even <= 0) REFERENCES: - [Iwa1972]_, pp 16-17, Special values of `L(1-n, \chi)` and `L(n, \chi)` - [IR1990]_ - [Was1997]_ """ from sage.all import SR, sqrt if n <= 0: return QuadraticBernoulliNumber(1-n,d)/(n-1) elif n >= 1: # Compute the kind of critical values (p10) if kronecker_symbol(fundamental_discriminant(d), -1) == 1: delta = 0 else: delta = 1 # Compute the positive special values (p17) if ((n - delta) % 2 == 0): f = abs(fundamental_discriminant(d)) if delta == 0: GS = sqrt(f) else: GS = I * sqrt(f) ans = SR(ZZ(-1)**(1+(n-delta)/2)) ans *= (2*pi/f)**n ans *= GS # Evaluate the Gauss sum here! =0 ans *= QQ.one()/(2 * I**delta) ans *= QuadraticBernoulliNumber(n,d)/factorial(n) return ans else: if delta == 0: raise TypeError("n must be a critical value (i.e. even > 0 or odd < 0)") if delta == 1: raise TypeError("n must be a critical value (i.e. odd > 0 or even <= 0)")
def measure(self,a,n,prec,quadratic_twist=+1,user_alpha=[],outside_call=False): """ outside_call: if using this from outside the series computation """ if quadratic_twist > 0: s = +1 else: s = -1 try: p, alpha, z, w, f = self.__measure_data[(n,prec,s)] except (KeyError, AttributeError): if not hasattr(self, '__measure_data'): self.__measure_data = {} p = self._p alpha = user_alpha z = 1/(alpha**n) w = p**(n-1) f = self.modular_symbol self.__measure_data[(n,prec,s)] = (p,alpha,z,w,f) if outside_call: if (user_alpha == self.alpha()[0]): self._emb = 1 else: self._emb = 2 if alpha != user_alpha: alpha = user_alpha z = 1/(alpha**n) f = self.modular_symbol self.__measure_data[(n,prec,s)] = (p,alpha,z,w,f) if quadratic_twist == 1: return z * f(a/(p*w)) - (z/alpha) * f(a/w) else: D = quadratic_twist chip = kronecker_symbol(D,p) if self._E.conductor() % p == 0: mu = chip**n * z * sum([kronecker_symbol(D,u) * f(a/(p*w)+ZZ(u)/D) for u in range(1,abs(D))]) else: mu = chip**n * sum([kronecker_symbol(D,u) *(z * f(a/(p*w)+ZZ(u)/D) - chip *(z/alpha)* f(a/w+ZZ(u)/D)) for u in range(1,abs(D))]) return s*mu
def quadratic_L_function__numerical(n, d, num_terms=1000): """ Evaluate the Dirichlet L-function (for quadratic character) numerically (in a very naive way). EXAMPLES: First, let us test several values for a given character:: sage: RR = RealField(100) sage: for i in range(5): ....: print("L({}, (-4/.)): {}".format(1+2*i, RR(quadratic_L_function__exact(1+2*i, -4)) - quadratic_L_function__numerical(RR(1+2*i),-4, 10000))) L(1, (-4/.)): 0.000049999999500000024999996962707 L(3, (-4/.)): 4.99999970000003...e-13 L(5, (-4/.)): 4.99999922759382...e-21 L(7, (-4/.)): ...e-29 L(9, (-4/.)): ...e-29 This procedure fails for negative special values, as the Dirichlet series does not converge here:: sage: quadratic_L_function__numerical(-3,-4, 10000) Traceback (most recent call last): ... ValueError: the Dirichlet series does not converge here Test for several characters that the result agrees with the exact value, to a given accuracy :: sage: for d in range(-20,0): # long time (2s on sage.math 2014) ....: if abs(RR(quadratic_L_function__numerical(1, d, 10000) - quadratic_L_function__exact(1, d))) > 0.001: ....: print("Oops! We have a problem at d = {}: exact = {}, numerical = {}".format(d, RR(quadratic_L_function__exact(1, d)), RR(quadratic_L_function__numerical(1, d)))) """ # Set the correct precision if it is given (for n). if isinstance(n.parent(), sage.rings.abc.RealField): R = n.parent() else: from sage.rings.real_mpfr import RealField R = RealField() if n < 0: raise ValueError('the Dirichlet series does not converge here') d1 = fundamental_discriminant(d) ans = R.zero() for i in range(1,num_terms): ans += R(kronecker_symbol(d1,i) / R(i)**n) return ans
def quadratic_L_function__numerical(n, d, num_terms=1000): """ Evaluate the Dirichlet L-function (for quadratic character) numerically (in a very naive way). EXAMPLES: First, let us test several values for a given character:: sage: RR = RealField(100) sage: for i in range(5): ....: print("L({}, (-4/.)): {}".format(1+2*i, RR(quadratic_L_function__exact(1+2*i, -4)) - quadratic_L_function__numerical(RR(1+2*i),-4, 10000))) L(1, (-4/.)): 0.000049999999500000024999996962707 L(3, (-4/.)): 4.99999970000003...e-13 L(5, (-4/.)): 4.99999922759382...e-21 L(7, (-4/.)): ...e-29 L(9, (-4/.)): ...e-29 This procedure fails for negative special values, as the Dirichlet series does not converge here:: sage: quadratic_L_function__numerical(-3,-4, 10000) Traceback (most recent call last): ... ValueError: the Dirichlet series does not converge here Test for several characters that the result agrees with the exact value, to a given accuracy :: sage: for d in range(-20,0): # long time (2s on sage.math 2014) ....: if abs(RR(quadratic_L_function__numerical(1, d, 10000) - quadratic_L_function__exact(1, d))) > 0.001: ....: print("Oops! We have a problem at d = {}: exact = {}, numerical = {}".format(d, RR(quadratic_L_function__exact(1, d)), RR(quadratic_L_function__numerical(1, d)))) """ # Set the correct precision if it is given (for n). if is_RealField(n.parent()): R = n.parent() else: R = RealField() if n < 0: raise ValueError('the Dirichlet series does not converge here') d1 = fundamental_discriminant(d) ans = R.zero() for i in range(1,num_terms): ans += R(kronecker_symbol(d1,i) / R(i)**n) return ans
def nu2(self): r""" Return the number of elliptic points of order 2 for this congruence subgroup `\Gamma_0(N)`. The number of these is given by a standard formula: 0 if `N` is divisible by 4 or any prime congruent to -1 mod 4, and otherwise `2^d` where d is the number of odd primes dividing `N`. EXAMPLE:: sage: Gamma0(2).nu2() 1 sage: Gamma0(4).nu2() 0 sage: Gamma0(21).nu2() 0 sage: Gamma0(1105).nu2() 8 sage: [Gamma0(n).nu2() for n in [1..19]] [1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0] """ n = self.level() if n % 4 == 0: return ZZ(0) return prod([1 + kronecker_symbol(-4, p) for p, _ in n.factor()])
def nu2(self): r""" Return the number of elliptic points of order 2 for this congruence subgroup `\Gamma_0(N)`. The number of these is given by a standard formula: 0 if `N` is divisible by 4 or any prime congruent to -1 mod 4, and otherwise `2^d` where d is the number of odd primes dividing `N`. EXAMPLE:: sage: Gamma0(2).nu2() 1 sage: Gamma0(4).nu2() 0 sage: Gamma0(21).nu2() 0 sage: Gamma0(1105).nu2() 8 sage: [Gamma0(n).nu2() for n in [1..19]] [1, 1, 0, 0, 2, 0, 0, 0, 0, 2, 0, 0, 2, 0, 0, 0, 2, 0, 0] """ n = self.level() if n%4 == 0: return ZZ(0) return prod([ 1 + kronecker_symbol(-4, p) for p, _ in n.factor()])
def _rank(self, K): """ Return the dimension of the level N space of given weight. """ if not K is QQ: raise NotImplementedError if not self.__level.is_prime(): raise NotImplementedError if self.__weight % 2 != 0: raise NotImplementedError("Only even weights available") N = self.__level if N == 1: ## By Igusa's theorem on the generators of the graded ring P = PowerSeriesRing(ZZ, 't') t = P.gen(0) dims = 1 / ((1 - t**4) * (1 - t**6) * (1 - t**10) * (1 - t**12)).add_bigoh(self.__weight + 1) return dims[self.__weight] if N == 2: ## As in Ibukiyama, Onodera - On the graded ring of modular forms of the Siegel ## paramodular group of level 2, Proposition 2 P = PowerSeriesRing(ZZ, 't') t = P.gen(0) dims = ((1 + t**10) * (1 + t**12) * (1 + t**11)) dims = dims / ((1 - t**4) * (1 - t**6) * (1 - t**8) * (1 - t**12)).add_bigoh(self.__weight + 1) return dims[self.__weight] if N == 3: ## By Dern, Paramodular forms of degree 2 and level 3, Corollary 5.6 P = PowerSeriesRing(ZZ, 't') t = P.gen(0) dims = ((1 + t**12) * (1 + t**8 + t**9 + t**10 + t**11 + t**19)) dims = dims / ((1 - t**4) * (1 - t**6)**2 * (1 - t**12)).add_bigoh(self.__weight + 1) return dims[self.__weight] if N == 5: ## By Marschner, Paramodular forms of degree 2 with particular emphasis on level t = 5, ## Corollary 7.3.4. PhD thesis electronically available via the library of ## RWTH University, Aachen, Germany P = PowerSeriesRing(ZZ, 't') t = P.gen(0) dims = t**30 + t**24 + t**23 + 2*t**22 + t**21 + 2*t**20 + t**19 + 2*t**18 \ + 2*t**16 + 2*t**14 + 2*t**12 + t**11 + 2*t**10 + t**9 + 2*t**8 + t**7 + t**6 + 1 dims = dims / ((1 - t**4) * (1 - t**5) * (1 - t**6) * (1 - t**12)).add_bigoh(self.__weight + 1) return dims[self.__weight] if self.__weight == 2: ## There are only cuspforms, since there is no elliptic modular form ## of weight 2. if N < 277: ## Poor, Yuen - Paramodular cusp forms tells us that all forms are ## Gritsenko lifts return JacobiFormD1NN_Gamma(self.__level, 2)._rank(QQ) raise NotImplementedError elif self.__weight == 4: ## This is the formula cited by Poor and Yuen in Paramodular cusp forms cuspidal_dim = Integer((N**2 - 143) / Integer(576) + N / Integer(8) + kronecker_symbol(-1, N) * (N - 12) / Integer(96) + kronecker_symbol(2, N) / Integer(8) + kronecker_symbol(3, N) / Integer(12) + kronecker_symbol(-3, N) * N / Integer(36)) else: ## This is the formula given by Ibukiyama in ## Relations of dimension of automorphic forms of Sp(2,R) and its compact twist Sp(2), ## Theorem 4 p = N k = self.__weight ## This is the reversed Ibukiyama symbol [.., .., ..; ..] def ibukiyama_symbol(modulus, *args): return args[k % modulus] ## if p == 2 this formula is wrong. If the weight is even it differs by ## -3/16 from the true dimension and if the weight is odd it differs by ## -1/16 from the true dimension. H1 = (p**2 + 1) * (2 * k - 2) * (2 * k - 3) * ( 2 * k - 4) / Integer(2**9 * 3**3 * 5) H2 = (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**8 * 3**2) \ + ( (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**7 * 3) if p !=2 else (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**9) ) H3 = (ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) / Integer(2**4 * 3) if p != 3 else 5 * ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) / Integer(2**5 * 3)) H4 = (ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) / Integer(2**2 * 3**3) if p != 2 else 5 * ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) / Integer(2**2 * 3**3)) H5 = ibukiyama_symbol(6, -1, -k + 1, -k + 2, 1, k - 1, k - 2) / Integer(2**2 * 3**2) if p % 4 == 1: H6 = 5 * (2 * k - 3) * (p + 1) / Integer( 2**7 * 3) + (-1)**k * (p + 1) / Integer(2**7) elif p % 4 == 3: H6 = (2 * k - 3) * (p - 1) / Integer( 2**7) + 5 * (-1)**k * (p - 1) / Integer(2**7 * 3) else: H6 = 3 * (2 * k - 3) / Integer(2**7) + 7 * (-1)**k / Integer( 2**7 * 3) if p % 3 == 1: H7 = (2 * k - 3) * (p + 1) / Integer(2 * 3**3) \ + (p + 1) * ibukiyama_symbol(3, 0, -1, 1) / Integer(2**2 * 3**3) elif p % 3 == 2: H7 = (2 * k - 3) * (p - 1) / Integer(2**2 * 3**3) \ + (p - 1) * ibukiyama_symbol(3, 0, -1, 1) / Integer(2 * 3**3) else: H7 = 5 * (2 * k - 3) / Integer(2**2 * 3**3) \ + ibukiyama_symbol(3, 0, -1, 1) / Integer(3**3) H8 = ibukiyama_symbol(12, 1, 0, 0, -1, -1, -1, -1, 0, 0, 1, 1, 1) / Integer(2 * 3) H9 = (2 * ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(3**2) if p != 2 else ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(2 * 3**2)) H10 = (1 + kronecker_symbol(5, p)) * ibukiyama_symbol( 5, 1, 0, 0, -1, 0) / Integer(5) H11 = (1 + kronecker_symbol(2, p)) * ibukiyama_symbol( 4, 1, 0, 0, -1) / Integer(2**3) if p % 12 == 1: H12 = ibukiyama_symbol(3, 0, 1, -1) / Integer(2 * 3) elif p % 12 == 11: H12 = (-1)**k / Integer(2 * 3) elif p == 2 or p == 3: H12 = (-1)**k / Integer(2**2 * 3) else: H12 = 0 I1 = ibukiyama_symbol(6, 0, 1, 1, 0, -1, -1) / Integer(6) I2 = ibukiyama_symbol(3, -2, 1, 1) / Integer(2 * 3**2) if p == 3: I3 = ibukiyama_symbol(3, -2, 1, 1) / Integer(3**2) elif p % 3 == 1: I3 = 2 * ibukiyama_symbol(3, -1, 1, 0) / Integer(3**2) else: I3 = 2 * ibukiyama_symbol(3, -1, 0, 1) / Integer(3**2) I4 = ibukiyama_symbol(4, -1, 1, 1, -1) / Integer(2**2) I5 = (-1)**k / Integer(2**3) I6 = (-1)**k * (2 - kronecker_symbol(-1, p)) / Integer(2**4) I7 = -(-1)**k * (2 * k - 3) / Integer(2**3 * 3) I8 = -p * (2 * k - 3) / Integer(2**4 * 3**2) I9 = -1 / Integer(2**3 * 3) I10 = (p + 1) / Integer(2**3 * 3) I11 = -(1 + kronecker_symbol(-1, p)) / Integer(8) I12 = -(1 + kronecker_symbol(-3, p)) / Integer(6) cuspidal_dim = H1 + H2 + H3 + H4 + H5 + H6 + H7 + H8 + H9 + H10 + H11 + H12 \ + I1 + I2 + I3 + I4 + I5 + I6 + I7 + I8 + I9 + I10 + I11 + I12 mfs = ModularForms(1, self.__weight) return cuspidal_dim + mfs.dimension() + mfs.cuspidal_subspace( ).dimension()
def _rank(self, K): """ Return the dimension of the level N space of given weight. """ if not K is QQ: raise NotImplementedError if not self.__level.is_prime(): raise NotImplementedError if self.__weight % 2 != 0: raise NotImplementedError("Only even weights available") N = self.__level if N == 1: ## By Igusa's theorem on the generators of the graded ring P = PowerSeriesRing(ZZ, "t") t = P.gen(0) dims = 1 / ((1 - t ** 4) * (1 - t ** 6) * (1 - t ** 10) * (1 - t ** 12)).add_bigoh(self.__weight + 1) return dims[self.__weight] if N == 2: ## As in Ibukiyama, Onodera - On the graded ring of modular forms of the Siegel ## paramodular group of level 2, Proposition 2 P = PowerSeriesRing(ZZ, "t") t = P.gen(0) dims = (1 + t ** 10) * (1 + t ** 12) * (1 + t ** 11) dims = dims / ((1 - t ** 4) * (1 - t ** 6) * (1 - t ** 8) * (1 - t ** 12)).add_bigoh(self.__weight + 1) return dims[self.__weight] if N == 3: ## By Dern, Paramodular forms of degree 2 and level 3, Corollary 5.6 P = PowerSeriesRing(ZZ, "t") t = P.gen(0) dims = (1 + t ** 12) * (1 + t ** 8 + t ** 9 + t ** 10 + t ** 11 + t ** 19) dims = dims / ((1 - t ** 4) * (1 - t ** 6) ** 2 * (1 - t ** 12)).add_bigoh(self.__weight + 1) return dims[self.__weight] if N == 5: ## By Marschner, Paramodular forms of degree 2 with particular emphasis on level t = 5, ## Corollary 7.3.4. PhD thesis electronically available via the library of ## RWTH University, Aachen, Germany P = PowerSeriesRing(ZZ, "t") t = P.gen(0) dims = ( t ** 30 + t ** 24 + t ** 23 + 2 * t ** 22 + t ** 21 + 2 * t ** 20 + t ** 19 + 2 * t ** 18 + 2 * t ** 16 + 2 * t ** 14 + 2 * t ** 12 + t ** 11 + 2 * t ** 10 + t ** 9 + 2 * t ** 8 + t ** 7 + t ** 6 + 1 ) dims = dims / ((1 - t ** 4) * (1 - t ** 5) * (1 - t ** 6) * (1 - t ** 12)).add_bigoh(self.__weight + 1) return dims[self.__weight] if self.__weight == 2: ## There are only cuspforms, since there is no elliptic modular form ## of weight 2. if N < 277: ## Poor, Yuen - Paramodular cusp forms tells us that all forms are ## Gritsenko lifts return JacobiFormD1NN_Gamma(self.__level, 2)._rank(QQ) raise NotImplementedError elif self.__weight == 4: ## This is the formula cited by Poor and Yuen in Paramodular cusp forms cuspidal_dim = Integer( (N ** 2 - 143) / Integer(576) + N / Integer(8) + kronecker_symbol(-1, N) * (N - 12) / Integer(96) + kronecker_symbol(2, N) / Integer(8) + kronecker_symbol(3, N) / Integer(12) + kronecker_symbol(-3, N) * N / Integer(36) ) else: ## This is the formula given by Ibukiyama in ## Relations of dimension of automorphic forms of Sp(2,R) and its compact twist Sp(2), ## Theorem 4 p = N k = self.__weight ## This is the reversed Ibukiyama symbol [.., .., ..; ..] def ibukiyama_symbol(modulus, *args): return args[k % modulus] ## if p == 2 this formula is wrong. If the weight is even it differs by ## -3/16 from the true dimension and if the weight is odd it differs by ## -1/16 from the true dimension. H1 = (p ** 2 + 1) * (2 * k - 2) * (2 * k - 3) * (2 * k - 4) / Integer(2 ** 9 * 3 ** 3 * 5) H2 = (-1) ** k * (2 * k - 2) * (2 * k - 4) / Integer(2 ** 8 * 3 ** 2) + ( (-1) ** k * (2 * k - 2) * (2 * k - 4) / Integer(2 ** 7 * 3) if p != 2 else (-1) ** k * (2 * k - 2) * (2 * k - 4) / Integer(2 ** 9) ) H3 = ( ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) / Integer(2 ** 4 * 3) if p != 3 else 5 * ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) / Integer(2 ** 5 * 3) ) H4 = ( ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) / Integer(2 ** 2 * 3 ** 3) if p != 2 else 5 * ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) / Integer(2 ** 2 * 3 ** 3) ) H5 = ibukiyama_symbol(6, -1, -k + 1, -k + 2, 1, k - 1, k - 2) / Integer(2 ** 2 * 3 ** 2) if p % 4 == 1: H6 = 5 * (2 * k - 3) * (p + 1) / Integer(2 ** 7 * 3) + (-1) ** k * (p + 1) / Integer(2 ** 7) elif p % 4 == 3: H6 = (2 * k - 3) * (p - 1) / Integer(2 ** 7) + 5 * (-1) ** k * (p - 1) / Integer(2 ** 7 * 3) else: H6 = 3 * (2 * k - 3) / Integer(2 ** 7) + 7 * (-1) ** k / Integer(2 ** 7 * 3) if p % 3 == 1: H7 = (2 * k - 3) * (p + 1) / Integer(2 * 3 ** 3) + (p + 1) * ibukiyama_symbol(3, 0, -1, 1) / Integer( 2 ** 2 * 3 ** 3 ) elif p % 3 == 2: H7 = (2 * k - 3) * (p - 1) / Integer(2 ** 2 * 3 ** 3) + (p - 1) * ibukiyama_symbol( 3, 0, -1, 1 ) / Integer(2 * 3 ** 3) else: H7 = 5 * (2 * k - 3) / Integer(2 ** 2 * 3 ** 3) + ibukiyama_symbol(3, 0, -1, 1) / Integer(3 ** 3) H8 = ibukiyama_symbol(12, 1, 0, 0, -1, -1, -1, -1, 0, 0, 1, 1, 1) / Integer(2 * 3) H9 = ( 2 * ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(3 ** 2) if p != 2 else ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(2 * 3 ** 2) ) H10 = (1 + kronecker_symbol(5, p)) * ibukiyama_symbol(5, 1, 0, 0, -1, 0) / Integer(5) H11 = (1 + kronecker_symbol(2, p)) * ibukiyama_symbol(4, 1, 0, 0, -1) / Integer(2 ** 3) if p % 12 == 1: H12 = ibukiyama_symbol(3, 0, 1, -1) / Integer(2 * 3) elif p % 12 == 11: H12 = (-1) ** k / Integer(2 * 3) elif p == 2 or p == 3: H12 = (-1) ** k / Integer(2 ** 2 * 3) else: H12 = 0 I1 = ibukiyama_symbol(6, 0, 1, 1, 0, -1, -1) / Integer(6) I2 = ibukiyama_symbol(3, -2, 1, 1) / Integer(2 * 3 ** 2) if p == 3: I3 = ibukiyama_symbol(3, -2, 1, 1) / Integer(3 ** 2) elif p % 3 == 1: I3 = 2 * ibukiyama_symbol(3, -1, 1, 0) / Integer(3 ** 2) else: I3 = 2 * ibukiyama_symbol(3, -1, 0, 1) / Integer(3 ** 2) I4 = ibukiyama_symbol(4, -1, 1, 1, -1) / Integer(2 ** 2) I5 = (-1) ** k / Integer(2 ** 3) I6 = (-1) ** k * (2 - kronecker_symbol(-1, p)) / Integer(2 ** 4) I7 = -(-1) ** k * (2 * k - 3) / Integer(2 ** 3 * 3) I8 = -p * (2 * k - 3) / Integer(2 ** 4 * 3 ** 2) I9 = -1 / Integer(2 ** 3 * 3) I10 = (p + 1) / Integer(2 ** 3 * 3) I11 = -(1 + kronecker_symbol(-1, p)) / Integer(8) I12 = -(1 + kronecker_symbol(-3, p)) / Integer(6) cuspidal_dim = ( H1 + H2 + H3 + H4 + H5 + H6 + H7 + H8 + H9 + H10 + H11 + H12 + I1 + I2 + I3 + I4 + I5 + I6 + I7 + I8 + I9 + I10 + I11 + I12 ) mfs = ModularForms(1, self.__weight) return cuspidal_dim + mfs.dimension() + mfs.cuspidal_subspace().dimension()
def _find_scaling_L_ratio(self): r""" This function is use to set ``_scaling``, the factor used to adjust the scalar multiple of the modular symbol. If `[0]`, the modular symbol evaluated at 0, is non-zero, we can just scale it with respect to the approximation of the L-value. It is known that the quotient is a rational number with small denominator. Otherwise we try to scale using quadratic twists. ``_scaling`` will be set to a rational non-zero multiple if we succeed and to 1 otherwise. Even if we fail we scale at least to make up the difference between the periods of the `X_0`-optimal curve and our given curve `E` in the isogeny class. EXAMPLES:: sage : m = EllipticCurve('11a1').modular_symbol(use_eclib=True) sage : m._scaling 1 sage: m = EllipticCurve('11a2').modular_symbol(use_eclib=True) sage: m._scaling 5/2 sage: m = EllipticCurve('11a3').modular_symbol(use_eclib=True) sage: m._scaling 1/10 sage: m = EllipticCurve('11a1').modular_symbol(use_eclib=False) sage: m._scaling 1/5 sage: m = EllipticCurve('11a2').modular_symbol(use_eclib=False) sage: m._scaling 1 sage: m = EllipticCurve('11a3').modular_symbol(use_eclib=False) sage: m._scaling 1/25 sage: m = EllipticCurve('37a1').modular_symbol(use_eclib=False) sage: m._scaling 1 sage: m = EllipticCurve('37a1').modular_symbol(use_eclib=True) sage: m._scaling -1 sage: m = EllipticCurve('389a1').modular_symbol(use_eclib=True) sage: m._scaling -1/2 sage: m = EllipticCurve('389a1').modular_symbol(use_eclib=False) sage: m._scaling 2 sage: m = EllipticCurve('196a1').modular_symbol(use_eclib=False) sage: m._scaling 1/2 Some harder cases fail:: sage: m = EllipticCurve('121b1').modular_symbol(use_eclib=False) Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1, 2 or -2. sage: m._scaling 1 TESTS:: sage: rk0 = ['11a1', '11a2', '15a1', '27a1', '37b1'] sage: for la in rk0: # long time (3s on sage.math, 2011) ....: E = EllipticCurve(la) ....: me = E.modular_symbol(use_eclib = True) ....: ms = E.modular_symbol(use_eclib = False) ....: print E.lseries().L_ratio()*E.real_components(), me(0), ms(0) 1/5 1/5 1/5 1 1 1 1/4 1/4 1/4 1/3 1/3 1/3 2/3 2/3 2/3 sage: rk1 = ['37a1','43a1','53a1', '91b1','91b2','91b3'] sage: [EllipticCurve(la).modular_symbol(use_eclib=True)(0) for la in rk1] # long time (1s on sage.math, 2011) [0, 0, 0, 0, 0, 0] sage: for la in rk1: # long time (8s on sage.math, 2011) ....: E = EllipticCurve(la) ....: m = E.modular_symbol(use_eclib = True) ....: lp = E.padic_lseries(5) ....: for D in [5,17,12,8]: ....: ED = E.quadratic_twist(D) ....: md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) ....: etaD = lp._quotient_of_periods_to_twist(D) ....: assert ED.lseries().L_ratio()*ED.real_components() * etaD == md """ E = self._E self._scaling = 1 # by now. self._failed_to_scale = False if self._sign == 1: at0 = self(0) # print 'modular symbol evaluates to ',at0,' at 0' if at0 != 0: l1 = self.__lalg__(1) if at0 != l1: verbose('scale modular symbols by %s' % (l1 / at0)) self._scaling = l1 / at0 else: # if [0] = 0, we can still hope to scale it correctly by considering twists of E Dlist = [ 5, 8, 12, 13, 17, 21, 24, 28, 29, 33, 37, 40, 41, 44, 53, 56, 57, 60, 61, 65, 69, 73, 76, 77, 85, 88, 89, 92, 93, 97 ] # a list of positive fundamental discriminants j = 0 at0 = 0 # computes [0]+ for the twist of E by D until one value is non-zero while j < 30 and at0 == 0: D = Dlist[j] # the following line checks if the twist of the newform of E by D is a newform # this is to avoid that we 'twist back' if all( valuation(E.conductor(), ell) <= valuation(D, ell) for ell in prime_divisors(D)): at0 = sum([ kronecker_symbol(D, u) * self(ZZ(u) / D) for u in range(1, abs(D)) ]) j += 1 if j == 30 and at0 == 0: # curves like "121b1", "225a1", "225e1", "256a1", "256b1", "289a1", "361a1", "400a1", "400c1", "400h1", "441b1", "441c1", "441d1", "441f1 .. will arrive here self._failed_to_scale = True self.__scale_by_periods_only__() else: l1 = self.__lalg__(D) if at0 != l1: verbose('scale modular symbols by %s found at D=%s ' % (l1 / at0, D), level=2) self._scaling = l1 / at0 else: # that is when sign = -1 Dlist = [ -3, -4, -7, -8, -11, -15, -19, -20, -23, -24, -31, -35, -39, -40, -43, -47, -51, -52, -55, -56, -59, -67, -68, -71, -79, -83, -84, -87, -88, -91 ] # a list of negative fundamental discriminants j = 0 at0 = 0 while j < 30 and at0 == 0: # computes [0]+ for the twist of E by D until one value is non-zero D = Dlist[j] if all( valuation(E.conductor(), ell) <= valuation(D, ell) for ell in prime_divisors(D)): at0 = -sum([ kronecker_symbol(D, u) * self(ZZ(u) / D) for u in range(1, abs(D)) ]) j += 1 if j == 30 and at0 == 0: # no more hope for a normalization # we do at least a scaling with the quotient of the periods self._failed_to_scale = True self.__scale_by_periods_only__() else: l1 = self.__lalg__(D) if at0 != l1: verbose('scale modular symbols by %s' % (l1 / at0)) self._scaling = l1 / at0
def siegel_product(self, u): """ Computes the infinite product of local densities of the quadratic form for the number `u`. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: Q.theta_series(11) 1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10 + O(q^11) sage: Q.siegel_product(1) 8 sage: Q.siegel_product(2) ## This one is wrong -- expect 24, and the higher powers of 2 don't work... =( 24 sage: Q.siegel_product(3) 32 sage: Q.siegel_product(5) 48 sage: Q.siegel_product(6) 96 sage: Q.siegel_product(7) 64 sage: Q.siegel_product(9) 104 sage: Q.local_density(2,1) 1 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 1 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 # long time (2s on sage.math, 2014) 1 sage: Q.local_density(2,2) 3/2 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 3/2 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 # long time (2s on sage.math, 2014) 3/2 TESTS:: sage: [1] + [Q.siegel_product(ZZ(a)) for a in range(1,11)] == Q.theta_series(11).list() # long time (2s on sage.math, 2014) True """ ## Protect u (since it fails often if it's an just an int!) u = ZZ(u) n = self.dim() d = self.det() ## ??? Warning: This is a factor of 2^n larger than it should be! ## DIAGNOSTIC verbose("n = " + str(n)) verbose("d = " + str(d)) verbose("In siegel_product: d = ", d, "\n"); ## Product of "bad" places to omit S = 2 * d * u ## DIAGNOSTIC verbose("siegel_product Break 1. \n") verbose(" u = ", u, "\n") ## Make the odd generic factors if ((n % 2) == 1): m = (n-1) // 2 d1 = fundamental_discriminant(((-1)**m) * 2*d * u) ## Replaced d by 2d here to compensate for the determinant f = abs(d1) ## gaining an odd power of 2 by using the matrix of 2Q instead ## of the matrix of Q. ## --> Old d1 = CoreDiscriminant((mpz_class(-1)^m) * d * u); ## Make the ratio of factorials factor: [(2m)! / m!] * prod_{i=1}^m (2*i-1) factor1 = 1 for i in range(1, m+1): factor1 *= 2*i - 1 for i in range(m+1, 2*m + 1): factor1 *= i genericfactor = factor1 * ((u / f) ** m) \ * QQ(sqrt((2 ** n) * f) / (u * d)) \ * abs(QuadraticBernoulliNumber(m, d1) / bernoulli(2*m)) ## DIAGNOSTIC verbose("siegel_product Break 2. \n") ## Make the even generic factor if ((n % 2) == 0): m = n // 2 d1 = fundamental_discriminant(((-1)**m) * d) f = abs(d1) ## DIAGNOSTIC #cout << " mpz_class(-1)^m = " << (mpz_class(-1)^m) << " and d = " << d << endl; #cout << " f = " << f << " and d1 = " << d1 << endl; genericfactor = m / QQ(sqrt(f*d)) \ * ((u/2) ** (m-1)) * (f ** m) \ / abs(QuadraticBernoulliNumber(m, d1)) \ * (2 ** m) ## This last factor compensates for using the matrix of 2*Q ##return genericfactor ## Omit the generic factors in S and compute them separately omit = 1 include = 1 S_divisors = prime_divisors(S) ## DIAGNOSTIC #cout << "\n S is " << S << endl; #cout << " The Prime divisors of S are :"; #PrintV(S_divisors); for p in S_divisors: Q_normal = self.local_normal_form(p) ## DIAGNOSTIC verbose(" p = " + str(p) + " and its Kronecker symbol (d1/p) = (" + str(d1) + "/" + str(p) + ") is " + str(kronecker_symbol(d1, p)) + "\n") omit *= 1 / (1 - (kronecker_symbol(d1, p) / (p**m))) ## DIAGNOSTIC verbose(" omit = " + str(omit) + "\n") verbose(" Q_normal is \n" + str(Q_normal) + "\n") verbose(" Q_normal = \n" + str(Q_normal)) verbose(" p = " + str(p) + "\n") verbose(" u = " +str(u) + "\n") verbose(" include = " + str(include) + "\n") include *= Q_normal.local_density(p, u) ## DIAGNOSTIC #cout << " Including the p = " << p << " factor: " << local_density(Q_normal, p, u) << endl; ## DIAGNSOTIC verbose(" --- Exiting loop \n") #// **************** Important ******************* #// Additional fix (only included for n=4) to deal #// with the power of 2 introduced at the real place #// by working with Q instead of 2*Q. This needs to #// be done for all other n as well... #/* #if (n==4) # genericfactor = 4 * genericfactor; #*/ ## DIAGNSOTIC #cout << endl; #cout << " generic factor = " << genericfactor << endl; #cout << " omit = " << omit << endl; #cout << " include = " << include << endl; #cout << endl; ## DIAGNSOTIC #// cout << "siegel_product Break 3. " << endl; ## Return the final factor (and divide by 2 if n=2) if (n == 2): return (genericfactor * omit * include / 2) else: return (genericfactor * omit * include)
def an_padic(self, p, prec=0, use_twists=True): r""" Returns the conjectural order of `Sha(E/\QQ)`, according to the `p`-adic analogue of the Birch and Swinnerton-Dyer conjecture as formulated in [MTT]_ and [BP]_. REFERENCES: .. [MTT] \B. Mazur, J. Tate, and J. Teitelbaum, On `p`-adic analogues of the conjectures of Birch and Swinnerton-Dyer, Inventiones mathematicae 84, (1986), 1-48. .. [BP] Dominique Bernardi and Bernadette Perrin-Riou, Variante `p`-adique de la conjecture de Birch et Swinnerton-Dyer (le cas supersingulier), C. R. Acad. Sci. Paris, Sér I. Math., 317 (1993), no. 3, 227-232. INPUT: - ``p`` - a prime > 3 - ``prec`` (optional) - the precision used in the computation of the `p`-adic L-Series - ``use_twists`` (default = ``True``) - If ``True`` the algorithm may change to a quadratic twist with minimal conductor to do the modular symbol computations rather than using the modular symbols of the curve itself. If ``False`` it forces the computation using the modular symbols of the curve itself. OUTPUT: `p`-adic number - that conjecturally equals `\# Sha(E/\QQ)`. If ``prec`` is set to zero (default) then the precision is set so that at least the first `p`-adic digit of conjectural `\# Sha(E/\QQ)` is determined. EXAMPLES: Good ordinary examples:: sage: EllipticCurve('11a1').sha().an_padic(5) # rank 0 1 + O(5^22) sage: EllipticCurve('43a1').sha().an_padic(5) # rank 1 1 + O(5) sage: EllipticCurve('389a1').sha().an_padic(5,4) # rank 2, long time (2s on sage.math, 2011) 1 + O(5^3) sage: EllipticCurve('858k2').sha().an_padic(7) # rank 0, non trivial sha, long time (10s on sage.math, 2011) 7^2 + O(7^24) sage: EllipticCurve('300b2').sha().an_padic(3) # 9 elements in sha, long time (2s on sage.math, 2011) 3^2 + O(3^24) sage: EllipticCurve('300b2').sha().an_padic(7, prec=6) # long time 2 + 7 + O(7^8) Exceptional cases:: sage: EllipticCurve('11a1').sha().an_padic(11) # rank 0 1 + O(11^22) sage: EllipticCurve('130a1').sha().an_padic(5) # rank 1 1 + O(5) Non-split, but rank 0 case (:trac:`7331`):: sage: EllipticCurve('270b1').sha().an_padic(5) # rank 0, long time (2s on sage.math, 2011) 1 + O(5^22) The output has the correct sign:: sage: EllipticCurve('123a1').sha().an_padic(41) # rank 1, long time (3s on sage.math, 2011) 1 + O(41) Supersingular cases:: sage: EllipticCurve('34a1').sha().an_padic(5) # rank 0 1 + O(5^22) sage: EllipticCurve('53a1').sha().an_padic(5) # rank 1, long time (11s on sage.math, 2011) 1 + O(5) Cases that use a twist to a lower conductor:: sage: EllipticCurve('99a1').sha().an_padic(5) 1 + O(5) sage: EllipticCurve('240d3').sha().an_padic(5) # sha has 4 elements here 4 + O(5) sage: EllipticCurve('448c5').sha().an_padic(7,prec=4, use_twists=False) # long time (2s on sage.math, 2011) 2 + 7 + O(7^6) sage: EllipticCurve([-19,34]).sha().an_padic(5) # see trac #6455, long time (4s on sage.math, 2011) 1 + O(5) Test for :trac:`15737`:: sage: E = EllipticCurve([-100,0]) sage: s = E.sha() sage: s.an_padic(13) 1 + O(13^20) """ try: return self.__an_padic[(p,prec)] except AttributeError: self.__an_padic = {} except KeyError: pass E = self.Emin tam = E.tamagawa_product() tors = E.torsion_order()**2 r = E.rank() if r > 0 : reg = E.padic_regulator(p) else: if E.is_supersingular(p): reg = vector([ Qp(p,20)(1), 0 ]) else: reg = Qp(p,20)(1) if use_twists and p > 2: Et, D = E.minimal_quadratic_twist() # trac 6455 : we have to assure that the twist back is allowed D = ZZ(D) if D % p == 0: D = ZZ(D/p) for ell in D.prime_divisors(): if ell % 2 == 1: if Et.conductor() % ell**2 == 0: D = ZZ(D/ell) ve = valuation(D,2) de = ZZ( (D/2**ve).abs() ) if de % 4 == 3: de = -de Et = E.quadratic_twist(de) # now check individually if we can twist by -1 or 2 or -2 Nmin = Et.conductor() Dmax = de for DD in [-4*de,8*de,-8*de]: Et = E.quadratic_twist(DD) if Et.conductor() < Nmin and valuation(Et.conductor(),2) <= valuation(DD,2): Nmin = Et.conductor() Dmax = DD D = Dmax Et = E.quadratic_twist(D) lp = Et.padic_lseries(p) else : lp = E.padic_lseries(p) D = 1 if r == 0 and D == 1: # short cut for rank 0 curves, we do not # to compute the p-adic L-function, the leading # term will be the L-value divided by the Neron # period. ms = E.modular_symbol(sign=+1, normalize='L_ratio') lstar = ms(0)/E.real_components() bsd = tam/tors if prec == 0: #prec = valuation(lstar/bsd, p) prec = 20 shan = Qp(p,prec=prec+2)(lstar/bsd) elif E.is_ordinary(p): K = reg.parent() lg = log(K(1+p)) if (E.is_good(p) or E.ap(p) == -1): if not E.is_good(p): eps = 2 else: eps = (1-arith.kronecker_symbol(D,p)/lp.alpha())**2 # according to the p-adic BSD this should be equal to the leading term of the p-adic L-series divided by sha: bsdp = tam * reg * eps/tors/lg**r else: r += 1 # exceptional zero eq = E.tate_curve(p) Li = eq.L_invariant() # according to the p-adic BSD (Mazur-Tate-Teitelbaum) # this should be equal to the leading term of the p-adic L-series divided by sha: bsdp = tam * reg * Li/tors/lg**r v = bsdp.valuation() if v > 0: verbose("the prime is irregular for this curve.") # determine how much prec we need to prove at least the # triviality of the p-primary part of Sha if prec == 0: n = max(v,2) bounds = lp._prec_bounds(n,r+1) while bounds[r] <= v: n += 1 bounds = lp._prec_bounds(n,r+1) verbose("set precision to %s"%n) else: n = max(2,prec) not_yet_enough_prec = True while not_yet_enough_prec: lps = lp.series(n,quadratic_twist=D,prec=r+1) lstar = lps[r] if (lstar != 0) or (prec != 0): not_yet_enough_prec = False else: n += 1 verbose("increased precision to %s"%n) shan = lstar/bsdp elif E.is_supersingular(p): K = reg[0].parent() lg = log(K(1+p)) # according to the p-adic BSD this should be equal to the leading term of the D_p - valued # L-series : bsdp = tam /tors/lg**r * reg # note this is an element in Q_p^2 verbose("the algebraic leading terms : %s"%bsdp) v = [bsdp[0].valuation(),bsdp[1].valuation()] if prec == 0: n = max(min(v)+2,3) else: n = max(3,prec) verbose("...computing the p-adic L-series") not_yet_enough_prec = True while not_yet_enough_prec: lps = lp.Dp_valued_series(n,quadratic_twist=D,prec=r+1) lstar = [lps[0][r],lps[1][r]] verbose("the leading terms : %s"%lstar) if (lstar[0] != 0 or lstar[1] != 0) or ( prec != 0): not_yet_enough_prec = False else: n += 1 verbose("increased precision to %s"%n) verbose("...putting things together") if bsdp[0] != 0: shan0 = lstar[0]/bsdp[0] else: shan0 = 0 # this should actually never happen if bsdp[1] != 0: shan1 = lstar[1]/bsdp[1] else: shan1 = 0 # this should conjecturally only happen when the rank is 0 verbose("the two values for Sha : %s"%[shan0,shan1]) # check consistency (the first two are only here to avoid a bug in the p-adic L-series # (namely the coefficients of zero-relative precision are treated as zero) if shan0 != 0 and shan1 != 0 and shan0 - shan1 != 0: raise RuntimeError("There must be a bug in the supersingular routines for the p-adic BSD.") #take the better if shan1 == 0 or shan0.precision_relative() > shan1.precision_relative(): shan = shan0 else: shan = shan1 else: raise ValueError("The curve has to have semi-stable reduction at p.") self.__an_padic[(p,prec)] = shan return shan
def an_padic(self, p, prec=0, use_twists=True): r""" Returns the conjectural order of `Sha(E/\QQ)`, according to the `p`-adic analogue of the Birch and Swinnerton-Dyer conjecture as formulated in [MTT1986]_ and [BP1993]_. INPUT: - ``p`` - a prime > 3 - ``prec`` (optional) - the precision used in the computation of the `p`-adic L-Series - ``use_twists`` (default = ``True``) - If ``True`` the algorithm may change to a quadratic twist with minimal conductor to do the modular symbol computations rather than using the modular symbols of the curve itself. If ``False`` it forces the computation using the modular symbols of the curve itself. OUTPUT: `p`-adic number - that conjecturally equals `\# Sha(E/\QQ)`. If ``prec`` is set to zero (default) then the precision is set so that at least the first `p`-adic digit of conjectural `\# Sha(E/\QQ)` is determined. EXAMPLES: Good ordinary examples:: sage: EllipticCurve('11a1').sha().an_padic(5) # rank 0 1 + O(5^22) sage: EllipticCurve('43a1').sha().an_padic(5) # rank 1 1 + O(5) sage: EllipticCurve('389a1').sha().an_padic(5,4) # rank 2, long time (2s on sage.math, 2011) 1 + O(5^3) sage: EllipticCurve('858k2').sha().an_padic(7) # rank 0, non trivial sha, long time (10s on sage.math, 2011) 7^2 + O(7^24) sage: EllipticCurve('300b2').sha().an_padic(3) # 9 elements in sha, long time (2s on sage.math, 2011) 3^2 + O(3^24) sage: EllipticCurve('300b2').sha().an_padic(7, prec=6) # long time 2 + 7 + O(7^8) Exceptional cases:: sage: EllipticCurve('11a1').sha().an_padic(11) # rank 0 1 + O(11^22) sage: EllipticCurve('130a1').sha().an_padic(5) # rank 1 1 + O(5) Non-split, but rank 0 case (:trac:`7331`):: sage: EllipticCurve('270b1').sha().an_padic(5) # rank 0, long time (2s on sage.math, 2011) 1 + O(5^22) The output has the correct sign:: sage: EllipticCurve('123a1').sha().an_padic(41) # rank 1, long time (3s on sage.math, 2011) 1 + O(41) Supersingular cases:: sage: EllipticCurve('34a1').sha().an_padic(5) # rank 0 1 + O(5^22) sage: EllipticCurve('53a1').sha().an_padic(5) # rank 1, long time (11s on sage.math, 2011) 1 + O(5) Cases that use a twist to a lower conductor:: sage: EllipticCurve('99a1').sha().an_padic(5) 1 + O(5) sage: EllipticCurve('240d3').sha().an_padic(5) # sha has 4 elements here 4 + O(5) sage: EllipticCurve('448c5').sha().an_padic(7,prec=4, use_twists=False) # long time (2s on sage.math, 2011) 2 + 7 + O(7^6) sage: EllipticCurve([-19,34]).sha().an_padic(5) # see trac #6455, long time (4s on sage.math, 2011) 1 + O(5) Test for :trac:`15737`:: sage: E = EllipticCurve([-100,0]) sage: s = E.sha() sage: s.an_padic(13) 1 + O(13^20) """ try: return self.__an_padic[(p, prec)] except AttributeError: self.__an_padic = {} except KeyError: pass E = self.Emin tam = E.tamagawa_product() tors = E.torsion_order()**2 r = E.rank() if r > 0: reg = E.padic_regulator(p) else: if E.is_supersingular(p): reg = vector([Qp(p, 20)(1), 0]) else: reg = Qp(p, 20)(1) if use_twists and p > 2: Et, D = E.minimal_quadratic_twist() # trac 6455 : we have to assure that the twist back is allowed D = ZZ(D) if D % p == 0: D = ZZ(D / p) for ell in D.prime_divisors(): if ell % 2 == 1: if Et.conductor() % ell**2 == 0: D = ZZ(D / ell) ve = valuation(D, 2) de = ZZ((D / 2**ve).abs()) if de % 4 == 3: de = -de Et = E.quadratic_twist(de) # now check individually if we can twist by -1 or 2 or -2 Nmin = Et.conductor() Dmax = de for DD in [-4 * de, 8 * de, -8 * de]: Et = E.quadratic_twist(DD) if Et.conductor() < Nmin and valuation(Et.conductor(), 2) <= valuation(DD, 2): Nmin = Et.conductor() Dmax = DD D = Dmax Et = E.quadratic_twist(D) lp = Et.padic_lseries(p) else: lp = E.padic_lseries(p) D = 1 if r == 0 and D == 1: # short cut for rank 0 curves, we do not # to compute the p-adic L-function, the leading # term will be the L-value divided by the Neron # period. ms = E.modular_symbol(sign=+1, normalize='L_ratio') lstar = ms(0) / E.real_components() bsd = tam / tors if prec == 0: # prec = valuation(lstar/bsd, p) prec = 20 shan = Qp(p, prec=prec + 2)(lstar / bsd) elif E.is_ordinary(p): K = reg.parent() lg = log(K(1 + p)) if (E.is_good(p) or E.ap(p) == -1): if not E.is_good(p): eps = 2 else: eps = (1 - arith.kronecker_symbol(D, p) / lp.alpha())**2 # according to the p-adic BSD this should be equal to the leading term of the p-adic L-series divided by sha: bsdp = tam * reg * eps / tors / lg**r else: r += 1 # exceptional zero eq = E.tate_curve(p) Li = eq.L_invariant() # according to the p-adic BSD (Mazur-Tate-Teitelbaum) # this should be equal to the leading term of the p-adic L-series divided by sha: bsdp = tam * reg * Li / tors / lg**r v = bsdp.valuation() if v > 0: verbose("the prime is irregular for this curve.") # determine how much prec we need to prove at least the # triviality of the p-primary part of Sha if prec == 0: n = max(v, 2) bounds = lp._prec_bounds(n, r + 1) while bounds[r] <= v: n += 1 bounds = lp._prec_bounds(n, r + 1) verbose("set precision to %s" % n) else: n = max(2, prec) not_yet_enough_prec = True while not_yet_enough_prec: lps = lp.series(n, quadratic_twist=D, prec=r + 1) lstar = lps[r] if (lstar != 0) or (prec != 0): not_yet_enough_prec = False else: n += 1 verbose("increased precision to %s" % n) shan = lstar / bsdp elif E.is_supersingular(p): K = reg[0].parent() lg = log(K(1 + p)) # according to the p-adic BSD this should be equal to the leading term of the D_p - valued # L-series : bsdp = tam / tors / lg**r * reg # note this is an element in Q_p^2 verbose("the algebraic leading terms : %s" % bsdp) v = [bsdp[0].valuation(), bsdp[1].valuation()] if prec == 0: n = max(min(v) + 2, 3) else: n = max(3, prec) verbose("...computing the p-adic L-series") not_yet_enough_prec = True while not_yet_enough_prec: lps = lp.Dp_valued_series(n, quadratic_twist=D, prec=r + 1) lstar = [lps[0][r], lps[1][r]] verbose("the leading terms : %s" % lstar) if (lstar[0] != 0 or lstar[1] != 0) or (prec != 0): not_yet_enough_prec = False else: n += 1 verbose("increased precision to %s" % n) verbose("...putting things together") if bsdp[0] != 0: shan0 = lstar[0] / bsdp[0] else: shan0 = 0 # this should actually never happen if bsdp[1] != 0: shan1 = lstar[1] / bsdp[1] else: shan1 = 0 # this should conjecturally only happen when the rank is 0 verbose("the two values for Sha : %s" % [shan0, shan1]) # check consistency (the first two are only here to avoid a bug in the p-adic L-series # (namely the coefficients of zero-relative precision are treated as zero) if shan0 != 0 and shan1 != 0 and shan0 - shan1 != 0: raise RuntimeError( "There must be a bug in the supersingular routines for the p-adic BSD." ) # take the better if shan1 == 0 or shan0.precision_relative( ) > shan1.precision_relative(): shan = shan0 else: shan = shan1 else: raise ValueError( "The curve has to have semi-stable reduction at p.") self.__an_padic[(p, prec)] = shan return shan
def possible_isogeny_degrees(E, verbose=False): r""" Return a list of primes `\ell` sufficient to generate the isogeny class of `E`. INPUT: - ``E`` -- An elliptic curve defined over a number field. OUTPUT: A finite list of primes `\ell` such that every curve isogenous to this curve can be obtained by a finite sequence of isogenies of degree one of the primes in the list. ALGORITHM: For curves without CM, the set may be taken to be the finite set of primes at which the Galois representation is not surjective, since the existence of an `\ell`-isogeny is equivalent to the image of the mod-`\ell` Galois representation being contained in a Borel subgroup. For curves with CM by the order `O` of discriminant `d`, the Galois representation is always non-surjective and the curve will admit `\ell`-isogenies for infinitely many primes `\ell`, but there are (of course) only finitely many codomains `E'`. The primes can be divided according to the discriminant `d'` of the CM order `O'` associated to `E`: either `O=O'`, or one contains the other with index `\ell`, since `\ell O\subset O'` and vice versa. Case (1): `O=O'`. The degrees of all isogenies between `E` and `E'` are precisely the integers represented by one of the classes of binary quadratic forms `Q` of discriminant `d`. Hence to obtain all possible isomorphism classes of codomain `E'`, we need only use one prime `\ell` represented by each such class `Q`. It would in fact suffice to use primes represented by forms which generate the class group. Here we simply omit the principal class and one from each pair of inverse classes, and include a prime represented by each of the remaining forms. Case (2): `[O':O]=\ell`: so `d=\ell^2d;`. We include all prime divisors of `d`. Case (3): `[O:O']=\ell`: we may assume that `\ell` does not divide `d` as we have already included these, so `\ell` either splits or is inert in `O`; the class numbers satisfy `h(O')=(\ell\pm1)h(O)` accordingly. We include all primes `\ell` such that `\ell\pm1` divides the degree `[K:\QQ]`. For curves with only potential CM we proceed as in the CM case, using `2[K:\QQ]` instead of `[K:\QQ]`. EXAMPLES: For curves without CM we determine the primes at which the mod `p` Galois representation is reducible, i.e. contained in a Borel subgroup:: sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees sage: E = EllipticCurve('11a1') sage: possible_isogeny_degrees(E) [5] We check that in this case `E` really does have rational `5`-isogenies:: sage: [phi.degree() for phi in E.isogenies_prime_degree()] [5, 5] Over an extension field:: sage: E3 = E.change_ring(CyclotomicField(3)) sage: possible_isogeny_degrees(E3) [5] sage: [phi.degree() for phi in E3.isogenies_prime_degree()] [5, 5] For curves with CM by a quadratic order of class number greater than `1`, we use the structure of the class group to only give one prime in each ideal class:: sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1]) sage: L.<a> = NumberField(pol) sage: j = hilbert_class_polynomial(-23).roots(L,multiplicities=False)[0] sage: E = EllipticCurve(j=j) sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees sage: possible_isogeny_degrees(E, verbose=True) CM case, discriminant = -23 initial primes: {2} upward primes: {} downward ramified primes: {} downward split primes: {2, 3} downward inert primes: {5} primes generating the class group: [2] Complete set of primes: {2, 3, 5} [2, 3, 5] """ if E.has_cm(): d = E.cm_discriminant() if verbose: print("CM case, discriminant = %s" % d) from sage.libs.pari.all import pari from sage.sets.all import Set from sage.arith.all import kronecker_symbol n = E.base_field().absolute_degree() if not E.has_rational_cm(): n *= 2 divs = n.divisors() data = pari(d).quadclassunit() # This has 4 components: the class number, class group # structure (ignored), class group generators (as quadratic # forms) and regulator (=1 since d<0, ignored). h = data[0].sage() # We must have 2*h dividing n, and will need the quotient so # see if the j-invariants of any proper sub-orders could lie # in the same field n_over_2h = n//(2*h) # Collect possible primes. First put in 2, and also 3 for # discriminant -3 (special case because of units): L = Set([ZZ(2), ZZ(3)]) if d==-3 else Set([ZZ(2)]) if verbose: print ("initial primes: %s" % L) # Step 1: "vertical" primes l such that the isogenous curve # has CM by an order whose index is l or 1/l times the index # of the order O of discriminant d. The latter case can only # happen when l^2 divides d. # Compute the ramified primes ram_l = d.odd_part().prime_factors() # if the CM is not rational we include all ramified primes, # which is simpler than using the class group later: if not E.has_rational_cm(): L1 = Set(ram_l) L += L1 if verbose: print ("ramified primes: %s" % L1) else: # Find the "upward" primes (index divided by l): L1 = Set([l for l in ram_l if d.valuation(l)>1]) L += L1 if verbose: print ("upward primes: %s" % L1) # Find the "downward" primes (index multiplied by l, class # number multiplied by l-kronecker_symbol(d,l)): # (a) ramified primes; the suborder has class number l*h, so l # must divide n/2h: L1 = Set([l for l in ram_l if l.divides(n_over_2h)]) L += L1 if verbose: print ("downward ramified primes: %s" % L1) # (b) split primes; the suborder has class number (l-1)*h, so # l-1 must divide n/2h: L1 = Set([lm1+1 for lm1 in divs if (lm1+1).is_prime() and kronecker_symbol(d,lm1+1)==+1]) L += L1 if verbose: print ("downward split primes: %s" % L1) # (c) inert primes; the suborder has class number (l+1)*h, so # l+1 must divide n/2h: L1 = Set([lp1-1 for lp1 in divs if (lp1-1).is_prime() and kronecker_symbol(d,lp1-1)==-1]) L += L1 if verbose: print ("downward inert primes: %s" % L1) # Now find primes represented by each form of discriminant d. # In the rational CM case, we use all forms associated to # generators of the class group, otherwise only forms of order # 2: if E.has_rational_cm(): from sage.quadratic_forms.binary_qf import BinaryQF Qs = [BinaryQF(list(q)) for q in data[2]] L1 = [Q.small_prime_value() for Q in Qs] if verbose: print("primes generating the class group: %s" % L1) L += Set(L1) # Return sorted list if verbose: print("Complete set of primes: %s" % L) return sorted(list(L)) # Non-CM case if verbose: print("Non-CM case, using Galois representation") return E.galois_representation().reducible_primes()
def siegel_product(self, u): """ Computes the infinite product of local densities of the quadratic form for the number `u`. EXAMPLES:: sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1]) sage: Q.theta_series(11) 1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10 + O(q^11) sage: Q.siegel_product(1) 8 sage: Q.siegel_product(2) ## This one is wrong -- expect 24, and the higher powers of 2 don't work... =( 24 sage: Q.siegel_product(3) 32 sage: Q.siegel_product(5) 48 sage: Q.siegel_product(6) 96 sage: Q.siegel_product(7) 64 sage: Q.siegel_product(9) 104 sage: Q.local_density(2,1) 1 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 1 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 1]) / M^3 # long time (2s on sage.math, 2014) 1 sage: Q.local_density(2,2) 3/2 sage: M = 4; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 3/2 sage: M = 16; len([v for v in mrange([M,M,M,M]) if Q(v) % M == 2]) / M^3 # long time (2s on sage.math, 2014) 3/2 TESTS:: sage: [1] + [Q.siegel_product(ZZ(a)) for a in range(1,11)] == Q.theta_series(11).list() # long time (2s on sage.math, 2014) True """ ## Protect u (since it fails often if it's an just an int!) u = ZZ(u) n = self.dim() d = self.det( ) ## ??? Warning: This is a factor of 2^n larger than it should be! # DIAGNOSTIC verbose("n = " + str(n)) verbose("d = " + str(d)) verbose("In siegel_product: d = " + str(d) + "\n") ## Product of "bad" places to omit S = 2 * d * u ## DIAGNOSTIC verbose("siegel_product Break 1. \n") verbose(" u = " + str(u) + "\n") ## Make the odd generic factors if ((n % 2) == 1): m = (n - 1) // 2 d1 = fundamental_discriminant( ((-1)**m) * 2 * d * u) ## Replaced d by 2d here to compensate for the determinant f = abs( d1) ## gaining an odd power of 2 by using the matrix of 2Q instead ## of the matrix of Q. ## --> Old d1 = CoreDiscriminant((mpz_class(-1)^m) * d * u); ## Make the ratio of factorials factor: [(2m)! / m!] * prod_{i=1}^m (2*i-1) factor1 = 1 for i in range(1, m + 1): factor1 *= 2 * i - 1 for i in range(m + 1, 2 * m + 1): factor1 *= i genericfactor = factor1 * ((u / f) ** m) \ * QQ(sqrt((2 ** n) * f) / (u * d)) \ * abs(QuadraticBernoulliNumber(m, d1) / bernoulli(2*m)) ## DIAGNOSTIC verbose("siegel_product Break 2. \n") ## Make the even generic factor if ((n % 2) == 0): m = n // 2 d1 = fundamental_discriminant(((-1)**m) * d) f = abs(d1) ## DIAGNOSTIC #cout << " mpz_class(-1)^m = " << (mpz_class(-1)^m) << " and d = " << d << endl; #cout << " f = " << f << " and d1 = " << d1 << endl; genericfactor = m / QQ(sqrt(f*d)) \ * ((u/2) ** (m-1)) * (f ** m) \ / abs(QuadraticBernoulliNumber(m, d1)) \ * (2 ** m) ## This last factor compensates for using the matrix of 2*Q ##return genericfactor ## Omit the generic factors in S and compute them separately omit = 1 include = 1 S_divisors = prime_divisors(S) ## DIAGNOSTIC #cout << "\n S is " << S << endl; #cout << " The Prime divisors of S are :"; #PrintV(S_divisors); for p in S_divisors: Q_normal = self.local_normal_form(p) ## DIAGNOSTIC verbose(" p = " + str(p) + " and its Kronecker symbol (d1/p) = (" + str(d1) + "/" + str(p) + ") is " + str(kronecker_symbol(d1, p)) + "\n") omit *= 1 / (1 - (kronecker_symbol(d1, p) / (p**m))) ## DIAGNOSTIC verbose(" omit = " + str(omit) + "\n") verbose(" Q_normal is \n" + str(Q_normal) + "\n") verbose(" Q_normal = \n" + str(Q_normal)) verbose(" p = " + str(p) + "\n") verbose(" u = " + str(u) + "\n") verbose(" include = " + str(include) + "\n") include *= Q_normal.local_density(p, u) ## DIAGNOSTIC #cout << " Including the p = " << p << " factor: " << local_density(Q_normal, p, u) << endl; ## DIAGNOSTIC verbose(" --- Exiting loop \n") #// **************** Important ******************* #// Additional fix (only included for n=4) to deal #// with the power of 2 introduced at the real place #// by working with Q instead of 2*Q. This needs to #// be done for all other n as well... #/* #if (n==4) # genericfactor = 4 * genericfactor; #*/ ## DIAGNOSTIC #cout << endl; #cout << " generic factor = " << genericfactor << endl; #cout << " omit = " << omit << endl; #cout << " include = " << include << endl; #cout << endl; ## DIAGNOSTIC #// cout << "siegel_product Break 3. " << endl; ## Return the final factor (and divide by 2 if n=2) if n == 2: return genericfactor * omit * include / 2 else: return genericfactor * omit * include
def _find_scaling_L_ratio(self): r""" This function is use to set ``_scaling``, the factor used to adjust the scalar multiple of the modular symbol. If `[0]`, the modular symbol evaluated at 0, is non-zero, we can just scale it with respect to the approximation of the L-value. It is known that the quotient is a rational number with small denominator. Otherwise we try to scale using quadratic twists. ``_scaling`` will be set to a rational non-zero multiple if we succeed and to 1 otherwise. Even if we fail we scale at least to make up the difference between the periods of the `X_0`-optimal curve and our given curve `E` in the isogeny class. EXAMPLES:: sage: m = EllipticCurve('11a1').modular_symbol(implementation="sage") sage: m._scaling 1/5 sage: m = EllipticCurve('11a2').modular_symbol(implementation="sage") sage: m._scaling 1 sage: m = EllipticCurve('11a3').modular_symbol(implementation="sage") sage: m._scaling 1/25 sage: m = EllipticCurve('37a1').modular_symbol(implementation="sage") sage: m._scaling 1 sage: m = EllipticCurve('37a1').modular_symbol() sage: m._scaling 1 sage: m = EllipticCurve('389a1').modular_symbol() sage: m._scaling 1 sage: m = EllipticCurve('389a1').modular_symbol(implementation="sage") sage: m._scaling 2 sage: m = EllipticCurve('196a1').modular_symbol(implementation="sage") sage: m._scaling 1/2 Some harder cases fail:: sage: m = EllipticCurve('121b1').modular_symbol(implementation="sage") Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1 and a power of 2 sage: m._scaling 1 TESTS:: sage: rk0 = ['11a1', '11a2', '15a1', '27a1', '37b1'] sage: for la in rk0: # long time (3s on sage.math, 2011) ....: E = EllipticCurve(la) ....: me = E.modular_symbol(implementation="eclib") ....: ms = E.modular_symbol(implementation="sage") ....: print("{} {} {}".format(E.lseries().L_ratio()*E.real_components(), me(0), ms(0))) 1/5 1/5 1/5 1 1 1 1/4 1/4 1/4 1/3 1/3 1/3 2/3 2/3 2/3 sage: rk1 = ['37a1','43a1','53a1', '91b1','91b2','91b3'] sage: [EllipticCurve(la).modular_symbol()(0) for la in rk1] # long time (1s on sage.math, 2011) [0, 0, 0, 0, 0, 0] sage: for la in rk1: # long time (8s on sage.math, 2011) ....: E = EllipticCurve(la) ....: m = E.modular_symbol() ....: lp = E.padic_lseries(5) ....: for D in [5,17,12,8]: ....: ED = E.quadratic_twist(D) ....: md = sum([kronecker(D,u)*m(ZZ(u)/D) for u in range(D)]) ....: etaD = lp._quotient_of_periods_to_twist(D) ....: assert ED.lseries().L_ratio()*ED.real_components() * etaD == md """ E = self._E self._scaling = 1 # initial value, may be changed later. self._failed_to_scale = False if self._sign == 1 : at0 = self(0) if at0 != 0 : l1 = self.__lalg__(1) if at0 != l1: verbose('scale modular symbols by %s'%(l1/at0)) self._scaling = l1/at0 else : # if [0] = 0, we can still hope to scale it correctly by considering twists of E Dlist = [5,8,12,13,17,21,24,28,29, 33, 37, 40, 41, 44, 53, 56, 57, 60, 61, 65, 69, 73, 76, 77, 85, 88, 89, 92, 93, 97] # a list of positive fundamental discriminants j = 0 at0 = 0 # computes [0]+ for the twist of E by D until one value is non-zero while j < 30 and at0 == 0 : D = Dlist[j] # the following line checks if the twist of the newform of E by D is a newform # this is to avoid that we 'twist back' if all( valuation(E.conductor(),ell)<= valuation(D,ell) for ell in prime_divisors(D) ) : at0 = sum([kronecker_symbol(D,u) * self(ZZ(u)/D) for u in range(1,abs(D))]) j += 1 if j == 30 and at0 == 0: # curves like "121b1", "225a1", "225e1", "256a1", "256b1", "289a1", "361a1", "400a1", "400c1", "400h1", "441b1", "441c1", "441d1", "441f1 .. will arrive here print("Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1 and a power of 2") self._failed_to_scale = True else : l1 = self.__lalg__(D) if at0 != l1: verbose('scale modular symbols by %s found at D=%s '%(l1/at0,D), level=2) self._scaling = l1/at0 else : # that is when sign = -1 Dlist = [-3,-4,-7,-8,-11,-15,-19,-20,-23,-24, -31, -35, -39, -40, -43, -47, -51, -52, -55, -56, -59, -67, -68, -71, -79, -83, -84, -87, -88, -91] # a list of negative fundamental discriminants j = 0 at0 = 0 while j < 30 and at0 == 0 : # computes [0]+ for the twist of E by D until one value is non-zero D = Dlist[j] if all( valuation(E.conductor(),ell)<= valuation(D,ell) for ell in prime_divisors(D) ) : at0 = - sum([kronecker_symbol(D,u) * self(ZZ(u)/D) for u in range(1,abs(D))]) j += 1 if j == 30 and at0 == 0: # no more hope for a normalization print("Warning : Could not normalize the modular symbols, maybe all further results will be multiplied by -1 and a power of 2") self._failed_to_scale = True else : l1 = self.__lalg__(D) if at0 != l1: verbose('scale modular symbols by %s'%(l1/at0)) self._scaling = l1/at0
def possible_isogeny_degrees(E, verbose=False): r""" Return a list of primes `\ell` sufficient to generate the isogeny class of `E`. INPUT: - ``E`` -- An elliptic curve defined over a number field. OUTPUT: A finite list of primes `\ell` such that every curve isogenous to this curve can be obtained by a finite sequence of isogenies of degree one of the primes in the list. ALGORITHM: For curves without CM, the set may be taken to be the finite set of primes at which the Galois representation is not surjective, since the existence of an `\ell`-isogeny is equivalent to the image of the mod-`\ell` Galois representation being contained in a Borel subgroup. For curves with CM by the order `O` of discriminant `d`, the Galois representation is always non-surjective and the curve will admit `\ell`-isogenies for infinitely many primes `\ell`, but there are (of course) only finitely many codomains `E'`. The primes can be divided according to the discriminant `d'` of the CM order `O'` associated to `E`: either `O=O'`, or one contains the other with index `\ell`, since `\ell O\subset O'` and vice versa. Case (1): `O=O'`. The degrees of all isogenies between `E` and `E'` are precisely the integers represented by one of the classes of binary quadratic forms `Q` of discriminant `d`. Hence to obtain all possible isomorphism classes of codomain `E'`, we need only use one prime `\ell` represented by each such class `Q`. It would in fact suffice to use primes represented by forms which generate the class group. Here we simply omit the principal class and one from each pair of inverse classes, and include a prime represented by each of the remaining forms. Case (2): `[O':O]=\ell`: so `d=\ell^2d;`. We include all prime divisors of `d`. Case (3): `[O:O']=\ell`: we may assume that `\ell` does not divide `d` as we have already included these, so `\ell` either splits or is inert in `O`; the class numbers satisfy `h(O')=(\ell\pm1)h(O)` accordingly. We include all primes `\ell` such that `\ell\pm1` divides the degree `[K:\QQ]`. For curves with only potential CM we proceed as in the CM case, using `2[K:\QQ]` instead of `[K:\QQ]`. EXAMPLES: For curves without CM we determine the primes at which the mod `p` Galois representation is reducible, i.e. contained in a Borel subgroup:: sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees sage: E = EllipticCurve('11a1') sage: possible_isogeny_degrees(E) [5] We check that in this case `E` really does have rational `5`-isogenies:: sage: [phi.degree() for phi in E.isogenies_prime_degree()] [5, 5] Over an extension field:: sage: E3 = E.change_ring(CyclotomicField(3)) sage: possible_isogeny_degrees(E3) [5] sage: [phi.degree() for phi in E3.isogenies_prime_degree()] [5, 5] For curves with CM by a quadratic order of class number greater than `1`, we use the structure of the class group to only give one prime in each ideal class:: sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1]) sage: L.<a> = NumberField(pol) sage: j = hilbert_class_polynomial(-23).roots(L,multiplicities=False)[0] sage: E = EllipticCurve(j=j) sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees sage: possible_isogeny_degrees(E, verbose=True) CM case, discriminant = -23 initial primes: {2} upward primes: {} downward ramified primes: {} downward split primes: {2, 3} downward inert primes: {5} primes generating the class group: [2] Complete set of primes: {2, 3, 5} [2, 3, 5] """ if E.has_cm(): d = E.cm_discriminant() if verbose: print("CM case, discriminant = %s" % d) from sage.libs.pari.all import pari from sage.sets.all import Set from sage.arith.all import kronecker_symbol n = E.base_field().absolute_degree() if not E.has_rational_cm(): n *= 2 divs = n.divisors() data = pari(d).quadclassunit() # This has 4 components: the class number, class group # structure (ignored), class group generators (as quadratic # forms) and regulator (=1 since d<0, ignored). h = data[0].sage() # We must have 2*h dividing n, and will need the quotient so # see if the j-invariants of any proper sub-orders could lie # in the same field n_over_2h = n//(2*h) # Collect possible primes. First put in 2, and also 3 for # discriminant -3 (special case because of units): L = Set([ZZ(2), ZZ(3)]) if d==-3 else Set([ZZ(2)]) if verbose: print("initial primes: %s" % L) # Step 1: "vertical" primes l such that the isogenous curve # has CM by an order whose index is l or 1/l times the index # of the order O of discriminant d. The latter case can only # happen when l^2 divides d. # Compute the ramified primes ram_l = d.odd_part().prime_factors() # if the CM is not rational we include all ramified primes, # which is simpler than using the class group later: if not E.has_rational_cm(): L1 = Set(ram_l) L += L1 if verbose: print("ramified primes: %s" % L1) else: # Find the "upward" primes (index divided by l): L1 = Set([l for l in ram_l if d.valuation(l)>1]) L += L1 if verbose: print("upward primes: %s" % L1) # Find the "downward" primes (index multiplied by l, class # number multiplied by l-kronecker_symbol(d,l)): # (a) ramified primes; the suborder has class number l*h, so l # must divide n/2h: L1 = Set([l for l in ram_l if l.divides(n_over_2h)]) L += L1 if verbose: print("downward ramified primes: %s" % L1) # (b) split primes; the suborder has class number (l-1)*h, so # l-1 must divide n/2h: L1 = Set([lm1+1 for lm1 in divs if (lm1+1).is_prime() and kronecker_symbol(d,lm1+1)==+1]) L += L1 if verbose: print("downward split primes: %s" % L1) # (c) inert primes; the suborder has class number (l+1)*h, so # l+1 must divide n/2h: L1 = Set([lp1-1 for lp1 in divs if (lp1-1).is_prime() and kronecker_symbol(d,lp1-1)==-1]) L += L1 if verbose: print("downward inert primes: %s" % L1) # Now find primes represented by each form of discriminant d. # In the rational CM case, we use all forms associated to # generators of the class group, otherwise only forms of order # 2: if E.has_rational_cm(): from sage.quadratic_forms.binary_qf import BinaryQF Qs = [BinaryQF(list(q)) for q in data[2]] L1 = [Q.small_prime_value() for Q in Qs] if verbose: print("primes generating the class group: %s" % L1) L += Set(L1) # Return sorted list if verbose: print("Complete set of primes: %s" % L) return sorted(list(L)) # Non-CM case if verbose: print("Non-CM case, using Galois representation") return E.galois_representation().reducible_primes()