Beispiel #1
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 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 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)
Beispiel #4
0
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:

    - [Iwasawa]_, 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
Beispiel #5
0
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:

    - [Iwasawa]_, 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 H2(i, j, p):
    """
    Returns the i,j-th entry of the Paley matrix, type II case.

    The Paley type II case corresponds to the case `p \cong 1 \mod{4}`
    for a prime `p` (see [Hora]_).

    .. TODO::

        This construction holds more generally for prime powers `q`
        congruent to `1 \mod{4}`. We should implement these but we
        first need to implement Quadratic character for `GF(q)`.

    EXAMPLES::

        sage: sage.combinat.matrices.hadamard_matrix.H2(1,2,5)
        1
    """
    if i == 0 and j == 0:
        return 0
    if i == 0 or j == 0:
        return 1
    if i == j:
        return 0
    return kronecker_symbol(i - j, p)
Beispiel #7
0
def QuadraticBernoulliNumber(k, d):
    r"""
    Compute k-th Bernoulli number for the primitive
    quadratic character associated to `\chi(x) = \left(\frac{d}{x}\right)`.

    Reference: Iwasawa's "Lectures on p-adic L-functions", pp7-16.

    EXAMPLES::

        sage: ## Makes a set of odd fund discriminants < -3
        sage: Fund_odd_test_set = [D  for D in range(-163, -3, 4)  if is_fundamental_discriminant(D)]

        sage: ## In general, we have B_{1, \chi_d} = -2h/w  for odd fund disc < 0
        sage: for D in Fund_odd_test_set:
        ...      if len(BinaryQF_reduced_representatives(D)) != -QuadraticBernoulliNumber(1, D):
        ...          print "Oops!  There is an error at D = ", D
    """
    ## 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
Beispiel #8
0
def QuadraticBernoulliNumber(k, d):
    r"""
    Compute k-th Bernoulli number for the primitive
    quadratic character associated to `\chi(x) = \left(\frac{d}{x}\right)`.

    Reference: Iwasawa's "Lectures on p-adic L-functions", pp7-16.

    EXAMPLES::

        sage: ## Makes a set of odd fund discriminants < -3
        sage: Fund_odd_test_set = [D  for D in range(-163, -3, 4)  if is_fundamental_discriminant(D)]  

        sage: ## In general, we have B_{1, \chi_d} = -2h/w  for odd fund disc < 0
        sage: for D in Fund_odd_test_set: 
        ...      if len(BinaryQF_reduced_representatives(D)) != -QuadraticBernoulliNumber(1, D): 
        ...          print "Oops!  There is an error at D = ", D 
    """
    ## 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
Beispiel #9
0
def H2(i, j, p):
    """
    Returns the i,j-th entry of the Paley matrix, type II case.

    The Paley type II case corresponds to the case `p \cong 1 \mod{4}`
    for a prime `p` (see [Hora]_).

    .. TODO::

        This construction holds more generally for prime powers `q`
        congruent to `1 \mod{4}`. We should implement these but we
        first need to implement Quadratic character for `GF(q)`.

    EXAMPLES::

        sage: sage.combinat.matrices.hadamard_matrix.H2(1,2,5)
        1
    """
    if i == 0 and j == 0:
        return 0
    if i == 0 or j == 0:
        return 1
    if i == j:
        return 0
    return kronecker_symbol(i - j, p)
Beispiel #10
0
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

    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:

    - [Iwasawa]_, pp 16-17, Special values of `L(1-n, \chi)` and `L(n, \chi)`
    - [IreRos]_
    - [WashCyc]_
    """
    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 *= 1 / (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)")
Beispiel #11
0
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

    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:

    - [Iwasawa]_, pp 16-17, Special values of `L(1-n, \chi)` and `L(n, \chi)`
    - [IreRos]_
    - [WashCyc]_
    """
    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 *= 1 / (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)")
Beispiel #12
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
Beispiel #13
0
def H1(i,j,p):
    """
    Returns the i,j-th entry of the Paley matrix, type I case.

    EXAMPLES::
        sage: sage.combinat.matrices.hadamard_matrix.H1(1,2,3)
        -1
    """
    if i==0:
        return 1
    if j==0:
        return -1
    if i==j:
        return 1
    return kronecker_symbol(i-j,p)
Beispiel #14
0
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(" + str(1+2*i) + ", (-4/.)): ", 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 = ", d, "    exact = ", RR(quadratic_L_function__exact(1, d)), "    numerical = ", 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(0)
    for i in range(1,num_terms):
        ans += R(kronecker_symbol(d1,i) / R(i)**n)
    return ans
Beispiel #15
0
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)`.

    References:

    - Iwasawa's "Lectures on p-adic L-functions", p16-17, "Special values of
      `L(1-n, \chi)` and `L(n, \chi)`
    - Ireland and Rosen's "A Classical Introduction to Modern Number Theory"
    - Washington's "Cyclotomic Fields"

    EXAMPLES::

        sage: bool(quadratic_L_function__exact(1, -4) == pi/4)
        True

    """
    from sage.all import SR, sqrt
    if n <= 0:
        k = 1 - n
        return -QuadraticBernoulliNumber(k, d) / k
    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 *= 1 / (2 * I**delta)
            ans *= QuadraticBernoulliNumber(n, d) / factorial(n)
            return ans
        else:
            if delta == 0:
                raise TypeError, "n must be a critical value!\n" + "(I.e. even > 0 or odd < 0.)"
            if delta == 1:
                raise TypeError, "n must be a critical value!\n" + "(I.e. odd > 0 or even <= 0.)"
Beispiel #16
0
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(" + str(1+2*i) + ", (-4/.)): ", 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 = ", d, "    exact = ", RR(quadratic_L_function__exact(1, d)), "    numerical = ", 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(0)
    for i in range(1, num_terms):
        ans += R(kronecker_symbol(d1, i) / R(i)**n)
    return ans
Beispiel #17
0
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)`.  

    References:

    - Iwasawa's "Lectures on p-adic L-functions", p16-17, "Special values of
      `L(1-n, \chi)` and `L(n, \chi)`
    - Ireland and Rosen's "A Classical Introduction to Modern Number Theory"
    - Washington's "Cyclotomic Fields"

    EXAMPLES::

        sage: bool(quadratic_L_function__exact(1, -4) == pi/4)
        True

    """
    from sage.all import SR, sqrt
    if n<=0:
        k = 1-n
        return -QuadraticBernoulliNumber(k,d)/k
    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 *= 1/(2 * I**delta)
            ans *= QuadraticBernoulliNumber(n,d)/factorial(n)
            return ans
        else:
            if delta == 0:
                raise TypeError, "n must be a critical value!\n" + "(I.e. even > 0 or odd < 0.)"
            if delta == 1:
                raise TypeError, "n must be a critical value!\n" + "(I.e. odd > 0 or even <= 0.)"
Beispiel #18
0
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::

        sage:  ## Test several values for a given character
        sage: RR = RealField(100)
        sage: for i in range(5):
        ...       print "L(" + str(1+2*i) + ", (-4/.)): ", 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

        sage: ## Testing the accuracy of the negative special values
        sage: ## ---- THIS FAILS SINCE THE DIRICHLET SERIES DOESN'T CONVERGE HERE! ----

        sage: ## Test several characters agree with the exact value, to a given accuracy.
        sage: for d in range(-20,0):
        ...       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 = ", d, "    exact = ", RR(quadratic_L_function__exact(1, d)), "    numerical = ", RR(quadratic_L_function__numerical(1, d))  
        ...           

    """
    ## Set the correct precision if it's given (for n).
    if is_RealField(n.parent()):
        R = n.parent()
    else:
        R = RealField()

    d1 = fundamental_discriminant(d)
    ans = R(0)
    for i in range(1,num_terms):
        ans += R(kronecker_symbol(d1,i) / R(i)**n)
    return ans
Beispiel #19
0
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::

        sage:  ## Test several values for a given character
        sage: RR = RealField(100)
        sage: for i in range(5):
        ...       print "L(" + str(1+2*i) + ", (-4/.)): ", 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

        sage: ## Testing the accuracy of the negative special values
        sage: ## ---- THIS FAILS SINCE THE DIRICHLET SERIES DOESN'T CONVERGE HERE! ----

        sage: ## Test several characters agree with the exact value, to a given accuracy.
        sage: for d in range(-20,0):
        ...       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 = ", d, "    exact = ", RR(quadratic_L_function__exact(1, d)), "    numerical = ", RR(quadratic_L_function__numerical(1, d))
        ...

    """
    ## Set the correct precision if it's given (for n).
    if is_RealField(n.parent()):
        R = n.parent()
    else:
        R = RealField()

    d1 = fundamental_discriminant(d)
    ans = R(0)
    for i in range(1, num_terms):
        ans += R(kronecker_symbol(d1, i) / R(i)**n)
    return ans
Beispiel #20
0
def H1(i, j, p):
    """
    Returns the i,j-th entry of the Paley matrix, type I case.
    The Paley type I case corresponds to the case `p \cong 3 \mod{4}`
    for a prime `p`.

    .. TODO::

        This construction holds more generally for prime powers `q`
        congruent to `3 \mod{4}`. We should implement these but we
        first need to implement Quadratic character for `GF(q)`.

    EXAMPLES::

        sage: sage.combinat.matrices.hadamard_matrix.H1(1,2,3)
        1
    """
    if i == 0 or j == 0:
        return 1
    # what follows will not be executed for (i, j) = (0, 0).
    if i == j:
        return -1
    return -kronecker_symbol(i - j, p)
Beispiel #21
0
def H1(i, j, p):
    """
    Returns the i,j-th entry of the Paley matrix, type I case.
    The Paley type I case corresponds to the case `p \cong 3 \mod{4}`
    for a prime `p`.

    .. TODO::

        This construction holds more generally for prime powers `q`
        congruent to `3 \mod{4}`. We should implement these but we
        first need to implement Quadratic character for `GF(q)`.

    EXAMPLES::

        sage: sage.combinat.matrices.hadamard_matrix.H1(1,2,3)
        1
    """
    if i == 0 or j == 0:
        return 1
    # what follows will not be executed for (i, j) = (0, 0).
    if i == j:
        return -1
    return -kronecker_symbol(i - j, p)
Beispiel #22
0
    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()
Beispiel #23
0
    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, Ser I. Math, 317 (1993), no 3,
           227-232.

        .. [SW] William Stein and Christian Wuthrich, Computations
           About Tate-Shafarevich Groups using Iwasawa theory,
           preprint 2009.

        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^2)
            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)
            Traceback (most recent call last):                           # 32-bit (see ticket :trac: `112111`)
            ...                                                          # 32-bit
            OverflowError: Python int too large to convert to C long     # 32-bit
            7^2 + O(7^6) # 64-bit
            sage: EllipticCurve('300b2').sha().an_padic(3)   # 9 elements in sha, long time (2s on sage.math, 2011)
            3^2 + O(3^6)
            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^2)
            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^2)

        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^2)
            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)
        """
        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
        reg = E.padic_regulator(p)
        # todo : here we should cache the rank computation
        r = E.rank()


        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 = D/p
            for ell in D.prime_divisors():
                if ell % 2 == 1:
                    if Et.conductor() % ell**2 == 0:
                        D = D/ell
            ve = valuation(D,2)
            de = (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)
            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.")

            # 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 _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)])
            ...           etaa = lp._quotient_of_periods_to_twist(D)
            ...           assert ED.lseries().L_ratio()*ED.real_components()*etaa == 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
Beispiel #25
0
    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)])
            ...           etaa = lp._quotient_of_periods_to_twist(D)
            ...           assert ED.lseries().L_ratio()*ED.real_components()*etaa == 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.__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.__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 (41s on sage.math, 2011)
        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 (41s on sage.math, 2011)
        3/2

    TESTS::

        sage: [1] + [Q.siegel_product(ZZ(a))  for a in range(1,11)] == Q.theta_series(11).list()
        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 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 (41s on sage.math, 2011)
        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 (41s on sage.math, 2011)
        3/2

    TESTS::

        sage: [1] + [Q.siegel_product(ZZ(a))  for a in range(1,11)] == Q.theta_series(11).list()
        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)
Beispiel #28
0
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.rings.arith 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()
Beispiel #29
0
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.rings.arith 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()