Exemple #1
0
    def upper_bound_on_elliptic_factors(self, p=None, ellmax=2):
        r"""
        Return an upper bound (provably correct) on the number of
        elliptic curves of conductor equal to the level of this
        supersingular module.

        INPUT:

        - ``p`` -- (default: 997) prime to work modulo

        ALGORITHM: Currently we only use `T_2`.  Function will be
        extended to use more Hecke operators later.

        The prime p is replaced by the smallest prime that does not
        divide the level.

        EXAMPLES::

            sage: SupersingularModule(37).upper_bound_on_elliptic_factors()
            2

        (There are 4 elliptic curves of conductor 37, but only 2 isogeny
        classes.)
        """
        from sage.misc.verbose import verbose

        # NOTE: The heuristic runtime is *very* roughly `p^2/(2\cdot 10^6)`.
        # ellmax -- (default: 2) use Hecke operators T_ell with ell <= ellmax
        if p is None:
            p = 997

        while self.level() % p == 0:
            p = next_prime(p)

        ell = 2
        t = self.hecke_matrix(ell).change_ring(FiniteField(p))

        # TODO: temporarily try using sparse=False
        # turn this off when sparse rank is optimized.
        t = t.dense_matrix()

        B = ZZ(4 * ell).isqrt()
        bnd = 0
        lower = -B
        upper = B + 1
        for a in range(lower, upper):
            tm = verbose("computing T_%s - %s" % (ell, a))
            if a == lower:
                c = a
            else:
                c = 1
            for i in range(t.nrows()):
                t[i, i] += c
            tm = verbose("computing kernel", tm)
            # dim = t.kernel().dimension()
            dim = t.nullity()
            bnd += dim
            verbose('got dimension = %s; new bound = %s' % (dim, bnd), tm)
        return bnd
Exemple #2
0
    def __init__(self, n, instance='key', m=None):
        """
        Construct LWE instance parameterised by security parameter ``n`` where
        all other parameters are chosen as in [CGW2013]_.

        INPUT:

        - ``n`` - security parameter (integer >= 89)
        - ``instance`` - one of

          - "key" - the LWE-instance that hides the secret key is generated
          - "encrypt" - the LWE-instance that hides the message is generated
            (default: ``key``)

        - ``m`` - number of allowed samples or ``None`` in which case ``m`` is
          chosen as in [CGW2013]_.  (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import UniformNoiseLWE
            sage: UniformNoiseLWE(89)
            LWE(89, 154262477, UniformSampler(0, 351), 'noise', 131)

            sage: UniformNoiseLWE(89, instance='encrypt')
            LWE(131, 154262477, UniformSampler(0, 497), 'noise', 181)
        """

        if n < 89:
            raise TypeError("Parameter too small")

        n2 = n
        C = 4 / sqrt(2 * pi)
        kk = floor((n2 - 2 * log(n2, 2)**2) / 5)
        n1 = floor((3 * n2 - 5 * kk) / 2)
        ke = floor((n1 - 2 * log(n1, 2)**2) / 5)
        l = floor((3 * n1 - 5 * ke) / 2) - n2
        sk = ceil((C * (n1 + n2))**(3 / 2))
        se = ceil((C * (n1 + n2 + l))**(3 / 2))
        q = next_prime(
            max(ceil((4 * sk)**((n1 + n2) / n1)),
                ceil((4 * se)**((n1 + n2 + l) / (n2 + l))),
                ceil(4 * (n1 + n2) * se * sk + 4 * se + 1)))

        if kk <= 0:
            raise TypeError("Parameter too small")

        if instance == 'key':
            D = UniformSampler(0, sk - 1)
            if m is None:
                m = n1
            LWE.__init__(self, n=n2, q=q, D=D, secret_dist='noise', m=m)
        elif instance == 'encrypt':
            D = UniformSampler(0, se - 1)
            if m is None:
                m = n2 + l
            LWE.__init__(self, n=n1, q=q, D=D, secret_dist='noise', m=m)
        else:
            raise TypeError("Parameter instance=%s not understood." %
                            (instance))
Exemple #3
0
    def upper_bound_on_elliptic_factors(self, p=None, ellmax=2):
        r"""
        Return an upper bound (provably correct) on the number of
        elliptic curves of conductor equal to the level of this
        supersingular module.

        INPUT:

        - ``p`` - (default: 997) prime to work modulo

        ALGORITHM: Currently we only use `T_2`.  Function will be
        extended to use more Hecke operators later.

        The prime p is replaced by the smallest prime that doesn't
        divide the level.

        EXAMPLES::

            sage: SupersingularModule(37).upper_bound_on_elliptic_factors()
            2

        (There are 4 elliptic curves of conductor 37, but only 2 isogeny
        classes.)
        """
        # NOTE: The heuristic runtime is *very* roughly `p^2/(2\cdot 10^6)`.
        #ellmax -- (default: 2) use Hecke operators T_ell with ell <= ellmax
        if p is None:
            p = 997

        while self.level() % p == 0:
             p = next_prime(p)

        ell = 2
        t = self.hecke_matrix(ell).change_ring(rings.GF(p))

        # TODO: temporarily try using sparse=False
        # turn this off when sparse rank is optimized.
        t = t.dense_matrix()

        B = 2*math.sqrt(ell)
        bnd = 0
        lower = -int(math.floor(B))
        upper = int(math.floor(B))+1
        for a in range(lower, upper):
            tm = verbose("computing T_%s - %s"%(ell, a))
            if a == lower:
                c = a
            else:
                c = 1
            for i in range(t.nrows()):
                t[i,i] += c
            tm = verbose("computing kernel",tm)
            #dim = t.kernel().dimension()
            dim = t.nrows() - t.rank()
            bnd += dim
            verbose('got dimension = %s; new bound = %s'%(dim, bnd), tm)
        return bnd
Exemple #4
0
    def good_primes(B):
        r"""
        Given the bound, returns the primes whose product is greater than ``B``
        and which would take the least amount of time to run the main sieve algorithm

        Complexity of finding points modulo primes is assumed to be N^2 * P_max^{N}.
        Complexity of lifting points and the LLL() function is assumed to
        be close to (dim_max^5) * (alpha / P_max)^dim_scheme.
        where alpha is the product of all primes, P_max is the largest prime in
        the list, dim_max is the max dimension of all components, and N is the dimension
        of the ambient space.
        """

        M = dict()  # stores optimal list of primes, corresponding to list size
        small_primes = sufficient_primes(B)
        max_length = len(small_primes)
        M[max_length] = small_primes
        current_count = max_length - 1
        dim = X.ambient_space().dimension()

        while current_count > 1:
            current_list = []  # stores prime which are bigger than least
            updated_list = []
            best_list = []

            least = (RR(B)**(1.00 / current_count)).floor()
            for i in range(current_count):
                current_list.append(next_prime(least))
                least = current_list[-1]
            # improving list of primes by taking primes less than least
            # this part of algorithm is used to centralize primes around `least`
            prod_prime = prod(current_list)
            least = current_list[0]
            while least != 2 and prod_prime > B and len(
                    updated_list) < current_count:
                best_list = updated_list + current_list[:current_count -
                                                        len(updated_list)]
                updated_list.append(previous_prime(least))
                least = updated_list[-1]

                removed_prime = current_list[current_count - len(updated_list)]
                prod_prime = (prod_prime * least) / removed_prime

            M[current_count] = sorted(best_list)
            current_count = current_count - 1

        best_size = 2
        best_time = (dim**2) * M[2][-1]**(dim) + (
            dim_max**5 * (prod(M[2]) / M[2][-1])**dim_scheme)
        for i in range(2, max_length + 1):
            current_time = (dim**2) * M[i][-1]**(dim) + (
                dim_max**5 * (prod(M[i]) / M[i][-1])**dim_scheme)
            if current_time < best_time:
                best_size = i
                best_time = current_time

        return M[best_size]
Exemple #5
0
def next_prime_not_dividing(P, I):
    while True:
        p = P.smallest_integer()
        if p == 1:
            Q = F.ideal(2)
        elif p % 5 in [2, 3]:  # inert
            Q = F.primes_above(next_prime(p))[0]
        elif p == 5:
            Q = F.ideal(7)
        else:  # p split
            A = F.primes_above(p)
            if A[0] == P:
                Q = A[1]
            else:
                Q = F.primes_above(next_prime(p))[0]
        if not Q.divides(I):
            return Q
        else:
            P = Q  # try again
Exemple #6
0
def next_prime_not_dividing(P, I):
    while True:
        p = P.smallest_integer()
        if p == 1:
            Q = F.ideal(2)
        elif p % 5 in [2,3]: # inert
            Q = F.primes_above(next_prime(p))[0]
        elif p == 5:
            Q = F.ideal(7)
        else: # p split
            A = F.primes_above(p)
            if A[0] == P:
                Q = A[1]
            else:
                Q = F.primes_above(next_prime(p))[0]
        if not Q.divides(I):
            return Q
        else:
            P = Q # try again
Exemple #7
0
    def __init__(self, n, instance='key', m=None):
        """
        Construct LWE instance parameterised by security parameter ``n`` where
        all other parameters are chosen as in [CGW2013]_.

        INPUT:

        - ``n`` - security parameter (integer >= 89)
        - ``instance`` - one of

          - "key" - the LWE-instance that hides the secret key is generated
          - "encrypt" - the LWE-instance that hides the message is generated
            (default: ``key``)

        - ``m`` - number of allowed samples or ``None`` in which case ``m`` is
          chosen as in [CGW2013]_.  (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import UniformNoiseLWE
            sage: UniformNoiseLWE(89)
            LWE(89, 154262477, UniformSampler(0, 351), 'noise', 131)

            sage: UniformNoiseLWE(89, instance='encrypt')
            LWE(131, 154262477, UniformSampler(0, 497), 'noise', 181)
        """

        if n<89:
            raise TypeError("Parameter too small")

        n2 = n
        C  = 4/sqrt(2*pi)
        kk = floor((n2-2*log(n2, 2)**2)/5)
        n1 = floor((3*n2-5*kk)/2)
        ke = floor((n1-2*log(n1, 2)**2)/5)
        l  = floor((3*n1-5*ke)/2)-n2
        sk = ceil((C*(n1+n2))**(3/2))
        se = ceil((C*(n1+n2+l))**(3/2))
        q = next_prime(max(ceil((4*sk)**((n1+n2)/n1)), ceil((4*se)**((n1+n2+l)/(n2+l))), ceil(4*(n1+n2)*se*sk+4*se+1)))

        if kk<=0:
            raise TypeError("Parameter too small")

        if instance == 'key':
            D  = UniformSampler(0, sk-1)
            if m is None:
                m = n1
            LWE.__init__(self, n=n2, q=q, D=D, secret_dist='noise', m=m)
        elif instance == 'encrypt':
            D   = UniformSampler(0, se-1)
            if m is None:
                m = n2+l
            LWE.__init__(self, n=n1, q=q, D=D, secret_dist='noise', m=m)
        else:
            raise TypeError("Parameter instance=%s not understood."%(instance))
Exemple #8
0
    def sufficient_primes(x):
        r"""
        Returns a list of primes whose product is > `x`
        """
        small_primes = [2, 3]
        prod_primes = 6

        while prod_primes < x:
            p = next_prime(small_primes[-1])
            small_primes.append(p)
            prod_primes *= p
        return small_primes
Exemple #9
0
def modular_symbol_space(E, sign, base_ring, bound=None):
    r"""
    Creates the space of modular symbols of a given sign over a give base_ring,
    attached to the isogeny class of elliptic curves.

    INPUT:

    - ``E`` - an elliptic curve over `\QQ`
    - ``sign`` - integer, -1, 0, or 1
    - ``base_ring`` - ring
    - ``bound`` - (default: None) maximum number of Hecke operators to
      use to cut out modular symbols factor.  If None, use
      enough to provably get the correct answer.

    OUTPUT: a space of modular symbols

    EXAMPLES::

        sage: import sage.schemes.elliptic_curves.ell_modular_symbols
        sage: E=EllipticCurve('11a1')
        sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.modular_symbol_space(E,-1,GF(37))
        sage: M
        Modular Symbols space of dimension 1 for Gamma_0(11) of weight 2 with sign -1 over Finite Field of size 37

    """
    _sign = int(sign)
    if _sign != sign:
        raise TypeError('sign must be an integer')
    if not (_sign in [-1,0,1]):
        raise TypeError('sign must -1, 0, or 1')
    N = E.conductor()
    M = ModularSymbols(N, sign=sign, base_ring=base_ring)
    if bound is None:
        bound = M.hecke_bound() + 10
    V = M
    p = 2
    while p <= bound and V.dimension() > 1:
        t = V.T(p)
        ap = E.ap(p)
        V = (t - ap).kernel()
        p = next_prime(p)

    return V
Exemple #10
0
def modular_symbol_space(E, sign, base_ring, bound=None):
    r"""
    Creates the space of modular symbols of a given sign over a give base_ring,
    attached to the isogeny class of elliptic curves.

    INPUT:

     - ``E`` - an elliptic curve over `\QQ`
     - ``sign`` - integer, -1, 0, or 1
     - ``base_ring`` - ring
     - ``bound`` - (default: None) maximum number of Hecke operators to
       use to cut out modular symbols factor.  If None, use
       enough to provably get the correct answer.

    OUTPUT: a space of modular symbols

    EXAMPLES::

        sage: import sage.schemes.elliptic_curves.ell_modular_symbols
        sage: E=EllipticCurve('11a1')
        sage: M=sage.schemes.elliptic_curves.ell_modular_symbols.modular_symbol_space(E,-1,GF(37))
        sage: M
        Modular Symbols space of dimension 1 for Gamma_0(11) of weight 2 with sign -1 over Finite Field of size 37

    """
    _sign = int(sign)
    if _sign != sign:
        raise TypeError('sign must be an integer')
    if not (_sign in [-1, 0, 1]):
        raise TypeError('sign must -1, 0, or 1')
    N = E.conductor()
    M = ModularSymbols(N, sign=sign, base_ring=base_ring)
    if bound is None:
        bound = M.hecke_bound() + 10
    V = M
    p = 2
    while p <= bound and V.dimension() > 1:
        t = V.T(p)
        ap = E.ap(p)
        V = (t - ap).kernel()
        p = next_prime(p)

    return V
Exemple #11
0
    def __init__(self, N, delta=0.01, m=None):
        """
        Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where
        the modulus ``q`` and the ``stddev`` of the noise is chosen as in
        [LP2011]_.

        INPUT:

        - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2)
        - ``delta`` - error probability per symbol (default: 0.01)
        - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is
          used (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import RingLindnerPeikert
            sage: RingLindnerPeikert(N=16)
            RingLWE(16, 1031, Discrete Gaussian sampler for polynomials of degree < 8 with σ=2.803372 in each component, x^8 + 1, 'noise', 24)
        """
        n = euler_phi(N)
        if m is None:
            m = 3 * n
        # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40
        #  i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0
        c = SR.var('c')
        c = find_root(2 * n * log(c) + n * (1 - c**2) + 40 * log(2) == 0, 1,
                      10)
        # Upper bound on s**2/t
        s_t_bound = (sqrt(2) * pi / c / sqrt(2 * n * log(2 / delta))).n()
        # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_
        q = next_prime(floor(2**round(log(256 / s_t_bound, 2))))
        # Gaussian parameter as defined in [LP2011]_
        s = sqrt(s_t_bound * floor(q / 4))
        # Transform s into stddev
        stddev = s / sqrt(2 * pi.n())
        D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, stddev)
        RingLWE.__init__(self,
                         N=N,
                         q=q,
                         D=D,
                         poly=None,
                         secret_dist='noise',
                         m=m)
Exemple #12
0
    def __init__(self, n, delta=0.01, m=None):
        """
        Construct LWE instance parameterised by security parameter ``n`` where
        the modulus ``q`` and the ``stddev`` of the noise is chosen as in
        [LP2011]_.

        INPUT:

        - ``n`` - security parameter (integer > 0)
        - ``delta`` - error probability per symbol (default: 0.01)
        - ``m`` - number of allowed samples or ``None`` in which case ``m=2*n +
          128`` as in [LP2011]_ (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import LindnerPeikert
            sage: LindnerPeikert(n=20)
            LWE(20, 2053, Discrete Gaussian sampler over the Integers with sigma = 3.600954 and c = 0, 'noise', 168)
        """
        if m is None:
            m = 2 * n + 128
        # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40
        #         (c*exp((1-c**2)/2))**(2*n) == 2**-40
        #    log((c*exp((1-c**2)/2))**(2*n)) == -40*log(2)
        #       (2*n)*log(c*exp((1-c**2)/2)) == -40*log(2)
        #  2*n*(log(c)+log(exp((1-c**2)/2))) == -40*log(2)
        #            2*n*(log(c)+(1-c**2)/2) == -40*log(2)
        #              2*n*log(c)+n*(1-c**2) == -40*log(2)
        #  2*n*log(c)+n*(1-c**2) + 40*log(2) == 0
        c = SR.var('c')
        c = find_root(2 * n * log(c) + n * (1 - c**2) + 40 * log(2) == 0, 1,
                      10)
        # Upper bound on s**2/t
        s_t_bound = (sqrt(2) * pi / c / sqrt(2 * n * log(2 / delta))).n()
        # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_
        q = next_prime(floor(2**round(log(256 / s_t_bound, 2))))
        # Gaussian parameter as defined in [LP2011]_
        s = sqrt(s_t_bound * floor(q / 4))
        # Transform s into stddev
        stddev = s / sqrt(2 * pi.n())
        D = DiscreteGaussianDistributionIntegerSampler(stddev)
        LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
Exemple #13
0
    def __init__(self, n, delta=0.01, m=None):
        """
        Construct LWE instance parameterised by security parameter ``n`` where
        the modulus ``q`` and the ``stddev`` of the noise is chosen as in
        [LP2011]_.

        INPUT:

        - ``n`` - security parameter (integer > 0)
        - ``delta`` - error probability per symbol (default: 0.01)
        - ``m`` - number of allowed samples or ``None`` in which case ``m=2*n +
          128`` as in [LP2011]_ (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import LindnerPeikert
            sage: LindnerPeikert(n=20)
            LWE(20, 2053, Discrete Gaussian sampler over the Integers with sigma = 3.600954 and c = 0, 'noise', 168)
        """
        if m is None:
            m = 2*n + 128
        # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40
        #         (c*exp((1-c**2)/2))**(2*n) == 2**-40
        #    log((c*exp((1-c**2)/2))**(2*n)) == -40*log(2)
        #       (2*n)*log(c*exp((1-c**2)/2)) == -40*log(2)
        #  2*n*(log(c)+log(exp((1-c**2)/2))) == -40*log(2)
        #            2*n*(log(c)+(1-c**2)/2) == -40*log(2)
        #              2*n*log(c)+n*(1-c**2) == -40*log(2)
        #  2*n*log(c)+n*(1-c**2) + 40*log(2) == 0
        c = SR.var('c')
        c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10)
        # Upper bound on s**2/t
        s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n()
        # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_
        q = next_prime(floor(2**round(log(256 / s_t_bound, 2))))
        # Gaussian parameter as defined in [LP2011]_
        s = sqrt(s_t_bound*floor(q/4))
        # Transform s into stddev
        stddev = s/sqrt(2*pi.n())
        D   = DiscreteGaussianDistributionIntegerSampler(stddev)
        LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
Exemple #14
0
    def __init__(self, N, delta=0.01, m=None):
        """
        Construct a Ring-LWE oracle in dimension ``n=phi(N)`` where
        the modulus ``q`` and the ``stddev`` of the noise is chosen as in
        [LP2011]_.

        INPUT:

        - ``N`` - index of cyclotomic polynomial (integer > 0, must be power of 2)
        - ``delta`` - error probability per symbol (default: 0.01)
        - ``m`` - number of allowed samples or ``None`` in which case ``3*n`` is
          used (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import RingLindnerPeikert
            sage: RingLindnerPeikert(N=16)
            RingLWE(16, 1031, Discrete Gaussian sampler for polynomials of degree < 8 with σ=2.803372 in each component, x^8 + 1, 'noise', 24)
        """
        n = euler_phi(N)
        if m is None:
            m = 3*n
        # Find c>=1 such that c*exp((1-c**2)/2))**(2*n) == 2**-40
        #  i.e c>=1 such that 2*n*log(c)+n*(1-c**2) + 40*log(2) == 0
        c = SR.var('c')
        c = find_root(2*n*log(c)+n*(1-c**2) + 40*log(2) == 0, 1, 10)
        # Upper bound on s**2/t
        s_t_bound = (sqrt(2) * pi / c / sqrt(2*n*log(2/delta))).n()
        # Interpretation of "choose q just large enough to allow for a Gaussian parameter s>=8" in [LP2011]_
        q = next_prime(floor(2**round(log(256 / s_t_bound, 2))))
        # Gaussian parameter as defined in [LP2011]_
        s = sqrt(s_t_bound*floor(q/4))
        # Transform s into stddev
        stddev = s/sqrt(2*pi.n())
        D = DiscreteGaussianDistributionPolynomialSampler(ZZ['x'], n, stddev)
        RingLWE.__init__(self, N=N, q=q, D=D, poly=None, secret_dist='noise', m=m)
Exemple #15
0
    def __init__(self, n, secret_dist='uniform', m=None):
        """
        Construct LWE instance parameterised by security parameter ``n`` where
        the modulus ``q`` and the ``stddev`` of the noise are chosen as in
        [Reg09]_.

        INPUT:

        - ``n`` - security parameter (integer > 0)
        - ``secret_dist`` - distribution of the secret. See documentation of :class:`LWE`
          for details (default='uniform')
        - ``m`` - number of allowed samples or ``None`` if no such limit exists
          (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import Regev
            sage: Regev(n=20)
            LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 1.915069 and c = 401, 'uniform', None)
        """
        q = ZZ(next_prime(n**2))
        s = RR(1/(RR(n).sqrt() * log(n, 2)**2) * q)
        D = DiscreteGaussianDistributionIntegerSampler(s/sqrt(2*pi.n()), q)
        LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m)
Exemple #16
0
    def __init__(self, n, secret_dist='uniform', m=None):
        """
        Construct LWE instance parameterised by security parameter ``n`` where
        the modulus ``q`` and the ``stddev`` of the noise are chosen as in
        [Reg09]_.

        INPUT:

        - ``n`` - security parameter (integer > 0)
        - ``secret_dist`` - distribution of the secret. See documentation of :class:`LWE`
          for details (default='uniform')
        - ``m`` - number of allowed samples or ``None`` if no such limit exists
          (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import Regev
            sage: Regev(n=20)
            LWE(20, 401, Discrete Gaussian sampler over the Integers with sigma = 1.915069 and c = 401, 'uniform', None)
        """
        q = ZZ(next_prime(n**2))
        s = RR(1 / (RR(n).sqrt() * log(n, 2)**2) * q)
        D = DiscreteGaussianDistributionIntegerSampler(s / sqrt(2 * pi.n()), q)
        LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m)
Exemple #17
0
    def complement(self, bound=None):
        """
        Return the largest Hecke-stable complement of this space.

        EXAMPLES::

            sage: M = ModularSymbols(15, 6).cuspidal_subspace()
            sage: M.complement()
            Modular Symbols subspace of dimension 4 of Modular Symbols space of dimension 20 for Gamma_0(15) of weight 6 with sign 0 over Rational Field
            sage: E = EllipticCurve("128a")
            sage: ME = E.modular_symbol_space()
            sage: ME.complement()
            Modular Symbols subspace of dimension 17 of Modular Symbols space of dimension 18 for Gamma_0(128) of weight 2 with sign 1 over Rational Field
        """

        if self.dual_free_module.is_in_cache():
            D = self.dual_free_module()
            V = D.basis_matrix().right_kernel()
            return self.submodule(V, check=False)

        if self.is_ambient():
            return self.ambient_hecke_module().zero_submodule()

        if self.is_zero():
            return self.ambient_hecke_module()

        if self.is_full_hecke_module():
            anemic = False
        else:
            anemic = True

        # TODO: optimize in some cases by computing image of
        # complementary factor instead of kernel...?
        misc.verbose("computing")
        N = self.level()
        A = self.ambient_hecke_module()
        V = A.free_module()
        p = 2
        if bound is None:
            bound = A.hecke_bound()
        while True:
            if anemic:
                while N % p == 0: p = arith.next_prime(p)
            misc.verbose("using T_%s"%p)
            f = self.hecke_polynomial(p)
            T = A.hecke_matrix(p)
            g = T.charpoly('x')
            V = T.kernel_on(V, poly=g//f, check=False)
            if V.rank() + self.rank() <= A.rank():
                break
            p = arith.next_prime(p)
            if p > bound:  # to avoid computing hecke bound unless necessary
                break

        if V.rank() + self.rank() == A.rank():
            C = A.submodule(V, check=False)
            return C

        # first attempt to compute the complement failed, we now try
        # the following naive approach: decompose the ambient space,
        # decompose self, and sum the pieces of ambient that are not
        # subspaces of self
        misc.verbose("falling back on naive algorithm")
        D = A.decomposition()
        C = A.zero_submodule()
        for X in D:
            if self.intersection(X).dimension() == 0:
                C = C + X
        if C.rank() + self.rank() == A.rank():
            return C

        # failed miserably
        raise RuntimeError("Computation of complementary space failed (cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), A.rank()-self.rank()))
def _next_good_prime(p, R, qq, patience, qqold):

    """
    Find the next prime `\\ell` which is good by ``qq`` but not by ``qqold``, 1 mod ``p``, and for which
    ``b^2+4*c`` is a square mod `\\ell`, for the sequence ``R`` if it is possible in runtime patience.

    INPUT:

    - ``p`` -- a prime

    - ``R`` -- an object in the class ``BinaryRecurrenceSequence``

    - ``qq`` -- a perfect power

    - ``patience`` -- a real number

    - ``qqold`` --  a perfect power less than or equal to ``qq``

    OUTPUT:

    - A prime `\\ell` such that `\\ell` is 1 mod ``p``, ``b^2+4*c`` is a square mod `\\ell` and the period of `\\ell` has ``goodness`` by ``qq`` but not ``qqold``, if patience has not be surpased.  Otherwise ``False``.


    EXAMPLES::

        sage: R = BinaryRecurrenceSequence(1,1)
        sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,1,100,1)        #ran out of patience to search for good primes
        False
        sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,2,100,1)
        29
        sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,2,100,2)        #ran out of patience, as qqold == qq, so no primes work
        False

    """

    #We are looking for pth powers in R.
    #Our primes must be good by qq, but not qqold.
    #We only allow patience number of iterations to find a good prime.

    #The variable _ell for R keeps track of the last "good" prime returned
    #that was not found from the dictionary _PGoodness

    #First, we check to see if we have already computed the goodness of a prime that fits
    #our requirement of being good by qq but not by qqold.  This is stored in the _PGoodness
    #dictionary.

    #Then if we have, we return the smallest such prime and delete it from the list.  If not, we
    #search through patience number of primes R._ell to find one good by qq but not qqold.  If it is
    #not good by either qqold or qq, then we add this prime to R._PGoodness under its goodness.

    #Possible_Primes keeps track of possible primes satisfying our goodness requirements we might return
    Possible_Primes = []


    #check to see if anything in R._PGoodness fits our goodness requirements
    for j in R._PGoodness:
        if (qqold < j <= qq) and len(R._PGoodness[j]):
            Possible_Primes.append(R._PGoodness[j][0])

    #If we found good primes, we take the smallest
    if Possible_Primes != []:
        q = min(Possible_Primes)
        n = _goodness(q, R, p)
        del R._PGoodness[n][0]    #if we are going to use it, then we delete it from R._PGoodness
        return q

    #If nothing is already stored in R._PGoodness, we start (from where we left off at R._ell) checking
    #for good primes.  We only tolerate patience number of tries before giving up.
    else:
        i = 0
        while i < patience:
            i += 1
            R._ell = next_prime(R._ell)

            #we require that R._ell is 1 mod p, so that p divides the order of the multiplicative
            #group mod R._ell, so that not all elements of GF(R._ell) are pth powers.
            if R._ell % p == 1:

                #requiring that b^2 + 4c is a square in GF(R._ell) ensures that the period mod R._ell
                #divides R._ell - 1
                if legendre_symbol(R.b**2+4*R.c, R._ell) == 1:

                    N = _goodness(R._ell, R, p)

                    #proceed only if R._ell satisfies the goodness requirements
                    if qqold < N <= qq:
                        return R._ell

                    #if we do not use the prime, we store it in R._PGoodness
                    else:
                        if N in R._PGoodness:
                            R._PGoodness[N].append(R._ell)
                        else :
                            R._PGoodness[N] = [R._ell]

        return False
Exemple #19
0
def next_prime_of_characteristic_coprime_to(P, I):
    p = next_prime(P.smallest_integer())
    N = ZZ(I.norm())
    while N%p == 0:
        p = next_prime(p)
    return F.primes_above(p)[0]
Exemple #20
0
def next_prime_of_characteristic_coprime_to(P, I):
    p = next_prime(P.smallest_integer())
    N = ZZ(I.norm())
    while N % p == 0:
        p = next_prime(p)
    return F.primes_above(p)[0]
Exemple #21
0
def Lpvalue(f,g,h,p,prec,N = None,modformsring = False, weightbound = False, eps = None, orthogonal_form = None, data_idx=None, magma_args = None,force_computation=False, algorithm='threestage', derivative_order=1, lauders_advice = False, use_magma = True, magma = None, num_coeffs_qexpansion = 20000, max_primes=5, outfile = None):
    if magma_args is None:
        magma_args = {}
    if algorithm not in ['twostage','threestage']:
        raise ValueError('Algorithm should be one of "twostage" (default) or "threestage"')

    if magma is None:
        from sage.interfaces.magma import Magma
        magma = Magma(**magma_args)
    if hasattr(g,'j_invariant'):
        elliptic_curve = g
        g = g.modular_form()
    else:
        elliptic_curve = None
    data = None
    if h is None:
        if hasattr(f, 'modulus'):
            # Assume we need to create f and h from Dirichlet character
            kronecker_character = f
            f, _, h = define_qexpansions_from_dirichlet_character(p, prec, kronecker_character, num_coeffs_qexpansion, magma)
        else:
            kronecker_character = None
            # Assume that f contains a list of lines of text to initialize both f and h
            data = f
            f, h = get_magma_qexpansions(data, data_idx, max(prec,200), Qp(p,prec), magma=magma)
            eps = f.character_full()


    ll,mm = g.weight(),h.weight()
    t = 0 # Assume t = 0 here
    kk = ll + mm - 2 * (1 + t) # Is this correct?
    p = ZZ(p)
    if N is None:
        N = lcm([ZZ(f.level()),ZZ(g.level()),ZZ(h.level())])
        nu = N.valuation(p)
        N = N.prime_to_m_part(p)
    else:
        N = ZZ(N)
        nu = N.valuation(p)
    if outfile is None:
        outfile = "output_iterated_integral_%s_%s_%s_%s.txt"%(p,g.level(), h.level(), prec)
    print("Writing output to file %s"%outfile)
    fwrite("######### STARTING COMPUTATION OF Lp ###########", outfile)

    if elliptic_curve is not None:
        fwrite("E = EllipticCurve(%s)"%list(elliptic_curve.ainvs()), outfile)
        fwrite("  cond(E) = %s"%elliptic_curve.conductor(), outfile)
    if kronecker_character is not None:
        fwrite("kronecker_character = %s"%kronecker_character, outfile)
        fwrite("  conductor = %s"%kronecker_character.conductor(), outfile)
    if data is not None:
        fwrite("Data for weight-1 forms:", outfile)
        for line in data:
            fwrite(line, outfile)
    fwrite("Tame level N = %s, prime p = %s, nu = %s"%(N,p,nu), outfile)
    fwrite("precision = %s"%prec, outfile)
    fwrite("------ parameters --------------------", outfile)
    fwrite("modformsring = %s"%modformsring, outfile)
    fwrite("weightbound = %s"%weightbound, outfile)
    fwrite("eps = %s"%eps, outfile)
    fwrite("orthogonal_form = %s"%orthogonal_form, outfile)
    fwrite("magma_args = %s"%magma_args, outfile)
    fwrite("force_computation = %s"%force_computation, outfile)
    fwrite("algorithm = %s"%algorithm, outfile)
    fwrite("derivative_order = %s"%derivative_order, outfile)
    fwrite("lauders_advice = %s"%lauders_advice, outfile)
    fwrite("use_magma = %s"%use_magma, outfile)
    fwrite("num_coeffs_qexpansion = %s"%num_coeffs_qexpansion, outfile)
    fwrite("##########################################", outfile)
    prec = ZZ(prec)

    fwrite("Step 1: Compute the Up matrix", outfile)
    if algorithm == "twostage":
        computation_name = '%s_%s_%s_%s_%s_%s_%s'%(p,N,nu,kk,prec,'triv' if eps is None else 'char',algorithm)
    else:
        computation_name = '%s_%s_%s_%s_%s_%s'%(p,N,nu,kk,prec,'triv' if eps is None else 'char')
    if use_magma:
        tmp_filename = '/tmp/magma_mtx_%s.tmp'%computation_name
        import os.path
        from sage.misc.persist import db, db_save
        try:
            if force_computation:
                raise IOError
            V = db('Lpvalue_Apow_ordbasis_eimat_%s'%computation_name)
            ord_basis, eimat, zetapm, elldash, mdash = V[:5]
            Apow_data = V[5:]
        except IOError:
            if force_computation or not os.path.exists(tmp_filename):
                if eps is not None:
                    eps_magma = sage_character_to_magma(eps,N,magma=magma)
                    magma.load("overconvergent_alan.m")
                    # Am, zetapm, eimatm, elldash, mdash = magma.UpOperatorData(p, eps_magma, kk, prec,WeightBound=weightbound,nvals=5)
                    Am, zetapm, eimatm, elldash, mdash = magma.HigherLevelUpGj(p, kk, prec, weightbound, eps_magma,'"B"',nvals=5)
                else:
                    # Am, zetapm, eimatm, elldash, mdash = magma.UpOperatorData(p, N, kk, prec,WeightBound=weightbound,nvals=5)
                    magma.load("overconvergent_alan.m")
                    Am, zetapm, eimatm, elldash, mdash = magma.HigherLevelUpGj(p, kk, prec, weightbound, N,'"B"',nvals=5)
                fwrite(" ..Converting to Sage...", outfile)
                Amodulus = Am[1,1].Parent().Modulus().sage()
                Aprec = Amodulus.valuation(p)
                Arows = Am.NumberOfRows().sage()
                Acols = Am.NumberOfColumns().sage()
                Emodulus = eimatm[1,1].Parent().Modulus().sage()
                Eprec = Emodulus.valuation(p)
                Erows = eimatm.NumberOfRows().sage()
                Ecols = eimatm.NumberOfColumns().sage()
                magma.load("get_qexpansions.m")
                magma.eval('F := Open("%s", "w");'%tmp_filename)
                magma.eval('fprintf F, "%s, %s, %s, %s \\n"'%(p,Aprec,Arows,Acols)) # parameters
                magma.eval('save_matrix(%s, F)'%(Am.name()))
                # for i in range(1,Arows+1):
                #     magma.eval('fprintf F, "%%o\\n", %s[%s]'%(Am.name(),i))
                magma.eval('fprintf F, "%s, %s, %s, %s \\n"'%(p,Eprec,Erows,Ecols)) # parameters
                magma.eval('save_matrix(%s, F)'%(eimatm.name()))
                # for i in range(1,Erows+1):
                #     magma.eval('fprintf F, "%%o\\n", %s[%s]'%(eimatm.name(),i))
                magma.eval('fprintf F, "%s\\n"'%zetapm)
                magma.eval('fprintf F, "%s\\n"'%elldash)
                magma.eval('fprintf F, "%s\\n"'%mdash)
                magma.eval('delete F;')
                magma.quit()

            # Read A and eimat from file
            from sage.structure.sage_object import load
            from sage.misc.sage_eval import sage_eval
            with open(tmp_filename,'r') as fmagma:
                A = read_matrix_from_file(fmagma)
                eimat = read_matrix_from_file(fmagma)
                zetapm= sage_eval(fmagma.readline())
                elldash = sage_eval(fmagma.readline())
                mdash = sage_eval(fmagma.readline())

            fwrite("Step 3b: Apply Up^(r-1) to H", outfile)
            if algorithm == 'twostage':
                V0  = list(find_Apow_and_ord_two_stage(A, eimat, p, prec))
            else:
                V0 = list(find_Apow_and_ord_three_stage(A,eimat,p,prec))
            ord_basis = V0[0]
            Apow_data = V0[1:]
            V = [ord_basis]
            V.extend([eimat, zetapm, elldash, mdash])
            V.extend(Apow_data)
            db_save(V,'Lpvalue_Apow_ordbasis_eimat_%s'%computation_name)
            from posix import remove
            remove(tmp_filename)

    else:
        A, eimat, elldash, mdash = UpOperator(p,N,kk,prec, modformsring = False, weightbound = 6)

    fwrite("Step 2: p-depletion, Coleman primitive, and multiply", outfile)
    fwrite(".. Need %s coefficients of the q-expansion..."%(p**(nu+1) * elldash), outfile)
    if data is not None:
        f, h = get_magma_qexpansions(data, data_idx, (p**(nu+1) * elldash) + 200, Qp(p,prec), magma=magma)

    H = depletion_coleman_multiply(g, h, p, p**(nu+1) * elldash, t=0)

    fwrite("Step 3a: Compute ordinary projection", outfile)

    if len(Apow_data) == 1:
        Hord = compute_ordinary_projection_two_stage(H, Apow_data, eimat, elldash,p)
    else:
        Hord = compute_ordinary_projection_three_stage(H, [ord_basis] + Apow_data, eimat, elldash,p,nu)
    fwrite('Changing Hord to ring %s'%g[1].parent(), outfile)
    Hord = Hord.change_ring(h[1].parent())
    print [Hord[i] for i in range(30)]
    fwrite("Step 4: Project onto f-component", outfile)
    while True:
        try:
            ell, piHord, epstwist = project_onto_eigenspace(f, ord_basis, Hord, kk, N * p, p = p, derivative_order=derivative_order, max_primes=max_primes)
            break
        except RuntimeError:
            derivative_order += 1
            verbose("Increasing experimental derivative order to %s"%derivative_order)
        except ValueError:
            verbose("Experimental derivative order (%s) seems too high"%derivative_order)
            fwrite("Experimental derivative_order = %s"%derivative_order, outfile)
            fwrite("Seems too high...", outfile)
            fwrite("######################################", outfile)
            assert 0
    n = 1
    while f[n] == 0:
        n = next_prime(n)
    if lauders_advice == True or orthogonal_form is None:
        Lpa =  piHord[n] / (f[n] * epstwist(n))
        fwrite("Experimental derivative_order = %s"%derivative_order, outfile)
        fwrite("Checking Lauder's coincidence... (following should be a bunch of 'large' valuations)", outfile)
        fwrite(str([(i,(Lpa * f[i] * epstwist(i) - piHord[i]).valuation(p)) for i in prime_range(50)]), outfile)
        fwrite("Done", outfile)
    else:
        gplus, gminus = f, orthogonal_form
        l1 = 2
        while N*p*ell % l1 == 0 or gplus[l1] == 0:
            l1 = next_prime(l1)
        proj_mat = matrix([[gplus[l1],gplus[p]],[gminus[l1],gminus[p]]])
        Lpalist =  (matrix([piHord[l1],piHord[p]]) * proj_mat**-1).list()
        Lpa = Lpalist[0]
        if Lpa.valuation() > prec / 2: # this is quite arbitrary!
            Lpa = Lpalist[1]
        Lpa = Lpa / f[n]
    fwrite("ell = %s"%ell, outfile)
    fwrite("######### FINISHED COMPUTATION ###########", outfile)
    fwrite("Lp = %s"%Lpa, outfile)
    fwrite("##########################################", outfile)
    return Lpa, ell
Exemple #22
0
    def complement(self, bound=None):
        """
        Return the largest Hecke-stable complement of this space.

        EXAMPLES::

            sage: M = ModularSymbols(15, 6).cuspidal_subspace()
            sage: M.complement()
            Modular Symbols subspace of dimension 4 of Modular Symbols space of dimension 20 for Gamma_0(15) of weight 6 with sign 0 over Rational Field
            sage: E = EllipticCurve("128a")
            sage: ME = E.modular_symbol_space()
            sage: ME.complement()
            Modular Symbols subspace of dimension 17 of Modular Symbols space of dimension 18 for Gamma_0(128) of weight 2 with sign 1 over Rational Field
        """

        if self.dual_free_module.is_in_cache():
            D = self.dual_free_module()
            V = D.basis_matrix().right_kernel()
            return self.submodule(V, check=False)

        if self.is_ambient():
            return self.ambient_hecke_module().zero_submodule()

        if self.is_zero():
            return self.ambient_hecke_module()

        if self.is_full_hecke_module():
            anemic = False
        else:
            anemic = True

        # TODO: optimize in some cases by computing image of
        # complementary factor instead of kernel...?
        misc.verbose("computing")
        N = self.level()
        A = self.ambient_hecke_module()
        V = A.free_module()
        p = 2
        if bound is None:
            bound = A.hecke_bound()
        while True:
            if anemic:
                while N % p == 0: p = arith.next_prime(p)
            misc.verbose("using T_%s"%p)
            f = self.hecke_polynomial(p)
            T = A.hecke_matrix(p)
            g = T.charpoly('x')
            V = T.kernel_on(V, poly=g//f, check=False)
            if V.rank() + self.rank() <= A.rank():
                break
            p = arith.next_prime(p)
            if p > bound:  # to avoid computing Hecke bound unless necessary
                break

        if V.rank() + self.rank() == A.rank():
            C = A.submodule(V, check=False)
            return C

        # first attempt to compute the complement failed, we now try
        # the following naive approach: decompose the ambient space,
        # decompose self, and sum the pieces of ambient that are not
        # subspaces of self
        misc.verbose("falling back on naive algorithm")
        D = A.decomposition()
        C = A.zero_submodule()
        for X in D:
            if self.intersection(X).dimension() == 0:
                C = C + X
        if C.rank() + self.rank() == A.rank():
            return C

        # failed miserably
        raise RuntimeError("Computation of complementary space failed (cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), A.rank()-self.rank()))
def _next_good_prime(p, R, qq, patience, qqold):
    """
    Find the next prime `\\ell` which is good by ``qq`` but not by ``qqold``, 1 mod ``p``, and for which
    ``b^2+4*c`` is a square mod `\\ell`, for the sequence ``R`` if it is possible in runtime patience.

    INPUT:

    - ``p`` -- a prime

    - ``R`` -- an object in the class ``BinaryRecurrenceSequence``

    - ``qq`` -- a perfect power

    - ``patience`` -- a real number

    - ``qqold`` --  a perfect power less than or equal to ``qq``

    OUTPUT:

    - A prime `\\ell` such that `\\ell` is 1 mod ``p``, ``b^2+4*c`` is a square mod `\\ell` and the period of `\\ell` has ``goodness`` by ``qq`` but not ``qqold``, if patience has not be surpased.  Otherwise ``False``.


    EXAMPLES::

        sage: R = BinaryRecurrenceSequence(1,1)
        sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,1,100,1)        #ran out of patience to search for good primes
        False
        sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,2,100,1)
        29
        sage: sage.combinat.binary_recurrence_sequences._next_good_prime(7,R,2,100,2)        #ran out of patience, as qqold == qq, so no primes work
        False

    """

    #We are looking for pth powers in R.
    #Our primes must be good by qq, but not qqold.
    #We only allow patience number of iterations to find a good prime.

    #The variable _ell for R keeps track of the last "good" prime returned
    #that was not found from the dictionary _PGoodness

    #First, we check to see if we have already computed the goodness of a prime that fits
    #our requirement of being good by qq but not by qqold.  This is stored in the _PGoodness
    #dictionary.

    #Then if we have, we return the smallest such prime and delete it from the list.  If not, we
    #search through patience number of primes R._ell to find one good by qq but not qqold.  If it is
    #not good by either qqold or qq, then we add this prime to R._PGoodness under its goodness.

    #Possible_Primes keeps track of possible primes satisfying our goodness requirements we might return
    Possible_Primes = []

    #check to see if anything in R._PGoodness fits our goodness requirements
    for j in R._PGoodness:
        if (qqold < j <= qq) and len(R._PGoodness[j]):
            Possible_Primes.append(R._PGoodness[j][0])

    #If we found good primes, we take the smallest
    if Possible_Primes != []:
        q = min(Possible_Primes)
        n = _goodness(q, R, p)
        del R._PGoodness[n][
            0]  #if we are going to use it, then we delete it from R._PGoodness
        return q

    #If nothing is already stored in R._PGoodness, we start (from where we left off at R._ell) checking
    #for good primes.  We only tolerate patience number of tries before giving up.
    else:
        i = 0
        while i < patience:
            i += 1
            R._ell = next_prime(R._ell)

            #we require that R._ell is 1 mod p, so that p divides the order of the multiplicative
            #group mod R._ell, so that not all elements of GF(R._ell) are pth powers.
            if R._ell % p == 1:

                #requiring that b^2 + 4c is a square in GF(R._ell) ensures that the period mod R._ell
                #divides R._ell - 1
                if legendre_symbol(R.b**2 + 4 * R.c, R._ell) == 1:

                    N = _goodness(R._ell, R, p)

                    #proceed only if R._ell satisfies the goodness requirements
                    if qqold < N <= qq:
                        return R._ell

                    #if we do not use the prime, we store it in R._PGoodness
                    else:
                        if N in R._PGoodness:
                            R._PGoodness[N].append(R._ell)
                        else:
                            R._PGoodness[N] = [R._ell]

        return False
Exemple #24
0
    def dual_free_module(self, bound=None, anemic=True, use_star=True):
        r"""
        Compute embedded dual free module if possible. In general this won't be
        possible, e.g., if this space is not Hecke equivariant, possibly if it
        is not cuspidal, or if the characteristic is not 0. In all these cases
        we raise a RuntimeError exception.

        If use_star is True (which is the default), we also use the +/-
        eigenspaces for the star operator to find the dual free module of self.
        If self does not have a star involution, use_star will automatically be
        set to False.

        EXAMPLES::

            sage: M = ModularSymbols(11, 2)
            sage: M.dual_free_module()
            Vector space of dimension 3 over Rational Field
            sage: Mpc = M.plus_submodule().cuspidal_submodule()
            sage: Mcp = M.cuspidal_submodule().plus_submodule()
            sage: Mcp.dual_free_module() == Mpc.dual_free_module()
            True
            sage: Mpc.dual_free_module()
            Vector space of degree 3 and dimension 1 over Rational Field
            Basis matrix:
            [  1 5/2   5]

            sage: M = ModularSymbols(35,2).cuspidal_submodule()
            sage: M.dual_free_module(use_star=False)
            Vector space of degree 9 and dimension 6 over Rational Field
            Basis matrix:
            [   1    0    0    0   -1    0    0    4   -2]
            [   0    1    0    0    0    0    0 -1/2  1/2]
            [   0    0    1    0    0    0    0 -1/2  1/2]
            [   0    0    0    1   -1    0    0    1    0]
            [   0    0    0    0    0    1    0   -2    1]
            [   0    0    0    0    0    0    1   -2    1]

            sage: M = ModularSymbols(40,2)
            sage: Mmc = M.minus_submodule().cuspidal_submodule()
            sage: Mcm = M.cuspidal_submodule().minus_submodule()
            sage: Mcm.dual_free_module() == Mmc.dual_free_module()
            True
            sage: Mcm.dual_free_module()
            Vector space of degree 13 and dimension 3 over Rational Field
            Basis matrix:
            [ 0  1  0  0  0  0  1  0 -1 -1  1 -1  0]
            [ 0  0  1  0 -1  0 -1  0  1  0  0  0  0]
            [ 0  0  0  0  0  1  1  0 -1  0  0  0  0]

            sage: M = ModularSymbols(43).cuspidal_submodule()
            sage: S = M[0].plus_submodule() + M[1].minus_submodule()
            sage: S.dual_free_module(use_star=False)
            Traceback (most recent call last):
            ...
            RuntimeError: Computation of complementary space failed (cut down to rank 7, but should have cut down to rank 4).
            sage: S.dual_free_module().dimension() == S.dimension()
            True

        We test that :trac:`5080` is fixed::

            sage: EllipticCurve('128a').congruence_number()
            32

        """

        # if we know the complement we can read off the dual module
        if self.complement.is_in_cache():
            misc.verbose('This module knows its complement already -- cheating in dual_free_module')
            C = self.complement()
            V = C.basis_matrix().right_kernel()
            return V

        misc.verbose("computing dual")

        A = self.ambient_hecke_module()

        if self.dimension() == 0:
            return A.zero_submodule()

        if A.dimension() == self.dimension():
            return A.free_module()

        # ALGORITHM: Compute the char poly of each Hecke operator on
        # the submodule, then use it to cut out a submodule of the
        # dual.  If the dimension cuts down to the dimension of self
        # terminate with success.  If it stays larger beyond the Sturm
        # bound, raise a RuntimeError exception.

        # In the case that the sign of self is not 1, we need to use
        # the star involution as well as the Hecke operators in order
        # to find the dual of self.
        #
        # Note that one needs to comment out the line caching the
        # result of this computation below in order to get meaningful
        # timings.

        # If the star involution doesn't make sense for self, then we
        # can't use it.
        if not hasattr(self, 'star_eigenvalues'):
            use_star = False

        if use_star:
            # If the star involution has both + and - eigenspaces on self,
            # then we compute the dual on each eigenspace, then put them
            # together.
            if len(self.star_eigenvalues()) == 2:
                V = self.plus_submodule(compute_dual = False).dual_free_module() + \
                    self.minus_submodule(compute_dual = False).dual_free_module()
                return V

            # At this point, we know that self is an eigenspace for star.
            V = A.sign_submodule(self.sign()).dual_free_module()
        else:
            V = A.free_module()

        N = self.level()
        p = 2
        if bound is None:
            bound = A.hecke_bound()
        while True:
            if anemic:
                while N % p == 0: p = arith.next_prime(p)
            misc.verbose("using T_%s"%p)
            f = self.hecke_polynomial(p)
            T = A.dual_hecke_matrix(p)
            V = T.kernel_on(V, poly=f, check=False)
            if V.dimension() <= self.dimension():
                break
            p = arith.next_prime(p)
            if p > bound:
                break

        if V.rank() == self.rank():
            return V
        else:
            # Failed to reduce V to the appropriate dimension
            W = self.complement()
            V2 = W.basis_matrix().right_kernel()
            if V2.rank() == self.rank():
                return V2
            else:
                raise RuntimeError("Computation of embedded dual vector space failed " + \
                  "(cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), self.rank()))
Exemple #25
0
    def dual_free_module(self, bound=None, anemic=True, use_star=True):
        r"""
        Compute embedded dual free module if possible. In general this won't be
        possible, e.g., if this space is not Hecke equivariant, possibly if it
        is not cuspidal, or if the characteristic is not 0. In all these cases
        we raise a RuntimeError exception.

        If use_star is True (which is the default), we also use the +/-
        eigenspaces for the star operator to find the dual free module of self.
        If self does not have a star involution, use_star will automatically be
        set to False.

        EXAMPLES::

            sage: M = ModularSymbols(11, 2)
            sage: M.dual_free_module()
            Vector space of dimension 3 over Rational Field
            sage: Mpc = M.plus_submodule().cuspidal_submodule()
            sage: Mcp = M.cuspidal_submodule().plus_submodule()
            sage: Mcp.dual_free_module() == Mpc.dual_free_module()
            True
            sage: Mpc.dual_free_module()
            Vector space of degree 3 and dimension 1 over Rational Field
            Basis matrix:
            [  1 5/2   5]

            sage: M = ModularSymbols(35,2).cuspidal_submodule()
            sage: M.dual_free_module(use_star=False)
            Vector space of degree 9 and dimension 6 over Rational Field
            Basis matrix:
            [   1    0    0    0   -1    0    0    4   -2]
            [   0    1    0    0    0    0    0 -1/2  1/2]
            [   0    0    1    0    0    0    0 -1/2  1/2]
            [   0    0    0    1   -1    0    0    1    0]
            [   0    0    0    0    0    1    0   -2    1]
            [   0    0    0    0    0    0    1   -2    1]

            sage: M = ModularSymbols(40,2)
            sage: Mmc = M.minus_submodule().cuspidal_submodule()
            sage: Mcm = M.cuspidal_submodule().minus_submodule()
            sage: Mcm.dual_free_module() == Mmc.dual_free_module()
            True
            sage: Mcm.dual_free_module()
            Vector space of degree 13 and dimension 3 over Rational Field
            Basis matrix:
            [ 0  1  0  0  0  0  1  0 -1 -1  1 -1  0]
            [ 0  0  1  0 -1  0 -1  0  1  0  0  0  0]
            [ 0  0  0  0  0  1  1  0 -1  0  0  0  0]

            sage: M = ModularSymbols(43).cuspidal_submodule()
            sage: S = M[0].plus_submodule() + M[1].minus_submodule()
            sage: S.dual_free_module(use_star=False)
            Traceback (most recent call last):
            ...
            RuntimeError: Computation of complementary space failed (cut down to rank 7, but should have cut down to rank 4).
            sage: S.dual_free_module().dimension() == S.dimension()
            True

        We test that :trac:`5080` is fixed::

            sage: EllipticCurve('128a').congruence_number()
            32

        """

        # if we know the complement we can read off the dual module
        if self.complement.is_in_cache():
            misc.verbose('This module knows its complement already -- cheating in dual_free_module')
            C = self.complement()
            V = C.basis_matrix().right_kernel()
            return V

        misc.verbose("computing dual")

        A = self.ambient_hecke_module()

        if self.dimension() == 0:
            return A.zero_submodule()

        if A.dimension() == self.dimension():
            return A.free_module()

        # ALGORITHM: Compute the char poly of each Hecke operator on
        # the submodule, then use it to cut out a submodule of the
        # dual.  If the dimension cuts down to the dimension of self
        # terminate with success.  If it stays larger beyond the Sturm
        # bound, raise a RuntimeError exception.

        # In the case that the sign of self is not 1, we need to use
        # the star involution as well as the Hecke operators in order
        # to find the dual of self.
        #
        # Note that one needs to comment out the line caching the
        # result of this computation below in order to get meaningful
        # timings.

        # If the star involution doesn't make sense for self, then we
        # can't use it.
        if not hasattr(self, 'star_eigenvalues'):
            use_star = False

        if use_star:
            # If the star involution has both + and - eigenspaces on self,
            # then we compute the dual on each eigenspace, then put them
            # together.
            if len(self.star_eigenvalues()) == 2:
                V = self.plus_submodule(compute_dual = False).dual_free_module() + \
                    self.minus_submodule(compute_dual = False).dual_free_module()
                return V

            # At this point, we know that self is an eigenspace for star.
            V = A.sign_submodule(self.sign()).dual_free_module()
        else:
            V = A.free_module()

        N = self.level()
        p = 2
        if bound is None:
            bound = A.hecke_bound()
        while True:
            if anemic:
                while N % p == 0: p = arith.next_prime(p)
            misc.verbose("using T_%s"%p)
            f = self.hecke_polynomial(p)
            T = A.dual_hecke_matrix(p)
            V = T.kernel_on(V, poly=f, check=False)
            if V.dimension() <= self.dimension():
                break
            p = arith.next_prime(p)
            if p > bound:
                break

        if V.rank() == self.rank():
            return V
        else:
            # Failed to reduce V to the appropriate dimension
            W = self.complement()
            V2 = W.basis_matrix().right_kernel()
            if V2.rank() == self.rank():
                return V2
            else:
                raise RuntimeError("Computation of embedded dual vector space failed " + \
                  "(cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), self.rank()))
Exemple #26
0
def project_onto_eigenspace(gamma, ord_basis, hord, weight=2, level=1, derivative_order = 1, p = None, max_primes = 10):
    ell = 1
    level = ZZ(level)
    R = hord[1].parent()
    prec = R.precision_cap()
    if gamma.character() is not None:
        embed_char = find_embeddings(gamma.character().values()[0].parent(),R)[0]
        chi_inv = gamma.character()**-1
        epstwist = lambda x: embed_char(chi_inv(x))
    else:
        epstwist = None
    tested_primes = 0
    qq = None
    success = False
    while tested_primes < max_primes:
        ell = next_prime(ell)
        if level % ell == 0:
            continue
        verbose('Using ell = %s'%ell)
        try:
            T = hecke_matrix_on_ord(ell, ord_basis, weight, level, p=p, eps=epstwist)
        except ValueError:
            break
        except RuntimeError:
            verbose("The function hecke_matrix_on_ord raised a Runtime Error. Stopping.")
            raise AssertionError
        aell = gamma[ell] / gamma[1]
        if epstwist is not None:
            aell *= epstwist(ell)
        verbose('a_ell = %s'%aell)
        pp = T.charpoly().change_ring(R)
        verbose('deg charpoly(T_ell) = %s'%pp.degree())
        if pp.degree() <= derivative_order:
            verbose("The derivative_order (= %s) is too high."%derivative_order)
            raise ValueError("The derivative_order (= %s) is too high."%derivative_order)
        x = pp.parent().gen()
        this_is_zero = pp.subs(R(aell))
        verbose("this_is_zero = %s"%this_is_zero)
        if this_is_zero.valuation(p) < 4: # DEBUG this value is arbitrary...
            verbose('!!! Should we skip ell = %s (because %s != 0 (val = %s))?????'%(ell,this_is_zero,this_is_zero.valuation(p)))
            tested_primes += 1
            continue
        if pp.derivative(derivative_order).subs(R(aell)).valuation(p) >= prec - 4: # DEBUG this value is arbitrary...
            verbose('pp.derivative(derivative_order).subs(R(aell)) = %s'%pp.derivative().subs(R(aell)))
            verbose('... Skipping ell = %s because polynomial has repeated roots'%ell)
            tested_primes += 1
        else:
            verbose("Performing division...")
            qq = pp.quo_rem((x-R(aell))**ZZ(derivative_order))[0]
            qq_aell = qq.subs(R(aell))
            verbose('qq_aell = %s'%qq_aell)
            try:
                qq_aell_inv = qq_aell**-1
                success = True
                verbose("Done, success!")
                break
            except:
                verbose("qq_aell seems to be zero")
                tested_primes += 1

    if not success:
        verbose("Too many tested primes...")
        raise RuntimeError("Reached maximum number of tested primes")
    # qq = qq.parent()([o.lift() for o in qq.list()])
    verbose("Now doing final steps of projection...")
    qqT = try_lift(qq(T))
    ord_basis_small = try_lift(ord_basis.submatrix(0,0,ncols=len(hord)))
    hord_in_katz = qexp_to_basis(hord, ord_basis_small)
    qT_hord_in_katz = hord_in_katz * qqT
    qT_hord = qT_hord_in_katz * try_lift(ord_basis)
    ans = (qq_aell_inv * try_lift(qT_hord)).change_ring(R)
    verbose("Now doing final steps of projection...DONE")
    if epstwist is None:
        epstwist = lambda ll : ZZ(1) if gcd(level,ll) == 1 else ZZ(0)
    return ell, ans, epstwist