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
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))
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
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]
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
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
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))
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
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
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
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)
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)
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)
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)
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)
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)
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
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]
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]
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
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
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()))
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