Esempio n. 1
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 [CGW13]_.

        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 [CGW13_].  (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))
Esempio n. 2
0
    def set_params(lam, k):
        n = pow(2, ceil(log(lam**2 * k)/log(2))) # dim of poly ring, closest power of 2 to k(lam^2)
        q = next_prime(ZZ(2)**(8*k*lam) * n**k, proof=False) # prime modulus

        sigma = int(sqrt(lam * n))
        sigma_prime = lam * int(n**(1.5))

        return (n, q, sigma, sigma_prime, k)
Esempio n. 3
0
 def _find_aq(self, p, M, check):
     q = ZZ(2)
     k = self.parent().weight()
     aq = self.Tq_eigenvalue(q, check=check)
     eisenloss = (aq - q**(k+1) - 1).valuation(p)
     while q != p and eisenloss >= M:
         q = next_prime(q)
         aq = self.Tq_eigenvalue(q, check=check)
         eisenloss = (aq - q**(k+1) - 1).valuation(p)
     return q, aq, eisenloss
Esempio n. 4
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 [CGW13]_.

        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 [CGW13_].  (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))
Esempio n. 5
0
    def set_params(lam, k):
        n = pow(2, ceil(
            log(lam**2 * k) /
            log(2)))  # dim of poly ring, closest power of 2 to k(lam^2)
        q = next_prime(ZZ(2)**(8 * k * lam) * n**k,
                       proof=False)  # prime modulus

        sigma = int(sqrt(lam * n))
        sigma_prime = lam * int(n**(1.5))

        return (n, q, sigma, sigma_prime, k)
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
Esempio n. 7
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
        [LP11]_.

        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, DiscreteGaussianPolynomialSamplerRejection(8, 2.803372, 53, 4), 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 = 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 [LP11]_
        q = next_prime(floor(2**round(log(256 / s_t_bound, 2))))
        # Gaussian parameter as defined in [LP11]_
        s = sqrt(s_t_bound * floor(q / 4))
        # Transform s into stddev
        stddev = s / sqrt(2 * pi.n())
        D = DiscreteGaussianPolynomialSampler(n, stddev)
        RingLWE.__init__(self,
                         N=N,
                         q=q,
                         D=D,
                         poly=None,
                         secret_dist='noise',
                         m=m)
Esempio n. 8
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
Esempio n. 9
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
        [LP11]_.

        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 [LP11]_ (default: ``None``)

        EXAMPLES::

            sage: from sage.crypto.lwe import LindnerPeikert
            sage: LindnerPeikert(n=20)
            LWE(20, 2053, DiscreteGaussianSamplerRejection(3.600954, 53, 4), '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 = 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 [LP11]_
        q = next_prime(floor(2**round(log(256 / s_t_bound, 2))))
        # Gaussian parameter as defined in [LP11]_
        s = sqrt(s_t_bound * floor(q / 4))
        # Transform s into stddev
        stddev = s / sqrt(2 * pi.n())
        D = DiscreteGaussianSampler(stddev)
        LWE.__init__(self, n=n, q=q, D=D, secret_dist='noise', m=m)
Esempio n. 10
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
        [LP11]_.

        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 [LP11]_ (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 [LP11]_
        q = next_prime(floor(2**round(log(256 / s_t_bound, 2))))
        # Gaussian parameter as defined in [LP11]_
        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)
Esempio n. 11
0
    def _find_aq(self, p, M, check):
        r"""
        Helper function for finding Hecke eigenvalue `aq` and `q`
        (with `q` not equal to `p`) in the case when `ap = 1 (mod p^M)`,
        which creates the need to use other Hecke eigenvalues

        INPUT:

        - ``p`` -- working prime

        - ``M`` -- precision

        - ``check`` --

        OUTPUT:

        Tuple `(q, aq, eisenloss)`, with

        - ``q`` -- a prime not equal to `p`

        - ``aq`` -- Hecke eigenvalue at `q`

        - ``eisenloss`` -- the `p`-adic valuation of `aq - q^(k+1) - 1`

        EXAMPLES::


        """
        q = ZZ(2)
        k = self.parent().weight()
        aq = self.Tq_eigenvalue(q, check=check)
        eisenloss = (aq - q**(k+1) - 1).valuation(p)
        while q != p and eisenloss >= M:
            q = next_prime(q)
            aq = self.Tq_eigenvalue(q, check=check)
            if q != p:
                eisenloss = (aq - q**(k+1) - 1).valuation(p)
            else:
                eisenloss = (aq - 1).valuation(p)                
        return q, aq, eisenloss
Esempio n. 12
0
    def __init__(self, n, secret_dist='uniform', m=None):
        """
        Construct LWE instance parameterised by security paramter ``n`` where
        the modulus ``q`` and the ``stddev`` of the noise are chosen as in
        [Reg09]_.

        INPUT:

        - ``n`` - security paramter (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: Regev(n=20)
            LWE(20, 401, DiscreteGaussianSamplerRejection(1.915069, 401, 4), 'uniform', None)
        """
        q = ZZ(next_prime(n**2))
        s = RR(1 / (RR(n).sqrt() * log(n, 2)**2) * q)
        D = DiscreteGaussianSampler(s / sqrt(2 * pi.n()), q)
        LWE.__init__(self, n=n, q=q, D=D, secret_dist=secret_dist, m=m)
Esempio n. 13
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)
Esempio n. 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
        [LP11]_.

        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 [LP11]_
        q = next_prime(floor(2**round(log(256 / s_t_bound, 2))))
        # Gaussian parameter as defined in [LP11]_
        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)
Esempio n. 15
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 #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())
Esempio n. 16
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 statisfies the goodness requirements
                    if qqold < N <= qq:
                        return R._ell

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

        return False
def _next_good_prime(p, R, qq, patience, qqold):

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

    INPUT:

    - ``p`` -- a prime

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

    - ``qq`` -- a perfect power

    - ``patience`` -- a real number

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

    OUTPUT:

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


    EXAMPLES::

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

    """

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

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

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

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

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


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

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

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

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

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

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

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

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

        return False