def random_element(self): r""" Return a random element of `\Q/n\Z`. The denominator is selected using the ``1/n`` distribution on integers, modified to return a positive value. The numerator is then selected uniformly. EXAMPLES:: sage: G = QQ/(6*ZZ) sage: G.random_element() 47/16 sage: G.random_element() 1 sage: G.random_element() 3/5 """ if self.n == 0: return self(QQ.random_element()) d = ZZ.random_element() if d >= 0: d = 2 * d + 1 else: d = -2 * d n = ZZ.random_element((self.n * d).ceil()) return self(n / d)
def mult_order(x): ct = ZZ.one() cur = x while cur != one: cur *= x ct += ZZ.one() return ZZ(ct)
def graded_dimension(self, k): r""" Return the dimension of the ``k``-th graded piece of ``self``. The `k`-th graded part of a free Lie algebra on `n` generators has dimension .. MATH:: \frac{1}{k} \sum_{d \mid k} \mu(d) n^{k/d}, where `\mu` is the Mobius function. REFERENCES: [MKO1998]_ EXAMPLES:: sage: L = LieAlgebra(QQ, 'x', 3) sage: H = L.Hall() sage: [H.graded_dimension(i) for i in range(1, 11)] [3, 3, 8, 18, 48, 116, 312, 810, 2184, 5880] sage: H.graded_dimension(0) 0 """ if k == 0: return 0 from sage.arith.all import moebius s = len(self.lie_algebra_generators()) k = ZZ(k) # Make sure we have something that is in ZZ return sum(moebius(d) * s**(k // d) for d in k.divisors()) // k
def __iter__(self): r""" Create an iterator that generates the elements of `\Q/n\Z` without repetition, organized by increasing denominator. For a fixed denominator, elements are listed by increasing numerator. EXAMPLES: The first 19 elements of `\Q/5\Z`:: sage: import itertools sage: list(itertools.islice(QQ/(5*ZZ), 19r)) [0, 1, 2, 3, 4, 1/2, 3/2, 5/2, 7/2, 9/2, 1/3, 2/3, 4/3, 5/3, 7/3, 8/3, 10/3, 11/3, 13/3] """ if self.n == 0: for x in QQ: yield self(x) else: d = ZZ(0) while True: for a in d.coprime_integers((d * self.n).floor()): yield self(a / d) d += 1
def add_sha_tor_primes(N1, N2): """ Add the 'sha', 'sha_primes', 'torsion_primes' fields to every curve in the database whose conductor is between N1 and N2 inclusive. """ query = {} query["conductor"] = {"$gte": int(N1), "$lte": int(N2)} res = curves.find(query) res = res.sort([("conductor", pymongo.ASCENDING)]) n = 0 for C in res: label = C["lmfdb_label"] if n % 1000 == 0: print label n += 1 torsion = ZZ(C["torsion"]) sha = RR(C["sha_an"]).round() sha_primes = sha.prime_divisors() torsion_primes = torsion.prime_divisors() data = {} data["sha"] = int(sha) data["sha_primes"] = [int(p) for p in sha_primes] data["torsion_primes"] = [int(p) for p in torsion_primes] curves.update({"lmfdb_label": label}, {"$set": data}, upsert=True)
def phi(self, i): r""" Return `\varphi_i` of ``self``. EXAMPLES:: sage: S = crystals.OddNegativeRoots(['A', [2,2]]) sage: mg = S.module_generator() sage: [mg.phi(i) for i in S.index_set()] [0, 0, 1, 0, 0] sage: b = mg.f(0) sage: [b.phi(i) for i in S.index_set()] [0, 1, 0, 1, 0] sage: b = mg.f_string([0,1,0,-1,0,-1]); b {-e[-2]+e[1], -e[-2]+e[2], -e[-1]+e[1]} sage: [b.phi(i) for i in S.index_set()] [2, 0, 0, 1, 1] TESTS:: sage: S = crystals.OddNegativeRoots(['A', [2,1]]) sage: def count_f(x, i): ....: ret = -1 ....: while x is not None: ....: x = x.f(i) ....: ret += 1 ....: return ret sage: for x in S: ....: for i in S.index_set(): ....: assert x.phi(i) == count_f(x, i) """ if i == 0: return ZZ.zero() if (-1,1) in self.value else ZZ.one() count = 0 ret = 0 if i < 0: lst = sorted(self.value, key=lambda x: (x[1], -x[0])) for val in reversed(lst): # We don't have to check val[1] because this is an odd root if val[0] == i: if count == 0: ret += 1 else: count -= 1 elif val[0] == i - 1: count += 1 else: # i > 0 lst = sorted(self.value, key=lambda x: (-x[0], -x[1])) for val in lst: # We don't have to check val[0] because this is an odd root if val[1] == i: if count == 0: ret += 1 else: count -= 1 elif val[1] == i + 1: count += 1 return ret
def twoadic(line): r""" Parses one line from a 2adic file. Returns the label and a dict containing fields with keys '2adic_index', '2adic_log_level', '2adic_gens' and '2adic_label'. Input line fields: conductor iso number ainvs index level gens label Sample input lines: 110005 a 2 [1,-1,1,-185793,29503856] 12 4 [[3,0,0,1],[3,2,2,3],[3,0,0,3]] X24 27 a 1 [0,0,1,0,-7] inf inf [] CM """ data = split(line) assert len(data) == 8 label = data[0] + data[1] + data[2] model = data[7] if model == "CM": return label, {"2adic_index": int(0), "2adic_log_level": None, "2adic_gens": None, "2adic_label": None} index = int(data[4]) level = ZZ(data[5]) log_level = int(level.valuation(2)) assert 2 ** log_level == level if data[6] == "[]": gens = [] else: gens = data[6][1:-1].replace("],[", "];[").split(";") gens = [[int(c) for c in g[1:-1].split(",")] for g in gens] return label, {"2adic_index": index, "2adic_log_level": log_level, "2adic_gens": gens, "2adic_label": model}
def __init__(self, params, asym=False): # set parameters (self.alpha, self.beta, self.rho, self.rho_f, self.eta, self.bound, self.n, self.k) = params self.x0 = ZZ(1) self.primes = [random_prime(2**self.eta, lbound = 2**(self.eta - 1), proof=False) for i in range(self.n)] primes = self.primes self.x0 = prod(primes) # generate CRT coefficients self.coeff = [ZZ((self.x0/p_i) * ZZ(Zmod(p_i)(self.x0/p_i)**(-1))) for p_i in primes] # generate secret g_i self.g = [random_prime(2**self.alpha, proof=False) for i in range(self.n)] # generate zs and zs^(-1) z = [] zinv = [] # generate z and z^(-1) if not asym: while True: z = ZZ.random_element(self.x0) try: zinv = ZZ(Zmod(self.x0)(z)**(-1)) break except ZeroDivisionError: ''' Error occurred, retry sampling ''' z, self.zinv = zip(*[(z,zinv) for i in range(self.k)]) else: # asymmetric version for i in range(self.k): while True: z_i = ZZ.random_element(self.x0) try: zinv_i = ZZ(Zmod(self.x0)(z_i)**(-1)) break except ZeroDivisionError: ''' Error occurred, retry sampling ''' z.append(z_i) zinv.append(zinv_i) self.zinv = zinv # generate p_zt zk = Zmod(self.x0)(1) self.p_zt = 0 for z_i in z: zk *= Zmod(self.x0)(z_i) for i in range(self.n): self.p_zt += Zmod(self.x0)(ZZ(Zmod(self.primes[i])(self.g[i])**(-1) * Zmod(self.primes[i])(zk)) * ZZ.random_element(2**self.beta) * (self.x0/self.primes[i])) self.p_zt = Zmod(self.x0)(self.p_zt)
def iterator_fast(n, l): """ Iterate over all ``l`` weighted integer vectors with total weight ``n``. INPUT: - ``n`` -- an integer - ``l`` -- the weights in weakly decreasing order EXAMPLES:: sage: from sage.combinat.integer_vector_weighted import iterator_fast sage: list(iterator_fast(3, [2,1,1])) [[1, 1, 0], [1, 0, 1], [0, 3, 0], [0, 2, 1], [0, 1, 2], [0, 0, 3]] sage: list(iterator_fast(2, [2])) [[1]] Test that :trac:`20491` is fixed:: sage: type(list(iterator_fast(2, [2]))[0][0]) <type 'sage.rings.integer.Integer'> """ if n < 0: return zero = ZZ.zero() one = ZZ.one() if not l: if n == 0: yield [] return if len(l) == 1: if n % l[0] == 0: yield [n // l[0]] return k = 0 cur = [n // l[k] + one] rem = n - cur[-1] * l[k] # Amount remaining while cur: cur[-1] -= one rem += l[k] if rem == zero: yield cur + [zero] * (len(l) - len(cur)) elif cur[-1] < zero or rem < zero: rem += cur.pop() * l[k] k -= 1 elif len(l) == len(cur) + 1: if rem % l[-1] == zero: yield cur + [rem // l[-1]] else: k += 1 cur.append(rem // l[k] + one) rem -= cur[-1] * l[k]
def dimension__jacobi_scalar(k, m) : raise RuntimeError( "There is a bug in the implementation" ) m = ZZ(m) dimension = 0 for d in (m // m.squarefree_part()).isqrt().divisors() : m_d = m // d**2 dimension += sum ( dimension__jacobi_scalar_f(k, m_d, f) for f in m_d.divisors() ) return dimension
def __init__(self, p, base_ring): r""" Initialisation function. EXAMPLE:: sage: pAdicWeightSpace(17) Space of 17-adic weight-characters defined over '17-adic Field with capped relative precision 20' """ ParentWithBase.__init__(self, base=base_ring) p = ZZ(p) if not p.is_prime(): raise ValueError, "p must be prime" self._p = p self._param = Qp(p)((p == 2 and 5) or (p + 1))
def __iter__(self): """ Iterate over ``self``. EXAMPLES:: sage: S = SignedPermutations(2) sage: [x for x in S] [[1, 2], [1, -2], [-1, 2], [-1, -2], [2, 1], [2, -1], [-2, 1], [-2, -1]] """ pmone = [ZZ.one(), -ZZ.one()] for p in self._P: for c in itertools.product(pmone, repeat=self._n): yield self.element_class(self, c, p)
def long_element(self, index_set=None): """ Return the longest element of ``self``, or of the parabolic subgroup corresponding to the given ``index_set``. INPUT: - ``index_set`` -- (optional) a subset (as a list or iterable) of the nodes of the indexing set EXAMPLES:: sage: S = SignedPermutations(4) sage: S.long_element() [-1, -2, -3, -4] TESTS: Check that this is the element of maximal length (:trac:`25200`):: sage: S = SignedPermutations(4) sage: S.long_element().length() == max(x.length() for x in S) True sage: all(SignedPermutations(n).long_element().length() == n^2 ....: for n in range(2,10)) True """ if index_set is not None: return super(SignedPermutations, self).long_element() return self.element_class(self, [-ZZ.one()] * self._n, self._P.one())
def _inner_qq(self, qelt1, qelt2): """ Symmetric form between two elements of the root lattice associated to ``self``. EXAMPLES:: sage: P = RootSystem(['F',4,1]).weight_lattice(extended=true) sage: Lambda = P.fundamental_weights() sage: V = IntegrableRepresentation(Lambda[0]) sage: alpha = V.root_lattice().simple_roots() sage: Matrix([[V._inner_qq(alpha[i], alpha[j]) for j in V._index_set] for i in V._index_set]) [ 2 -1 0 0 0] [ -1 2 -1 0 0] [ 0 -1 2 -1 0] [ 0 0 -1 1 -1/2] [ 0 0 0 -1/2 1] .. WARNING: If ``qelt1`` or ``qelt1`` accidentally gets coerced into the extended weight lattice, this will return an answer, and it will be wrong. To make this code robust, parents should be checked. This is not done since in the application the parents are known, so checking would unnecessarily slow us down. """ mc1 = qelt1.monomial_coefficients() mc2 = qelt2.monomial_coefficients() zero = ZZ.zero() return sum(mc1.get(i, zero) * mc2.get(j, zero) * self._cartan_matrix[i,j] / self._eps[i] for i in self._index_set for j in self._index_set)
def _inner_pp(self, pelt1, pelt2): """ Symmetric form between an two elements of the weight lattice associated to ``self``. EXAMPLES:: sage: P = RootSystem(['G',2,1]).weight_lattice(extended=true) sage: Lambda = P.fundamental_weights() sage: V = IntegrableRepresentation(Lambda[0]) sage: alpha = V.root_lattice().simple_roots() sage: Matrix([[V._inner_pp(Lambda[i],P(alpha[j])) for j in V._index_set] for i in V._index_set]) [ 1 0 0] [ 0 1/3 0] [ 0 0 1] sage: Matrix([[V._inner_pp(Lambda[i],Lambda[j]) for j in V._index_set] for i in V._index_set]) [ 0 0 0] [ 0 2/3 1] [ 0 1 2] """ mc1 = pelt1.monomial_coefficients() mc2 = pelt2.monomial_coefficients() zero = ZZ.zero() mc1d = mc1.get('delta', zero) mc2d = mc2.get('delta', zero) return sum(mc1.get(i,zero) * self._ac[i] * mc2d + mc2.get(i,zero) * self._ac[i] * mc1d for i in self._index_set) \ + sum(mc1.get(i,zero) * mc2.get(j,zero) * self._ip[ii,ij] for ii, i in enumerate(self._index_set_classical) for ij, j in enumerate(self._index_set_classical))
def __init__(self, field, num_integer_primes=10000, max_iterations=100): r""" Construct a new iterator of small degree one primes. EXAMPLES:: sage: x = QQ['x'].gen() sage: K.<a> = NumberField(x^2 - 3) sage: K.primes_of_degree_one_list(3) # random [Fractional ideal (2*a + 1), Fractional ideal (-a + 4), Fractional ideal (3*a + 2)] """ self._field = field self._poly = self._field.absolute_field("b").defining_polynomial() self._poly = ZZ["x"](self._poly.denominator() * self._poly()) # make integer polynomial # this uses that [ O_K : Z[a] ]^2 = | disc(f(x)) / disc(O_K) | from sage.libs.pari.all import pari self._prod_of_small_primes = ZZ( pari("TEMPn = %s; TEMPps = primes(TEMPn); prod(X = 1, TEMPn, TEMPps[X])" % num_integer_primes) ) self._prod_of_small_primes //= self._prod_of_small_primes.gcd(self._poly.discriminant()) self._integer_iter = iter(ZZ) self._queue = [] self._max_iterations = max_iterations
def random_solution(B,K): r""" Returns a random solution in non-negative integers to the equation `a_1 + 2 a_2 + 3 a_3 + ... + B a_B = K`, using a greedy algorithm. Note that this is *much* faster than using ``WeightedIntegerVectors.random_element()``. INPUT: - ``B``, ``K`` -- non-negative integers. OUTPUT: - list. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import random_solution sage: random_solution(5,10) [1, 1, 1, 1, 0] """ a = [] for i in xrange(B,1,-1): ai = ZZ.random_element((K // i) + 1) a.append(ai) K = K - ai*i a.append(K) a.reverse() return a
def __classcall_private__(cls, n=1, R=0): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: H1 = groups.matrix.Heisenberg(n=2, R=5) sage: H2 = groups.matrix.Heisenberg(n=2, R=ZZ.quo(5)) sage: H1 is H2 True sage: H1 = groups.matrix.Heisenberg(n=2) sage: H2 = groups.matrix.Heisenberg(n=2, R=ZZ) sage: H1 is H2 True """ if n not in ZZ or n <= 0: raise TypeError("degree of Heisenberg group must be a positive integer") if R in ZZ: if R == 0: R = ZZ elif R > 1: R = ZZ.quo(R) else: raise ValueError("R must be a positive integer") elif R is not ZZ and R not in Rings().Finite(): raise NotImplementedError("R must be a finite ring or ZZ") return super(HeisenbergGroup, cls).__classcall__(cls, n, R)
def residue_field(self): r""" Return the residue class field of this ideal, which must be prime. .. TODO:: Implement this for more general rings. Currently only defined for `\ZZ` and for number field orders. EXAMPLES:: sage: P = ZZ.ideal(61); P Principal ideal (61) of Integer Ring sage: F = P.residue_field(); F Residue field of Integers modulo 61 sage: pi = F.reduction_map(); pi Partially defined reduction map: From: Rational Field To: Residue field of Integers modulo 61 sage: pi(123/234) 6 sage: pi(1/61) Traceback (most recent call last): ... ZeroDivisionError: Cannot reduce rational 1/61 modulo 61: it has negative valuation sage: lift = F.lift_map(); lift Lifting map: From: Residue field of Integers modulo 61 To: Integer Ring sage: lift(F(12345/67890)) 33 sage: (12345/67890) % 61 33 TESTS:: sage: ZZ.ideal(96).residue_field() Traceback (most recent call last): ... ValueError: The ideal (Principal ideal (96) of Integer Ring) is not prime :: sage: R.<x>=QQ[] sage: I=R.ideal(x^2+1) sage: I.is_prime() True sage: I.residue_field() Traceback (most recent call last): ... TypeError: residue fields only supported for polynomial rings over finite fields. """ if not self.is_prime(): raise ValueError("The ideal (%s) is not prime"%self) from sage.rings.integer_ring import ZZ if self.ring() is ZZ: return ZZ.residue_field(self, check = False) raise NotImplementedError("residue_field() is only implemented for ZZ and rings of integers of number fields.")
def allbsd(line): r""" Parses one line from an allbsd file. Returns the label and a dict containing fields with keys 'conductor', 'iso', 'number', 'ainvs', 'rank', 'torsion', 'torsion_primes', 'tamagawa_product', 'real_period', 'special_value', 'regulator', 'sha_an', 'sha', 'sha_primes', all values being strings or floats or ints or lists of ints. Input line fields: conductor iso number ainvs rank torsion tamagawa_product real_period special_value regulator sha_an Sample input line: 11 a 1 [0,-1,1,-10,-20] 0 5 5 1.2692093042795534217 0.25384186085591068434 1 1.00000000000000000000 """ data = split(line) label = data[0] + data[1] + data[2] ainvs = parse_ainvs(data[3]) torsion = ZZ(data[5]) sha_an = RR(data[10]) sha = sha_an.round() sha_primes = sha.prime_divisors() torsion_primes = torsion.prime_divisors() data = { 'conductor': int(data[0]), 'iso': data[0] + data[1], 'number': int(data[2]), 'ainvs': ainvs, 'rank': int(data[4]), 'tamagawa_product': int(data[6]), 'real_period': float(data[7]), 'special_value': float(data[8]), 'regulator': float(data[9]), 'sha_an': float(sha_an), 'sha': int(sha), 'sha_primes': [int(p) for p in sha_primes], 'torsion': int(torsion), 'torsion_primes': [int(p) for p in torsion_primes] } return label, data
def period_from_coords(p, E, P, prec=20): r""" Given a point `P` in the formal group of the elliptic curve `E` with split multiplicative reduction, this produces an element `u` in `\QQ_p^{\times}` mapped to the point `P` by the Tate parametrisation. The algorithm return the unique such element in `1+p\ZZ_p`. INPUT: - ``P`` - a point on the elliptic curve. - ``prec`` - the `p`-adic precision, default is 20. """ R = Qp(p, prec) if P[0].valuation(p) >= 0: raise ValueError, "The point must lie in the formal group." Etate = E.tate_curve(p) Eq = Etate.curve(prec=prec) isom = Etate._isomorphism(prec=prec) C = isom[0] r = isom[1] s = isom[2] t = isom[3] xx = r + C ** 2 * P[0] yy = t + s * C ** 2 * P[0] + C ** 3 * P[1] try: EqCp = Eq.change_ring(yy.parent()) Pq = EqCp([xx, yy]) except: raise RuntimeError, "Bug : Point %s does not lie on the curve " % [xx, yy] tt = -xx / yy eqhat = Eq.formal() eqlog = eqhat.log(prec + 3) z = eqlog(tt) u = ZZ(1) fac = ZZ(1) for i in range(1, 2 * prec + 1): fac = fac * i u = u + z ** i / fac q = Etate.parameter(prec=prec) un = u * q ** (-(u.valuation() / q.valuation()).floor()) return un
def Birkhoff_polytope(self, n): """ Return the Birkhoff polytope with `n!` vertices. The vertices of this polyhedron are the (flattened) `n` by `n` permutation matrices. So the ambient vector space has dimension `n^2` but the dimension of the polyhedron is `(n-1)^2`. INPUT: - ``n`` -- a positive integer giving the size of the permutation matrices. .. SEEALSO:: :meth:`sage.matrix.matrix2.Matrix.as_sum_of_permutations` -- return the current matrix as a sum of permutation matrices EXAMPLES:: sage: b3 = polytopes.Birkhoff_polytope(3) sage: b3.f_vector() (1, 6, 15, 18, 9, 1) sage: print b3.ambient_dim(), b3.dim() 9 4 sage: b3.is_lattice_polytope() True sage: p3 = b3.ehrhart_polynomial() # optional - latte_int sage: p3 # optional - latte_int 1/8*t^4 + 3/4*t^3 + 15/8*t^2 + 9/4*t + 1 sage: [p3(i) for i in [1,2,3,4]] # optional - latte_int [6, 21, 55, 120] sage: [len((i*b3).integral_points()) for i in [1,2,3,4]] [6, 21, 55, 120] sage: b4 = polytopes.Birkhoff_polytope(4) sage: print b4.n_vertices(), b4.ambient_dim(), b4.dim() 24 16 9 """ from itertools import permutations verts = [] for p in permutations(range(n)): verts.append( [ZZ.one() if p[i]==j else ZZ.zero() for j in range(n) for i in range(n) ] ) return Polyhedron(vertices=verts, base_ring=ZZ)
def cardinality(self): """ Return the cardinality of ``self``. EXAMPLES:: sage: IntegerVectors(3, 3, min_part=1).cardinality() 1 sage: IntegerVectors(5, 3, min_part=1).cardinality() 6 sage: IntegerVectors(13, 4, max_part=4).cardinality() 20 sage: IntegerVectors(k=4, max_part=3).cardinality() 256 sage: IntegerVectors(k=3, min_part=2, max_part=4).cardinality() 27 sage: IntegerVectors(13, 4, min_part=2, max_part=4).cardinality() 16 """ if self.k is None: if self.n is None: return PlusInfinity() if ('max_length' not in self.constraints and self.constraints.get('min_part', 0) <= 0): return PlusInfinity() elif ('max_part' in self.constraints and self.constraints['max_part'] != PlusInfinity()): if (self.n is None and len(self.constraints) == 2 and 'min_part' in self.constraints and self.constraints['min_part'] >= 0): num = self.constraints['max_part'] - self.constraints['min_part'] + 1 return Integer(num ** self.k) if len(self.constraints) == 1: m = self.constraints['max_part'] if self.n is None: return Integer((m + 1) ** self.k) if m >= self.n: return Integer(binomial(self.n + self.k - 1, self.n)) # do by inclusion / exclusion on the number # i of parts greater than m return Integer(sum( (-1)**i * binomial(self.n+self.k-1-i*(m+1), self.k-1) \ * binomial(self.k,i) for i in range(self.n/(m+1)+1) )) return ZZ.sum(ZZ.one() for x in self)
def gcd(self,other): """ Greatest common divisor. NOTE: Since we are in a field and the greatest common divisor is only determined up to a unit, it is correct to either return zero or one. Note that fraction fields of unique factorization domains provide a more sophisticated gcd. EXAMPLES:: sage: GF(5)(1).gcd(GF(5)(1)) 1 sage: GF(5)(1).gcd(GF(5)(0)) 1 sage: GF(5)(0).gcd(GF(5)(0)) 0 For fields of characteristic zero (i.e., containing the integers as a sub-ring), evaluation in the integer ring is attempted. This is for backwards compatibility:: sage: gcd(6.0,8); gcd(6.0,8).parent() 2 Integer Ring If this fails, we resort to the default we see above:: sage: gcd(6.0*CC.0,8*CC.0); gcd(6.0*CC.0,8*CC.0).parent() 1.00000000000000 Complex Field with 53 bits of precision AUTHOR: - Simon King (2011-02): Trac ticket #10771 """ P = self.parent() try: other = P(other) except (TypeError, ValueError): raise ArithmeticError("The second argument can not be interpreted in the parent of the first argument. Can't compute the gcd") from sage.rings.integer_ring import ZZ if ZZ.is_subring(P): try: return ZZ(self).gcd(ZZ(other)) except TypeError: pass # there is no custom gcd, so, we resort to something that always exists # (that's new behaviour) if self==0 and other==0: return P.zero() return P.one()
def __init__(self, parent, k, chi=None): r""" Create a locally algebraic weight-character. EXAMPLES:: sage: pAdicWeightSpace(29)(13, DirichletGroup(29, Qp(29)).0) (13, 29, [2 + 2*29 + ... + O(29^20)]) """ WeightCharacter.__init__(self, parent) k = ZZ(k) self._k = k if chi is None: chi = trivial_character(self._p, QQ) n = ZZ(chi.conductor()) if n == 1: n = self._p if not n.is_power_of(self._p): raise ValueError, "Character must have %s-power conductor" % p self._chi = DirichletGroup(n, chi.base_ring())(chi)
def one(self): """ Return the identity element of ``self``. EXAMPLES:: sage: S = SignedPermutations(4) sage: S.one() [1, 2, 3, 4] """ return self.element_class(self, [ZZ.one()] * self._n, self._P.identity())
def lcm(self,other): """ Least common multiple. NOTE: Since we are in a field and the least common multiple is only determined up to a unit, it is correct to either return zero or one. Note that fraction fields of unique factorization domains provide a more sophisticated lcm. EXAMPLES:: sage: GF(2)(1).lcm(GF(2)(0)) 0 sage: GF(2)(1).lcm(GF(2)(1)) 1 If the field contains the integer ring, it is first attempted to compute the gcd there:: sage: lcm(15.0,12.0); lcm(15.0,12.0).parent() 60 Integer Ring If this fails, we resort to the default we see above:: sage: lcm(6.0*CC.0,8*CC.0); lcm(6.0*CC.0,8*CC.0).parent() 1.00000000000000 Complex Field with 53 bits of precision sage: lcm(15.2,12.0) 1.00000000000000 AUTHOR: - Simon King (2011-02): Trac ticket #10771 """ P = self.parent() try: other = P(other) except (TypeError, ValueError): raise ArithmeticError("The second argument can not be interpreted in the parent of the first argument. Can't compute the lcm") from sage.rings.integer_ring import ZZ if ZZ.is_subring(P): try: return ZZ(self).lcm(ZZ(other)) except TypeError: pass # there is no custom lcm, so, we resort to something that always exists if self==0 or other==0: return P.zero() return P.one()
def encode(self, m, S): ''' encodes a vector m (in ZZ^n) to index set S ''' c = Zmod(self.x0)(0) for i in range(self.n): r_i = ZZ.random_element(2**self.rho) c += Zmod(self.x0)((m[i] + self.g[i] * r_i) * self.coeff[i]) zinv = prod([self.zinv[i] for i in S]) return Zmod(self.x0)(c * zinv)
def simple_reflection(self, i): r""" Return the ``i``-th simple reflection of ``self``. EXAMPLES:: sage: S = SignedPermutations(4) sage: S.simple_reflection(1) [2, 1, 3, 4] sage: S.simple_reflection(4) [1, 2, 3, -4] """ if i not in self.index_set(): raise ValueError("i must be in the index set") if i < self._n: p = list(range(1, self._n + 1)) p[i - 1] = i + 1 p[i] = i return self.element_class(self, [ZZ.one()] * self._n, self._P(p)) temp = [ZZ.one()] * self._n temp[-1] = -ZZ.one() return self.element_class(self, temp, self._P.identity())
def coeff(m): m = ZZ(m) if m < 0: return ZZ(0) elif m == 0: return ZZ(1) factor = -2*k / QQ(bernoulli(k)) / lamk sum1 = sigma(m, k-1) if M.divides(m): sum2 = (lamk-1) * sigma(ZZ(m/M), k-1) else: sum2 = ZZ(0) if (M == 1): sum3 = ZZ(0) else: if (m == 1): N = ZZ(1) else: N = ZZ(m / M**ZZ(m.valuation(M))) sum3 = -sigma(ZZ(N), k-1) * ZZ(m/N)**(k-1) / (lamk + 1) return factor * (sum1 + sum2 + sum3) * dval**m
def my_level1_UpGj(p, k, m): r""" Returns a square matrix `A` over ``IntegerRing(p^m)``. The matrix `A` is the finite square matrix which occurs on input p,k and m in Step 6 of Algorithm 1 in [Lau2011]_. Notational change from paper: In Step 1 following Wan we defined j by `k = k_0 + j(p-1)` with `0 \le k_0 < p-1`. Here we replace j by ``kdiv`` so that we may use j as a column index for matrices. INPUT: - ``p`` -- prime at least 5. - ``k`` -- the weight. - ``m`` -- positive integer. OUTPUT: - the matrix A and the matrix E. """ # Step 1 t = cputime() if k == 1: k0 = p else: k0 = k % (p - 1) n = floor(((p + 1) / (p - 1)) * (m + 1)) ell = dimension_modular_forms(1, k0 + n * (p - 1)) ellp = ell * p mdash = m + ceil(n / (p + 1)) verbose("done step 1", t) t = cputime() # Steps 2 and 3 e, Ep1 = katz_expansions(k0, p, ellp, mdash, n) verbose("done steps 2+3", t) t = cputime() # Step 4 G = compute_G(p, Ep1) verbose("done step 4a", t) t = cputime() k = ZZ(k) # convert to sage integer if k == 1: kdiv = -1 else: kdiv = k // (p - 1) Gkdiv = G**kdiv u = [] for i in range(0, ell): ei = e[i] ui = Gkdiv * ei u.append(ui) verbose("done step 4b", t) t = cputime() # Step 5 and computation of T in Step 6 S = e[0][0].parent() T = matrix(S, ell, ell) for i in range(0, ell): for j in range(0, ell): T[i, j] = u[i][p * j] verbose("done step 5", t) t = cputime() # Step 6: solve T = AE using fact E is upper triangular. # Warning: assumes that T = AE (rather than pT = AE) has # a solution over Z/(p^mdash). This has always been the case in # examples computed by the author, see Note 3.1. A = matrix(S, ell, ell) verbose("solving a square matrix problem of dimension %s" % ell, t) #A = solve_XAB_echelon(e.submatrix(0,0,nrows=ell,ncols=ell), T) for i in range(ell): Ti = T[i] for j in range(ell): ej = Ti.parent()([e[j][l] for l in range(0, ell)]) lj = ZZ(ej[j]) A[i, j] = S(ZZ(Ti[j]) / lj) Ti = Ti - A[i, j] * ej A = MatrixSpace(Zmod(p**m), ell, ell)(A) verbose("done step 6", t) e = Matrix(ZZ, ell, ell, [e[j][l] for j in range(ell) for l in range(ell)]) return A, e, ell, mdash
def make_conductor(ecnfdata, hfield): N, c, d = [ZZ(c) for c in ecnfdata['conductor_ideal'][1:-1].split(',')] return hfield.K().ideal([N // d, c + d * hfield.K().gen()])
def __init__(self, v, R=None, weight=2, character=None): if R is None: R = v[0].parent() self._qexp = [R(o) for o in v] self._weight = ZZ(weight) self._character = character
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False, return_pari_objects=True): r""" Enumerates *all* totally real fields of degree ``n`` with discriminant at most ``B``, primitive or otherwise. INPUT: - ``n`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] sage: enumerate_totallyreal_fields_all(1, 10) [[1, x - 1]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: enumerate_totallyreal_fields_all(2, 10) [[5, x^2 - x - 1], [8, x^2 - 2]] sage: type(enumerate_totallyreal_fields_all(2, 10)[0][1]) <type 'cypari2.gen.Gen'> sage: enumerate_totallyreal_fields_all(2, 10, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0, 0, 0, 0] if len(divisors(n)) > 4: raise ValueError("Only implemented for n = p*q with p,q prime") for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim( d, int(math.floor((1. * B)**(1. * d / n))), verbose=verbose) for i in range(len(Sds)): if verbose: print("=" * 80) print("Taking F =", Sds[i][1]) F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n / d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(4): counts[i] += T[0][i] S += [[t[0], pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0], t[1]] for t in T] j = i + 1 for E in enumerate_totallyreal_fields_prim( n / d, int( math.floor((1. * B)**(1. / d) / (1. * Sds[i][0])**(n * 1. / d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append( [EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort(key=lambda x: (x[0], [QQ(x) for x in x[1].polrecip().Vec()])) weed_fields(S) # Output. if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print("=" * 80) print("Polynomials tested: {}".format(counts[0])) print("Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print("Irreducible polynomials: {}".format(counts[2])) print("Polynomials with nfdisc <= B: {}".format(counts[3])) for i in range(len(S)): print(S[i]) if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [[ZZ(_) for _ in counts], [[ZZ(s[0]), [QQ(_) for _ in s[1].polrecip().Vec()]] for s in S]] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[ZZ(s[0]), Px([QQ(_) for _ in s[1].list()])] for s in S]
def __init__(self, E, P, proof=None, algorithm="pari", globally=False): r""" Initializes the reduction data for the elliptic curve `E` at the prime `P`. INPUT: - ``E`` -- an elliptic curve defined over a number field, or `\QQ`. - ``P`` -- a prime ideal of the field, or a prime integer if the field is `\QQ`. - ``proof`` (bool)-- if True, only use provably correct methods (default controlled by global proof module). Note that the proof module is number_field, not elliptic_curves, since the functions that actually need the flag are in number fields. - ``algorithm`` (string, default: "pari") -- Ignored unless the base field is `\QQ`. If "pari", use the PARI C-library ``ellglobalred`` implementation of Tate's algorithm over `\QQ`. If "generic", use the general number field implementation. - ``globally`` (bool, default: False) -- If True, the algorithm uses the generators of principal ideals rather than an arbitrary uniformizer. .. note:: This function is not normally called directly by users, who may access the data via methods of the EllipticCurve classes. EXAMPLES:: sage: from sage.schemes.elliptic_curves.ell_local_data import EllipticCurveLocalData sage: E = EllipticCurve('14a1') sage: EllipticCurveLocalData(E,2) Local data at Principal ideal (2) of Integer Ring: Reduction type: bad non-split multiplicative Local minimal model: Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field Minimal discriminant valuation: 6 Conductor exponent: 1 Kodaira Symbol: I6 Tamagawa Number: 2 :: sage: EllipticCurveLocalData(E,2,algorithm="generic") Local data at Principal ideal (2) of Integer Ring: Reduction type: bad non-split multiplicative Local minimal model: Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field Minimal discriminant valuation: 6 Conductor exponent: 1 Kodaira Symbol: I6 Tamagawa Number: 2 :: sage: EllipticCurveLocalData(E,2,algorithm="pari") Local data at Principal ideal (2) of Integer Ring: Reduction type: bad non-split multiplicative Local minimal model: Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field Minimal discriminant valuation: 6 Conductor exponent: 1 Kodaira Symbol: I6 Tamagawa Number: 2 :: sage: EllipticCurveLocalData(E,2,algorithm="unknown") Traceback (most recent call last): ... ValueError: algorithm must be one of 'pari', 'generic' :: sage: EllipticCurveLocalData(E,3) Local data at Principal ideal (3) of Integer Ring: Reduction type: good Local minimal model: Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field Minimal discriminant valuation: 0 Conductor exponent: 0 Kodaira Symbol: I0 Tamagawa Number: 1 :: sage: EllipticCurveLocalData(E,7) Local data at Principal ideal (7) of Integer Ring: Reduction type: bad split multiplicative Local minimal model: Elliptic Curve defined by y^2 + x*y + y = x^3 + 4*x - 6 over Rational Field Minimal discriminant valuation: 3 Conductor exponent: 1 Kodaira Symbol: I3 Tamagawa Number: 3 """ self._curve = E K = E.base_field() p = check_prime(K, P) # error handling done in that function if algorithm != "pari" and algorithm != "generic": raise ValueError("algorithm must be one of 'pari', 'generic'") self._reduction_type = None if K is QQ: self._prime = ZZ.ideal(p) else: self._prime = p if algorithm == "pari" and K is QQ: Eint = E.integral_model() data = Eint.pari_curve().elllocalred(p) self._fp = data[0].sage() self._KS = KodairaSymbol(data[1].sage()) self._cp = data[3].sage() # We use a global minimal model since we can: self._Emin_reduced = Eint.minimal_model() self._val_disc = self._Emin_reduced.discriminant().valuation(p) if self._fp > 0: self._reduction_type = Eint.ap(p) # = 0,-1 or +1 else: self._Emin, ch, self._val_disc, self._fp, self._KS, self._cp, self._split = self._tate( proof, globally) if self._fp > 0: if self._Emin.c4().valuation(p) > 0: self._reduction_type = 0 elif self._split: self._reduction_type = +1 else: self._reduction_type = -1
def Lpvalue(f, g, h, p, prec, N=None, modformsring=False, weightbound=6, eps=None, orthogonal_form=None, magma_args=None, force_computation=False, derivative_order=2): if magma_args is None: magma_args = {} from sage.interfaces.magma import Magma magma = Magma(**magma_args) 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())]) N = N.prime_to_m_part(p) print("Tame level N = %s, prime p = %s" % (N, p)) prec = ZZ(prec) print("Step 1: Compute the Up matrix") # A, eimat, elldash, mdash = UpOperator(p, N, k=kk, m=prec, modformsring=modformsring, weightbound=weightbound) computation_name = '%s_%s_%s_%s_%s' % (p, N, kk, prec, 'triv' if eps is None else 'char') 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 Apow, ord_basis, eimat, zetapm, elldash, mdash = db( 'Lpvalue_Apow_ordbasis_eimat_%s' % computation_name) except IOError: if force_computation or not os.path.exists(tmp_filename): if eps is not None: eps_magma = sage_character_to_magma(eps, magma=magma) Am, zetapm, eimatm, elldash, mdash = magma.UpOperatorData( p, eps_magma, kk, prec, nvals=5) else: Am, zetapm, eimatm, elldash, mdash = magma.UpOperatorData( p, N, kk, prec, nvals=5) print(" ..Converting to Sage...") Amodulus = Am[1, 1].Parent().Modulus().sage() Arows = Am.NumberOfRows().sage() Acols = Am.NumberOfColumns().sage() Emodulus = eimatm[1, 1].Parent().Modulus().sage() Erows = eimatm.NumberOfRows().sage() Ecols = eimatm.NumberOfColumns().sage() magma.eval('F := Open("%s", "w");' % tmp_filename) magma.eval('fprintf F, "Matrix(Zmod(%s),%s, %s, "' % (Amodulus, Arows, Acols)) #%%o) \\n", ElementToSequence(%s)'%Am.name()) magma.eval('fprintf F, "%%o", ElementToSequence(%s)' % Am.name()) magma.eval('fprintf F, ") \\n"') magma.eval( 'fprintf F, "Matrix(Zmod(%s),%s, %s, "' % (Emodulus, Erows, Ecols)) #%%o) \\n", ElementToSequence(%s)'%eimatm.name()) magma.eval('fprintf F, "%%o", ElementToSequence(%s)' % eimatm.name()) magma.eval('fprintf F, ") \\n"') magma.eval('fprintf F, "%%o\\n", %s' % zetapm.name()) magma.eval('fprintf F, "%%o\\n", %s' % elldash.name()) magma.eval('fprintf F, "%%o\\n", %s' % mdash.name()) magma.eval('delete F;') # zetapm, elldash, mdash = zetapm.sage(), elldash.sage(), mdash.sage() 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 = sage_eval(fmagma.readline(), preparse=False) eimat = sage_eval(fmagma.readline(), preparse=False) zetapm = sage_eval(fmagma.readline()) elldash = sage_eval(fmagma.readline()) mdash = sage_eval(fmagma.readline()) print("Step 3b: Apply Up^(r-1) to H") V = list(find_Apow_and_ord(A, eimat, p, prec)) Apow, ord_basis = V V.extend([eimat, zetapm, elldash, mdash]) db_save(V, 'Lpvalue_Apow_ordbasis_eimat_%s' % computation_name) from posix import remove remove(tmp_filename) print("Step 2: p-depletion, Coleman primitive, and multiply") H = depletion_coleman_multiply(g, h, p, p * elldash, t=0) print("Step 3a: Compute Up(H)") UpH = vector([H(p * n) for n in range(elldash)]) try: Emat = eimat.apply_map(lambda x: x.lift()).submatrix(0, 0, ncols=elldash) except AttributeError: Emat = eimat.submatrix(0, 0, ncols=elldash) alphas = solve_xAb_echelon(Emat, UpH) Hord = (alphas * Apow.apply_map(lambda x: x.lift())) * Emat Hord = Hord.change_ring(Apow.parent().base_ring()) print("Step 4: Project onto f-component") R = Qp(p, prec) if orthogonal_form is None: ell, piHord = project_onto_eigenspace(f, ord_basis, Hord.change_ring(R), kk, N * p, eps) n = 1 while f[n] == 0: n += 1 Lpa = R(piHord[n]) / R(f[n]) else: ell, piHord = project_onto_eigenspace( f, ord_basis, Hord.change_ring(R), kk, N * p, eps, derivative_order=derivative_order) 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] n = 1 while f[n] == 0: n += 1 Lpa = Lpa / f[n] return Lpa, ell
def BinaryQF_reduced_representatives(D, primitive_only=False): r""" Returns a list of inequivalent reduced representatives for the equivalence classes of positive definite binary forms of discriminant D. INPUT: - `D` -- (integer) A negative discriminant. - ``primitive_only`` -- (bool, default False) flag controlling whether only primitive forms are included. OUTPUT: (list) A lexicographically-ordered list of inequivalent reduced representatives for the equivalence classes of positive definite binary forms of discriminant `D`. If ``primitive_only`` is ``True`` then imprimitive forms (which only exist when `D` is not fundamental) are omitted; otherwise they are included. EXAMPLES:: sage: BinaryQF_reduced_representatives(-4) [x^2 + y^2] sage: BinaryQF_reduced_representatives(-163) [x^2 + x*y + 41*y^2] sage: BinaryQF_reduced_representatives(-12) [x^2 + 3*y^2, 2*x^2 + 2*x*y + 2*y^2] sage: BinaryQF_reduced_representatives(-16) [x^2 + 4*y^2, 2*x^2 + 2*y^2] sage: BinaryQF_reduced_representatives(-63) [x^2 + x*y + 16*y^2, 2*x^2 - x*y + 8*y^2, 2*x^2 + x*y + 8*y^2, 3*x^2 + 3*x*y + 6*y^2, 4*x^2 + x*y + 4*y^2] The number of inequivalent reduced binary forms with a fixed negative fundamental discriminant D is the class number of the quadratic field `Q(\sqrt{D})`:: sage: len(BinaryQF_reduced_representatives(-13*4)) 2 sage: QuadraticField(-13*4, 'a').class_number() 2 sage: p=next_prime(2^20); p 1048583 sage: len(BinaryQF_reduced_representatives(-p)) 689 sage: QuadraticField(-p, 'a').class_number() 689 sage: BinaryQF_reduced_representatives(-23*9) [x^2 + x*y + 52*y^2, 2*x^2 - x*y + 26*y^2, 2*x^2 + x*y + 26*y^2, 3*x^2 + 3*x*y + 18*y^2, 4*x^2 - x*y + 13*y^2, 4*x^2 + x*y + 13*y^2, 6*x^2 - 3*x*y + 9*y^2, 6*x^2 + 3*x*y + 9*y^2, 8*x^2 + 7*x*y + 8*y^2] sage: BinaryQF_reduced_representatives(-23*9, primitive_only=True) [x^2 + x*y + 52*y^2, 2*x^2 - x*y + 26*y^2, 2*x^2 + x*y + 26*y^2, 4*x^2 - x*y + 13*y^2, 4*x^2 + x*y + 13*y^2, 8*x^2 + 7*x*y + 8*y^2] TESTS:: sage: BinaryQF_reduced_representatives(5) Traceback (most recent call last): ... ValueError: discriminant must be negative and congruent to 0 or 1 modulo 4 """ D = ZZ(D) if not (D < 0 and (D % 4 in [0, 1])): raise ValueError( "discriminant must be negative and congruent to 0 or 1 modulo 4") # For a fundamental discriminant all forms are primitive so we need not check: if primitive_only: primitive_only = not is_fundamental_discriminant(D) form_list = [] from sage.misc.all import xsrange from sage.rings.arith import gcd # Only iterate over positive a and over b of the same # parity as D such that 4a^2 + D <= b^2 <= a^2 for a in xsrange(1, 1 + ((-D) // 3).isqrt()): a4 = 4 * a s = D + a * a4 w = 1 + (s - 1).isqrt() if s > 0 else 0 if w % 2 != D % 2: w += 1 for b in xsrange(w, a + 1, 2): t = b * b - D if t % a4 == 0: c = t // a4 if (not primitive_only) or gcd([a, b, c]) == 1: if b > 0 and a > b and c > a: form_list.append(BinaryQF([a, -b, c])) form_list.append(BinaryQF([a, b, c])) form_list.sort() return form_list
def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of cusp forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of forms of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES: We compute the same dimension in two different ways :: sage: K = CyclotomicField(3) sage: eps = DirichletGroup(7*43,K).0^2 sage: G = Gamma1(7*43) Via Cohen--Oesterle:: sage: Gamma1(7*43).dimension_cusp_forms(2, eps) 28 Via Quer's method:: sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer") 28 Some more examples:: sage: G.<eps> = DirichletGroup(9) sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]] [0, 0, 1, 0, 3, 0, 5, 0, 7, 0] sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]] [0, 0, 0, 2, 0, 4, 0, 6, 0, 8] """ from .all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_cusp_forms(self, k) N = self.level() K = eps.base_ring() eps = DirichletGroup(N, K)(eps) if K.characteristic() != 0: raise NotImplementedError('dimension_cusp_forms() is only implemented for rings of characteristic 0') if eps.is_trivial(): return Gamma0(N).dimension_cusp_forms(k) if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()): return ZZ(0) if k == 1: try: n = self.dimension_cusp_forms(1) if n == 0: return ZZ(0) else: # never happens at present raise NotImplementedError("Computations of dimensions of spaces of weight 1 cusp forms not implemented at present") except NotImplementedError: raise # now the main part if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N,(eps**d).kernel()) dim = dim + moebius(d)*G.dimension_cusp_forms(k) return dim//phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) ) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_cusp_forms")
def lift_map(target): """ Create a lift map, to be used for lifting the cross ratios of a matroid representation. .. SEEALSO:: :meth:`lift_cross_ratios() <sage.matroids.utilities.lift_cross_ratios>` INPUT: - ``target`` -- a string describing the target (partial) field. OUTPUT: - a dictionary Depending on the value of ``target``, the following lift maps will be created: - "reg": a lift map from `\GF3` to the regular partial field `(\ZZ, <-1>)`. - "sru": a lift map from `\GF7` to the sixth-root-of-unity partial field `(\QQ(z), <z>)`, where `z` is a sixth root of unity. The map sends 3 to `z`. - "dyadic": a lift map from `\GF{11}` to the dyadic partial field `(\QQ, <-1, 2>)`. - "gm": a lift map from `\GF{19}` to the golden mean partial field `(\QQ(t), <-1,t>)`, where `t` is a root of `t^2-t-1`. The map sends `5` to `t`. The example below shows that the latter map satisfies three necessary conditions stated in :meth:`lift_cross_ratios() <sage.matroids.utilities.lift_cross_ratios>` EXAMPLES:: sage: from sage.matroids.utilities import lift_map sage: lm = lift_map('gm') sage: for x in lm: ....: if (x == 1) is not (lm[x] == 1): ....: print 'not a proper lift map' ....: for y in lm: ....: if (x+y == 0) and not (lm[x]+lm[y] == 0): ....: print 'not a proper lift map' ....: if (x+y == 1) and not (lm[x]+lm[y] == 1): ....: print 'not a proper lift map' ....: for z in lm: ....: if (x*y==z) and not (lm[x]*lm[y]==lm[z]): ....: print 'not a proper lift map' """ if target == "reg": R = GF(3) return {R(1): ZZ(1)} if target == "sru": R = GF(7) z = ZZ['z'].gen() S = NumberField(z * z - z + 1, 'z') return {R(1): S(1), R(3): S(z), R(3)**(-1): S(z)**5} if target == "dyadic": R = GF(11) return {R(1): QQ(1), R(-1): QQ(-1), R(2): QQ(2), R(6): QQ(1 / 2)} if target == "gm": R = GF(19) t = QQ['t'].gen() G = NumberField(t * t - t - 1, 't') return { R(1): G(1), R(5): G(t), R(1) / R(5): G(1) / G(t), R(-5): G(-t), R(-5)**(-1): G(-t)**(-1), R(5)**2: G(t)**2, R(5)**(-2): G(t)**(-2) } raise NotImplementedError(target)
def __init__(self, coxeter_matrix, base_ring, index_set): """ Initialize ``self``. EXAMPLES:: sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]]) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar) sage: TestSuite(W).run() # long time sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]]) sage: TestSuite(W).run(max_runs=30) # long time sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]]) sage: TestSuite(W).run(max_runs=30) # long time We check that :trac:`16630` is fixed:: sage: CoxeterGroup(['D',4], base_ring=QQ).category() Category of finite irreducible coxeter groups sage: CoxeterGroup(['H',4], base_ring=QQbar).category() Category of finite irreducible coxeter groups sage: F = CoxeterGroups().Finite() sage: all(CoxeterGroup([letter,i]) in F ....: for i in range(2,5) for letter in ['A','B','D']) True sage: all(CoxeterGroup(['E',i]) in F for i in range(6,9)) True sage: CoxeterGroup(['F',4]).category() Category of finite irreducible coxeter groups sage: CoxeterGroup(['G',2]).category() Category of finite irreducible coxeter groups sage: all(CoxeterGroup(['H',i]) in F for i in range(3,5)) True sage: all(CoxeterGroup(['I',i]) in F for i in range(2,5)) True """ self._matrix = coxeter_matrix n = coxeter_matrix.rank() # Compute the matrix with entries `2 \cos( \pi / m_{ij} )`. MS = MatrixSpace(base_ring, n, sparse=True) one = MS.one() # FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty E = UniversalCyclotomicField().gen if base_ring is UniversalCyclotomicField(): def val(x): if x == -1: return 2 else: return E(2 * x) + ~E(2 * x) elif is_QuadraticField(base_ring): def val(x): if x == -1: return 2 else: return base_ring((E(2 * x) + ~E(2 * x)).to_cyclotomic_field()) else: from sage.functions.trig import cos from sage.symbolic.constants import pi def val(x): if x == -1: return 2 else: return base_ring(2 * cos(pi / x)) gens = [one + MS([SparseEntry(i, j, val(coxeter_matrix[index_set[i], index_set[j]])) for j in range(n)]) for i in range(n)] # Make the generators dense matrices for consistency and speed gens = [g.dense_matrix() for g in gens] category = CoxeterGroups() # Now we shall see if the group is finite, and, if so, refine # the category to ``category.Finite()``. Otherwise the group is # infinite and we refine the category to ``category.Infinite()``. if self._matrix.is_finite(): category = category.Finite() else: category = category.Infinite() if all(self._matrix._matrix[i, j] == 2 for i in range(n) for j in range(i)): category = category.Commutative() if self._matrix.is_irreducible(): category = category.Irreducible() self._index_set_inverse = {i: ii for ii, i in enumerate(self._matrix.index_set())} FinitelyGeneratedMatrixGroup_generic.__init__(self, ZZ(n), base_ring, gens, category=category)
def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of Eisenstein series forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of Eisenstein series of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). AUTHORS: - William Stein - Cohen--Oesterle algorithm - Jordi Quer - algorithm based on GammaH subgroups - David Loeffler (2009) - code refactoring EXAMPLES: The following two computations use different algorithms:: sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] So do these:: sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] """ from .all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_eis(self, k) N = self.level() K = eps.base_ring() eps = DirichletGroup(N, K)(eps) if eps.is_trivial(): return Gamma0(N).dimension_eis(k) # Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid: if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()): return ZZ(0) if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N,(eps**d).kernel()) dim = dim + moebius(d)*G.dimension_eis(k) return dim//phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle j = 2-k # We use the Cohen-Oesterle formula in a subtle way to # compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on # computing with modular forms). alpha = -ZZ( K(Gamma0(N).index()*(j-1)/ZZ(12)) + CohenOesterle(eps,j) ) if k == 1: return alpha else: return alpha - self.dimension_cusp_forms(k, eps) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_eis")
def an_padic(self, p, prec=0, use_twists=True): r""" Returns the conjectural order of `Sha(E/\QQ)`, according to the `p`-adic analogue of the Birch and Swinnerton-Dyer conjecture as formulated in [MTT]_ and [BP]_. REFERENCES: .. [MTT] \B. Mazur, J. Tate, and J. Teitelbaum, On `p`-adic analogues of the conjectures of Birch and Swinnerton-Dyer, Inventiones mathematicae 84, (1986), 1-48. .. [BP] Dominique Bernardi and Bernadette Perrin-Riou, Variante `p`-adique de la conjecture de Birch et Swinnerton-Dyer (le cas supersingulier), C. R. Acad. Sci. Paris, Sér I. Math., 317 (1993), no. 3, 227-232. INPUT: - ``p`` - a prime > 3 - ``prec`` (optional) - the precision used in the computation of the `p`-adic L-Series - ``use_twists`` (default = ``True``) - If ``True`` the algorithm may change to a quadratic twist with minimal conductor to do the modular symbol computations rather than using the modular symbols of the curve itself. If ``False`` it forces the computation using the modular symbols of the curve itself. OUTPUT: `p`-adic number - that conjecturally equals `\# Sha(E/\QQ)`. If ``prec`` is set to zero (default) then the precision is set so that at least the first `p`-adic digit of conjectural `\# Sha(E/\QQ)` is determined. EXAMPLES: Good ordinary examples:: sage: EllipticCurve('11a1').sha().an_padic(5) # rank 0 1 + O(5^22) sage: EllipticCurve('43a1').sha().an_padic(5) # rank 1 1 + O(5) sage: EllipticCurve('389a1').sha().an_padic(5,4) # rank 2, long time (2s on sage.math, 2011) 1 + O(5^3) sage: EllipticCurve('858k2').sha().an_padic(7) # rank 0, non trivial sha, long time (10s on sage.math, 2011) 7^2 + O(7^24) sage: EllipticCurve('300b2').sha().an_padic(3) # 9 elements in sha, long time (2s on sage.math, 2011) 3^2 + O(3^24) sage: EllipticCurve('300b2').sha().an_padic(7, prec=6) # long time 2 + 7 + O(7^8) Exceptional cases:: sage: EllipticCurve('11a1').sha().an_padic(11) # rank 0 1 + O(11^22) sage: EllipticCurve('130a1').sha().an_padic(5) # rank 1 1 + O(5) Non-split, but rank 0 case (:trac:`7331`):: sage: EllipticCurve('270b1').sha().an_padic(5) # rank 0, long time (2s on sage.math, 2011) 1 + O(5^22) The output has the correct sign:: sage: EllipticCurve('123a1').sha().an_padic(41) # rank 1, long time (3s on sage.math, 2011) 1 + O(41) Supersingular cases:: sage: EllipticCurve('34a1').sha().an_padic(5) # rank 0 1 + O(5^22) sage: EllipticCurve('53a1').sha().an_padic(5) # rank 1, long time (11s on sage.math, 2011) 1 + O(5) Cases that use a twist to a lower conductor:: sage: EllipticCurve('99a1').sha().an_padic(5) 1 + O(5) sage: EllipticCurve('240d3').sha().an_padic(5) # sha has 4 elements here 4 + O(5) sage: EllipticCurve('448c5').sha().an_padic(7,prec=4, use_twists=False) # long time (2s on sage.math, 2011) 2 + 7 + O(7^6) sage: EllipticCurve([-19,34]).sha().an_padic(5) # see trac #6455, long time (4s on sage.math, 2011) 1 + O(5) Test for :trac:`15737`:: sage: E = EllipticCurve([-100,0]) sage: s = E.sha() sage: s.an_padic(13) 1 + O(13^20) """ try: return self.__an_padic[(p, prec)] except AttributeError: self.__an_padic = {} except KeyError: pass E = self.Emin tam = E.tamagawa_product() tors = E.torsion_order()**2 r = E.rank() if r > 0: reg = E.padic_regulator(p) else: if E.is_supersingular(p): reg = vector([Qp(p, 20)(1), 0]) else: reg = Qp(p, 20)(1) if use_twists and p > 2: Et, D = E.minimal_quadratic_twist() # trac 6455 : we have to assure that the twist back is allowed D = ZZ(D) if D % p == 0: D = ZZ(D / p) for ell in D.prime_divisors(): if ell % 2 == 1: if Et.conductor() % ell**2 == 0: D = ZZ(D / ell) ve = valuation(D, 2) de = ZZ((D / 2**ve).abs()) if de % 4 == 3: de = -de Et = E.quadratic_twist(de) # now check individually if we can twist by -1 or 2 or -2 Nmin = Et.conductor() Dmax = de for DD in [-4 * de, 8 * de, -8 * de]: Et = E.quadratic_twist(DD) if Et.conductor() < Nmin and valuation(Et.conductor(), 2) <= valuation(DD, 2): Nmin = Et.conductor() Dmax = DD D = Dmax Et = E.quadratic_twist(D) lp = Et.padic_lseries(p) else: lp = E.padic_lseries(p) D = 1 if r == 0 and D == 1: # short cut for rank 0 curves, we do not # to compute the p-adic L-function, the leading # term will be the L-value divided by the Neron # period. ms = E.modular_symbol(sign=+1, normalize='L_ratio') lstar = ms(0) / E.real_components() bsd = tam / tors if prec == 0: # prec = valuation(lstar/bsd, p) prec = 20 shan = Qp(p, prec=prec + 2)(lstar / bsd) elif E.is_ordinary(p): K = reg.parent() lg = log(K(1 + p)) if (E.is_good(p) or E.ap(p) == -1): if not E.is_good(p): eps = 2 else: eps = (1 - arith.kronecker_symbol(D, p) / lp.alpha())**2 # according to the p-adic BSD this should be equal to the leading term of the p-adic L-series divided by sha: bsdp = tam * reg * eps / tors / lg**r else: r += 1 # exceptional zero eq = E.tate_curve(p) Li = eq.L_invariant() # according to the p-adic BSD (Mazur-Tate-Teitelbaum) # this should be equal to the leading term of the p-adic L-series divided by sha: bsdp = tam * reg * Li / tors / lg**r v = bsdp.valuation() if v > 0: verbose("the prime is irregular for this curve.") # determine how much prec we need to prove at least the # triviality of the p-primary part of Sha if prec == 0: n = max(v, 2) bounds = lp._prec_bounds(n, r + 1) while bounds[r] <= v: n += 1 bounds = lp._prec_bounds(n, r + 1) verbose("set precision to %s" % n) else: n = max(2, prec) not_yet_enough_prec = True while not_yet_enough_prec: lps = lp.series(n, quadratic_twist=D, prec=r + 1) lstar = lps[r] if (lstar != 0) or (prec != 0): not_yet_enough_prec = False else: n += 1 verbose("increased precision to %s" % n) shan = lstar / bsdp elif E.is_supersingular(p): K = reg[0].parent() lg = log(K(1 + p)) # according to the p-adic BSD this should be equal to the leading term of the D_p - valued # L-series : bsdp = tam / tors / lg**r * reg # note this is an element in Q_p^2 verbose("the algebraic leading terms : %s" % bsdp) v = [bsdp[0].valuation(), bsdp[1].valuation()] if prec == 0: n = max(min(v) + 2, 3) else: n = max(3, prec) verbose("...computing the p-adic L-series") not_yet_enough_prec = True while not_yet_enough_prec: lps = lp.Dp_valued_series(n, quadratic_twist=D, prec=r + 1) lstar = [lps[0][r], lps[1][r]] verbose("the leading terms : %s" % lstar) if (lstar[0] != 0 or lstar[1] != 0) or (prec != 0): not_yet_enough_prec = False else: n += 1 verbose("increased precision to %s" % n) verbose("...putting things together") if bsdp[0] != 0: shan0 = lstar[0] / bsdp[0] else: shan0 = 0 # this should actually never happen if bsdp[1] != 0: shan1 = lstar[1] / bsdp[1] else: shan1 = 0 # this should conjecturally only happen when the rank is 0 verbose("the two values for Sha : %s" % [shan0, shan1]) # check consistency (the first two are only here to avoid a bug in the p-adic L-series # (namely the coefficients of zero-relative precision are treated as zero) if shan0 != 0 and shan1 != 0 and shan0 - shan1 != 0: raise RuntimeError( "There must be a bug in the supersingular routines for the p-adic BSD." ) # take the better if shan1 == 0 or shan0.precision_relative( ) > shan1.precision_relative(): shan = shan0 else: shan = shan1 else: raise ValueError( "The curve has to have semi-stable reduction at p.") self.__an_padic[(p, prec)] = shan return shan
def _group_gens(self): r""" Return a set of generators of the group `S(K_0) / S(K_u)` (which is either `{\rm SL}_2(\ZZ / p^u \ZZ)` if the conductor is even, and a quotient of an Iwahori subgroup if the conductor is odd). EXAMPLES:: sage: from sage.modular.local_comp.type_space import example_type_space sage: example_type_space()._group_gens() [[1, 1, 0, 1], [0, -1, 1, 0]] sage: example_type_space(3)._group_gens() [[1, 1, 0, 1], [1, 0, 3, 1], [2, 0, 0, 5]] """ if (self.conductor() % 2) == 0: return [[ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(0), ZZ(-1), ZZ(1), ZZ(0)]] else: p = self.prime() if p == 2: return [[ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(1), ZZ(0), ZZ(p), ZZ(1)]] else: a = Zmod(p**(self.u() + 1))(ZZ(Zmod(p).unit_gens()[0])) return [[ZZ(1), ZZ(1), ZZ(0), ZZ(1)], [ZZ(1), ZZ(0), ZZ(p), ZZ(1)], [ZZ(a), 0, 0, ZZ(~a)]]
def _sage_(self): """ Convert self to a Sage object. EXAMPLES:: sage: a = axiom(1/2); a #optional - axiom 1 - 2 sage: a.sage() #optional - axiom 1/2 sage: _.parent() #optional - axiom Rational Field sage: gp(axiom(1/2)) #optional - axiom 1/2 sage: fricas(1/2).sage() #optional - fricas 1/2 DoubleFloat's in Axiom are converted to be in RDF in Sage. :: sage: axiom(2.0).as_type('DoubleFloat').sage() #optional - axiom 2.0 sage: _.parent() #optional - axiom Real Double Field sage: axiom(2.1234)._sage_() #optional - axiom 2.12340000000000 sage: _.parent() #optional - axiom Real Field with 53 bits of precision sage: a = RealField(100)(pi) sage: axiom(a)._sage_() #optional - axiom 3.1415926535897932384626433833 sage: _.parent() #optional - axiom Real Field with 100 bits of precision sage: axiom(a)._sage_() == a #optional - axiom True sage: axiom(2.0)._sage_() #optional - axiom 2.00000000000000 sage: _.parent() #optional - axiom Real Field with 53 bits of precision We can also convert Axiom's polynomials to Sage polynomials. sage: a = axiom(x^2 + 1) #optional - axiom sage: a.type() #optional - axiom Polynomial Integer sage: a.sage() #optional - axiom x^2 + 1 sage: _.parent() #optional - axiom Univariate Polynomial Ring in x over Integer Ring sage: axiom('x^2 + y^2 + 1/2').sage() #optional - axiom y^2 + x^2 + 1/2 sage: _.parent() #optional - axiom Multivariate Polynomial Ring in y, x over Rational Field """ P = self._check_valid() type = str(self.type()) if type in ["Type", "Domain"]: return self._sage_domain() if type == "Float": from sage.rings.all import RealField, ZZ prec = max(self.mantissa().length()._sage_(), 53) R = RealField(prec) x, e, b = self.unparsed_input_form().lstrip('float(').rstrip( ')').split(',') return R(ZZ(x) * ZZ(b)**ZZ(e)) elif type == "DoubleFloat": from sage.rings.all import RDF return RDF(repr(self)) elif type.startswith('Polynomial'): from sage.rings.all import PolynomialRing base_ring = P(type.lstrip('Polynomial '))._sage_domain() vars = str(self.variables())[1:-1] R = PolynomialRing(base_ring, vars) return R(self.unparsed_input_form()) #If all else fails, try using the unparsed input form try: import sage.misc.sage_eval return sage.misc.sage_eval.sage_eval(self.unparsed_input_form()) except Exception: raise NotImplementedError
def my_levelN_UpGj(p, N, k, m, modformsring, bound): r""" INPUT: - ``p`` -- prime at least 5. - ``N`` -- integer at least 2 and not divisible by p (level). - ``k`` -- the weight of f. - ``m`` -- positive integer. - ``modformsring`` -- True or False. - ``bound`` -- (even) positive integer. OUTPUT: - the matrix A and the matrix E. """ t = cputime() # Step 1 if k == 1: k0 = p else: k0 = k % (p - 1) n = floor(((p + 1) / (p - 1)) * (m + 1)) elldash = compute_elldash(p, N, k0, n) elldashp = elldash * p mdash = m + ceil(n / (p + 1)) verbose("done step 1", t) t = cputime() # Steps 2 and 3 e, Ep1 = my_higher_level_katz_exp(p, N, k0, m, mdash, elldash, elldashp, modformsring, bound) ell = dimension(transpose(e)[0].parent()) S = e[0, 0].parent() verbose("done steps 2+3", t) t = cputime() # Step 4 R = Ep1.parent() G = compute_G(p, Ep1) Alist = [] verbose("done step 4a", t) t = cputime() k = ZZ(k) # convert to sage integer if k == 1: kdiv = -1 else: kdiv = k // (p - 1) Gkdiv = G**kdiv T = matrix(S, ell, elldash) for i in range(ell): ei = R(e[i].list()) Gkdivei = Gkdiv * ei # act by G^kdiv for j in range(0, elldash): T[i, j] = Gkdivei[p * j] verbose("done steps 4b and 5", t) t = cputime() # Step 6: solve T = AE using fact E is upper triangular. # Warning: assumes that T = AE (rather than pT = AE) has # a solution over Z/(p^mdash). This has always been the case in # examples computed by the author, see Note 3.1. A = matrix(S, ell, ell) verbose("solving a square matrix problem of dimension %s" % ell) verbose("elldash is %s" % elldash) for i in range(0, ell): Ti = T[i] for j in range(0, ell): ej = Ti.parent()([e[j][l] for l in range(0, elldash)]) ejleadpos = ej.nonzero_positions()[0] lj = ZZ(ej[ejleadpos]) A[i, j] = S(ZZ(Ti[j]) / lj) Ti = Ti - A[i, j] * ej # A = solve_XAB_echelon(e.submatrix(0,0,nrows=ell,ncols=ell),T.submatrix(0,0,ncols=ell)) A = MatrixSpace(Zmod(p**m), ell, ell)(A) verbose("done step 6", t) return A, e, elldash, mdash
def dimension_new_cusp_forms(self, k=2, p=0): r""" Return the dimension of the space of new (or `p`-new) weight `k` cusp forms for this congruence subgroup. INPUT: - `k` -- an integer (default: 2), the weight. Not fully implemented for `k = 1`. - `p` -- integer (default: 0); if nonzero, compute the `p`-new subspace. OUTPUT: Integer ALGORITHM: This comes from the formula given in Theorem 1 of http://www.math.ubc.ca/~gerg/papers/downloads/DSCFN.pdf EXAMPLES:: sage: Gamma0(11000).dimension_new_cusp_forms() 240 sage: Gamma0(11000).dimension_new_cusp_forms(k=1) 0 sage: Gamma0(22).dimension_new_cusp_forms(k=4) 3 sage: Gamma0(389).dimension_new_cusp_forms(k=2,p=17) 32 TESTS:: sage: L = [1213, 1331, 2169, 2583, 2662, 2745, 3208, ....: 3232, 3465, 3608, 4040, 4302, 4338] sage: all(Gamma0(N).dimension_new_cusp_forms(2)==100 for N in L) True """ from sage.arith.all import moebius from sage.functions.other import floor N = self.level() k = ZZ(k) if not (p == 0 or N % p): return (self.dimension_cusp_forms(k) - 2 * self.restrict(N // p).dimension_new_cusp_forms(k)) if k < 2 or k % 2: return ZZ.zero() factors = list(N.factor()) def s0(q, a): # function s_0^# if a == 1: return 1 - 1 / q elif a == 2: return 1 - 1 / q - 1 / q**2 else: return (1 - 1 / q) * (1 - 1 / q**2) def vinf(q, a): # function v_oo^# if a % 2: return 0 elif a == 2: return q - 2 else: return q**(a / 2 - 2) * (q - 1)**2 def v2(q, a): # function v_2^# if q % 4 == 1: if a == 2: return -1 else: return 0 elif q % 4 == 3: if a == 1: return -2 elif a == 2: return 1 else: return 0 elif a in (1, 2): return -1 elif a == 3: return 1 else: return 0 def v3(q, a): # function v_3^# if q % 3 == 1: if a == 2: return -1 else: return 0 elif q % 3 == 2: if a == 1: return -2 elif a == 2: return 1 else: return 0 elif a in (1, 2): return -1 elif a == 3: return 1 else: return 0 res = (k - 1) / 12 * N * prod(s0(q, a) for q, a in factors) res -= prod(vinf(q, a) for q, a in factors) / ZZ(2) res += ( (1 - k) / 4 + floor(k / 4)) * prod(v2(q, a) for q, a in factors) res += ( (1 - k) / 3 + floor(k / 3)) * prod(v3(q, a) for q, a in factors) if k == 2: res += moebius(N) return res
def S_to_Q(self,S,Q): r""" Given $S$ a point on self over an extension field, computes the Coleman integrals $\{\int_S^Q x^i dx/2y \}_{i=0}^{2g-1}$ **one should be able to feed $S,Q$ into coleman_integral, but currently that segfaults** INPUT: - S: a point with coordinates in an extension of $\Q_p$ (with unif. a) - Q: a non-Weierstrass point defined over $\Q_p$ OUTPUT: the Coleman integrals $\{\int_S^Q x^i dx/2y \}_{i=0}^{2g-1}$ in terms of $a$ EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^3-10*x+9) sage: K = Qp(5,6) sage: HK = H.change_ring(K) sage: J.<a> = K.extension(x^20-5) sage: HJ = H.change_ring(J) sage: w = HK.invariant_differential() sage: x,y = HK.monsky_washnitzer_gens() sage: P = HK(1,0) sage: Q = HK(0,3) sage: S = HK.get_boundary_point(HJ,P) sage: P_to_S = HK.P_to_S(P,S) sage: S_to_Q = HJ.S_to_Q(S,Q) sage: P_to_S + S_to_Q (2*a^40 + a^80 + a^100 + O(a^105), a^20 + 2*a^40 + 4*a^60 + 2*a^80 + O(a^105)) sage: HK.coleman_integrals_on_basis(P,Q) (2*5^2 + 5^4 + 5^5 + 3*5^6 + O(5^7), 5 + 2*5^2 + 4*5^3 + 2*5^4 + 5^6 + O(5^7)) AUTHOR: - Jennifer Balakrishnan """ FS = self.frobenius(S) FS = (FS[0],FS[1]) FQ = self.frobenius(Q) import sage.schemes.elliptic_curves.monsky_washnitzer as monsky_washnitzer try: M_frob, forms = self._frob_calc except AttributeError: M_frob, forms = self._frob_calc = monsky_washnitzer.matrix_of_frobenius_hyperelliptic(self) try: HJ = self._curve_over_ram_extn K = HJ.base_ring() except AttributeError: HJ = S.scheme() K = self.base_ring() g = self.genus() prec2 = K.precision_cap() p = K.prime() dim = 2*g V = VectorSpace(K,dim) if S == FS: S_to_FS = V(dim*[0]) else: P = self(ZZ(FS[0][0]),ZZ(FS[1][0])) x,y = self.local_coord(P,prec2) integrals = [(x**i*x.derivative()/(2*y)).integral() for i in range(dim)] S_to_FS = vector([I(FS[1])-I(S[1]) for I in integrals]) if HJ(Q[0],Q[1]) == HJ(FQ): FQ_to_Q = V(dim*[0]) else: FQ_to_Q = V(self.tiny_integrals_on_basis(FQ, Q)) try: L = [f(K(S[0]), K(S[1])) - f(K(Q[0]), K(Q[1])) for f in forms] except ValueError: forms = [f.change_ring(K) for f in forms] L = [f(S[0], S[1]) - f(Q[0], Q[1]) for f in forms] b = V(L) M_sys = matrix(K, M_frob).transpose() - 1 B = (~M_sys) v = [B.list()[i].valuation() for i in range(len(B.list()))] vv= min(v) B = (p**(-vv)*B).change_ring(K) B = p**(vv)*B return B*(b-S_to_FS-FQ_to_Q)
class ModularFormsAmbient_R(ambient.ModularFormsAmbient): def __init__(self, M, base_ring): """ Ambient space of modular forms over a ring other than QQ. EXAMPLES:: sage: M = ModularForms(23,2,base_ring=GF(7)) ## indirect doctest sage: M Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(23) of weight 2 over Finite Field of size 7 sage: M == loads(dumps(M)) True """ self.__M = M if M.character() is not None: self.__R_character = M.character().change_ring(base_ring) else: self.__R_character = None ambient.ModularFormsAmbient.__init__(self, M.group(), M.weight(), base_ring, M.character()) @cached_method( key=lambda self, sign: ZZ(sign) ) # convert sign to an Integer before looking this up in the cache def modular_symbols(self, sign=0): r""" Return the space of modular symbols attached to this space, with the given sign (default 0). TESTS:: sage: K.<i> = QuadraticField(-1) sage: chi = DirichletGroup(5, base_ring = K).0 sage: L.<c> = K.extension(x^2 - 402*i) sage: M = ModularForms(chi, 7, base_ring = L) sage: symbs = M.modular_symbols() sage: symbs.character() == chi True sage: symbs.base_ring() == L True """ sign = ZZ(sign) return self.__M.modular_symbols(sign).change_ring(self.base_ring()) def _repr_(self): """ String representation for self. EXAMPLES:: sage: M = ModularForms(23,2,base_ring=GF(7)) ## indirect doctest sage: M._repr_() 'Modular Forms space of dimension 3 for Congruence Subgroup Gamma0(23) of weight 2 over Finite Field of size 7' sage: chi = DirichletGroup(109).0 ** 36 sage: ModularForms(chi, 2, base_ring = chi.base_ring()) Modular Forms space of dimension 9, character [zeta3] and weight 2 over Cyclotomic Field of order 108 and degree 36 """ s = str(self.__M) i = s.find('over') if i != -1: s = s[:i] return s + 'over %s' % self.base_ring() def _compute_q_expansion_basis(self, prec=None): """ Compute q-expansions for a basis of self to precision prec. EXAMPLES:: sage: M = ModularForms(23,2,base_ring=GF(7)) sage: M._compute_q_expansion_basis(10) [q + 6*q^3 + 6*q^4 + 5*q^6 + 2*q^7 + 6*q^8 + 2*q^9 + O(q^10), q^2 + 5*q^3 + 6*q^4 + 2*q^5 + q^6 + 2*q^7 + 5*q^8 + O(q^10), 1 + 5*q^3 + 5*q^4 + 5*q^6 + 3*q^8 + 5*q^9 + O(q^10)] TESTS: This checks that :trac:`13445` is fixed:: sage: M = ModularForms(Gamma1(29), base_ring=GF(29)) sage: S = M.cuspidal_subspace() sage: 0 in [f.valuation() for f in S.basis()] False sage: len(S.basis()) == dimension_cusp_forms(Gamma1(29), 2) True """ if prec is None: prec = self.prec() R = self._q_expansion_ring() c = self.base_ring().characteristic() if c == 0: B = self.__M.q_expansion_basis(prec) return [R(f) for f in B] elif c.is_prime_power(): K = self.base_ring() p = K.characteristic().prime_factors()[0] from sage.rings.all import GF Kp = GF(p) newB = [ f.change_ring(K) for f in list( self.__M.cuspidal_subspace().q_integral_basis(prec)) ] A = Kp**prec gens = [f.padded_list(prec) for f in newB] V = A.span(gens) B = [f.change_ring(K) for f in self.__M.q_integral_basis(prec)] for f in B: fc = f.padded_list(prec) gens.append(fc) if not A.span(gens) == V: newB.append(f) V = A.span(gens) if len(newB) != self.dimension(): raise RuntimeError( "The dimension of the space is %s but the basis we computed has %s elements" % (self.dimension(), len(newB))) lst = [R(f) for f in newB] return [f / f[f.valuation()] for f in lst] else: # this returns a basis of q-expansions, without guaranteeing that # the first vectors form a basis of the cuspidal subspace # TODO: bring this in line with the other cases # simply using the above code fails because free modules over # general rings do not have a .span() method B = self.__M.q_integral_basis(prec) return [R(f) for f in B] def cuspidal_submodule(self): r""" Return the cuspidal subspace of this space. EXAMPLES:: sage: C = CuspForms(7, 4, base_ring=CyclotomicField(5)) # indirect doctest sage: type(C) <class 'sage.modular.modform.cuspidal_submodule.CuspidalSubmodule_R_with_category'> """ return CuspidalSubmodule_R(self) def change_ring(self, R): r""" Return this modular forms space with the base ring changed to the ring R. EXAMPLES:: sage: chi = DirichletGroup(109, CyclotomicField(3)).0 sage: M9 = ModularForms(chi, 2, base_ring = CyclotomicField(9)) sage: M9.change_ring(CyclotomicField(15)) Modular Forms space of dimension 10, character [zeta3 + 1] and weight 2 over Cyclotomic Field of order 15 and degree 8 sage: M9.change_ring(QQ) Traceback (most recent call last): ... ValueError: Space cannot be defined over Rational Field """ if not R.has_coerce_map_from(self.__M.base_ring()): raise ValueError("Space cannot be defined over %s" % R) return ModularFormsAmbient_R(self.__M, R)
def q_binomial(n, k, q=None, algorithm='auto'): r""" Return the `q`-binomial coefficient. This is also known as the Gaussian binomial coefficient, and is defined by .. MATH:: \binom{n}{k}_q = \frac{(1-q^n)(1-q^{n-1}) \cdots (1-q^{n-k+1})} {(1-q)(1-q^2)\cdots (1-q^k)}. See :wikipedia:`Gaussian_binomial_coefficient`. If `q` is unspecified, then the variable is the generator `q` for a univariate polynomial ring over the integers. INPUT: - ``n, k`` -- the values `n` and `k` defined above - ``q`` -- (default: ``None``) the variable `q`; if ``None``, then use a default variable in `\ZZ[q]` - ``algorithm`` -- (default: ``'auto'``) the algorithm to use and can be one of the following: - ``'auto'`` -- automatically choose the algorithm; see the algorithm section below - ``'naive'`` -- use the naive algorithm - ``'cyclotomic'`` -- use cyclotomic algorithm ALGORITHM: The naive algorithm uses the product formula. The cyclotomic algorithm uses a product of cyclotomic polynomials (cf. [CH2006]_). When the algorithm is set to ``'auto'``, we choose according to the following rules: - If ``q`` is a polynomial: When ``n`` is small or ``k`` is small with respect to ``n``, one uses the naive algorithm. When both ``n`` and ``k`` are big, one uses the cyclotomic algorithm. - If ``q`` is in the symbolic ring, one uses the cyclotomic algorithm. - Otherwise one uses the naive algorithm, unless ``q`` is a root of unity, then one uses the cyclotomic algorithm. EXAMPLES: By default, the variable is the generator of `\ZZ[q]`:: sage: from sage.combinat.q_analogues import q_binomial sage: g = q_binomial(5,1) ; g q^4 + q^3 + q^2 + q + 1 sage: g.parent() Univariate Polynomial Ring in q over Integer Ring The `q`-binomial coefficient vanishes unless `0 \leq k \leq n`:: sage: q_binomial(4,5) 0 sage: q_binomial(5,-1) 0 Other variables can be used, given as third parameter:: sage: p = ZZ['p'].gen() sage: q_binomial(4,2,p) p^4 + p^3 + 2*p^2 + p + 1 The third parameter can also be arbitrary values:: sage: q_binomial(5,1,2) == g.subs(q=2) True sage: q_binomial(5,1,1) 5 sage: q_binomial(4,2,-1) 2 sage: q_binomial(4,2,3.14) 152.030056160000 sage: R = GF(25, 't') sage: t = R.gen(0) sage: q_binomial(6, 3, t) 2*t + 3 We can also do this for more complicated objects such as matrices or symmetric functions:: sage: q_binomial(4,2,matrix([[2,1],[-1,3]])) [ -6 84] [-84 78] sage: Sym = SymmetricFunctions(QQ) sage: s = Sym.schur() sage: q_binomial(4,1, s[2]+s[1]) s[] + s[1] + s[1, 1] + s[1, 1, 1] + 2*s[2] + 4*s[2, 1] + 3*s[2, 1, 1] + 4*s[2, 2] + 3*s[2, 2, 1] + s[2, 2, 2] + 3*s[3] + 7*s[3, 1] + 3*s[3, 1, 1] + 6*s[3, 2] + 2*s[3, 2, 1] + s[3, 3] + 4*s[4] + 6*s[4, 1] + s[4, 1, 1] + 3*s[4, 2] + 3*s[5] + 2*s[5, 1] + s[6] TESTS: One checks that the first two arguments are integers:: sage: q_binomial(1/2,1) Traceback (most recent call last): ... TypeError: no conversion of this rational to integer One checks that `n` is nonnegative:: sage: q_binomial(-4,1) Traceback (most recent call last): ... ValueError: n must be nonnegative This also works for variables in the symbolic ring:: sage: z = var('z') sage: factor(q_binomial(4,2,z)) (z^2 + z + 1)*(z^2 + 1) This also works for complex roots of unity:: sage: q_binomial(6, 1, QQbar(I)) I + 1 Note that the symbolic computation works (see :trac:`14982`):: sage: q_binomial(6, 1, I) I + 1 Check that the algorithm does not matter:: sage: q_binomial(6,3, algorithm='naive') == q_binomial(6,3, algorithm='cyclotomic') True One more test:: sage: q_binomial(4, 2, Zmod(6)(2), algorithm='naive') 5 Check that it works with Python integer for ``q``:: sage: q_binomial(3r, 2r, 1r) 3 REFERENCES: .. [CH2006] William Y.C. Chen and Qing-Hu Hou, *Factors of the Gaussian coefficients*, Discrete Mathematics 306 (2006), 1446-1449. :doi:`10.1016/j.disc.2006.03.031` AUTHORS: - Frederic Chapoton, David Joyner and William Stein """ # sanity checks n = ZZ(n) k = ZZ(k) if n < 0: raise ValueError('n must be nonnegative') # polynomiality test if q is None: from sage.rings.polynomial.polynomial_ring import polygen q = polygen(ZZ, name='q') is_polynomial = True else: from sage.rings.polynomial.polynomial_element import Polynomial is_polynomial = isinstance(q, Polynomial) R = parent(q) try: zero = R.zero() except AttributeError: zero = R('0') try: one = R.one() except AttributeError: one = R('1') if not (0 <= k and k <= n): return zero k = min(n - k, k) # Pick the smallest k # heuristic choice of the fastest algorithm if algorithm == 'auto': from sage.symbolic.ring import SR if is_polynomial: if n <= 70 or k <= n // 4: algorithm = 'naive' else: algorithm = 'cyclo_polynomial' elif q in SR: algorithm = 'cyclo_generic' else: algorithm = 'naive' elif algorithm == 'cyclotomic': if is_polynomial: algorithm = 'cyclo_polynomial' else: algorithm = 'cyclo_generic' elif algorithm != 'naive': raise ValueError("invalid algorithm choice") # the algorithms if algorithm == 'naive': denom = prod(one - q**i for i in range(1, k + 1)) if not denom: # q is a root of unity, use the cyclotomic algorithm from sage.rings.polynomial.cyclotomic import cyclotomic_value return cyclotomic_value(n, k, q, algorithm='cyclotomic') else: num = prod(one - q**i for i in range(n - k + 1, n + 1)) try: try: return num // denom except TypeError: return num / denom except (TypeError, ZeroDivisionError): # use substitution instead return q_binomial(n, k)(q) elif algorithm == 'cyclo_generic': from sage.rings.polynomial.cyclotomic import cyclotomic_value return prod( cyclotomic_value(d, q) for d in range(2, n + 1) if (n // d) != (k // d) + ((n - k) // d)) elif algorithm == 'cyclo_polynomial': return prod( R.cyclotomic_polynomial(d) for d in range(2, n + 1) if (n // d) != (k // d) + ((n - k) // d))
def _llt_generic(self, skp, stat): r""" Takes in partition, list of partitions, or a list of skew partitions as well as a function which takes in two partitions and a level and returns a coefficient. INPUT: - ``self`` -- a family of LLT symmetric functions bases - ``skp`` -- a partition or a list of partitions or a list of skew partitions - ``stat`` -- a function which accepts two partitions and a value for the level and returns a coefficient which is a polynomial in a parameter `t`. The first partition is the index of the LLT function, the second partition is the index of the monomial basis element. OUTPUT: - returns the monomial expansion of the LLT symmetric function indexed by ``skp`` EXAMPLES:: sage: L3 = SymmetricFunctions(FractionField(QQ['t'])).llt(3) sage: f = lambda skp,mu,level: QQ(1) sage: L3._llt_generic([3,2,1],f) m[1, 1] + m[2] sage: L3._llt_generic([[2,1],[1],[2]],f) m[1, 1, 1, 1, 1, 1] + m[2, 1, 1, 1, 1] + m[2, 2, 1, 1] + m[2, 2, 2] + m[3, 1, 1, 1] + m[3, 2, 1] + m[3, 3] + m[4, 1, 1] + m[4, 2] + m[5, 1] + m[6] sage: L3._llt_generic([[[2,2],[1]],[[2,1],[]]],f) m[1, 1, 1, 1] + m[2, 1, 1] + m[2, 2] + m[3, 1] + m[4] """ if skp in sage.combinat.partition.Partitions(): m = (sum(skp) / self.level()).floor() if m == 0: raise ValueError, "level (%=) must divide %s " % (sum(skp), self.level()) mu = sage.combinat.partition.Partitions(ZZ( sum(skp) / self.level())) elif isinstance( skp, list ) and skp[0] in sage.combinat.skew_partition.SkewPartitions(): #skp is a list of skew partitions skp2 = [ sage.combinat.partition.Partition( core=[], quotient=[skp[i][0] for i in range(len(skp))]) ] skp2 += [ sage.combinat.partition.Partition( core=[], quotient=[skp[i][1] for i in range(len(skp))]) ] mu = sage.combinat.partition.Partitions( ZZ((skp2[0].size() - skp2[1].size()) / self.level())) skp = skp2 elif isinstance( skp, list) and skp[0] in sage.combinat.partition.Partitions(): #skp is a list of partitions skp = sage.combinat.partition.Partition(core=[], quotient=skp) mu = sage.combinat.partition.Partitions(ZZ( sum(skp) / self.level())) else: raise ValueError, "LLT polynomials not defined for %s" % skp BR = self.base_ring() return sum([ BR(stat(skp, nu, self.level()).subs(t=self.t)) * self._m(nu) for nu in mu ])
def MatrixGroup(*gens, **kwds): r""" Return the matrix group with given generators. INPUT: - ``*gens`` -- matrices, or a single list/tuple/iterable of matrices, or a matrix group. - ``check`` -- boolean keyword argument (optional, default: ``True``). Whether to check that each matrix is invertible. EXAMPLES:: sage: F = GF(5) sage: gens = [matrix(F,2,[1,2, -1, 1]), matrix(F,2, [1,1, 0,1])] sage: G = MatrixGroup(gens); G Matrix group over Finite Field of size 5 with 2 generators ( [1 2] [1 1] [4 1], [0 1] ) In the second example, the generators are a matrix over `\ZZ`, a matrix over a finite field, and the integer `2`. Sage determines that they both canonically map to matrices over the finite field, so creates that matrix group there:: sage: gens = [matrix(2,[1,2, -1, 1]), matrix(GF(7), 2, [1,1, 0,1]), 2] sage: G = MatrixGroup(gens); G Matrix group over Finite Field of size 7 with 3 generators ( [1 2] [1 1] [2 0] [6 1], [0 1], [0 2] ) Each generator must be invertible:: sage: G = MatrixGroup([matrix(ZZ,2,[1,2,3,4])]) Traceback (most recent call last): ... ValueError: each generator must be an invertible matrix sage: F = GF(5); MS = MatrixSpace(F,2,2) sage: MatrixGroup([MS.0]) Traceback (most recent call last): ... ValueError: each generator must be an invertible matrix sage: MatrixGroup([MS.0], check=False) # works formally but is mathematical nonsense Matrix group over Finite Field of size 5 with 1 generators ( [1 0] [0 0] ) Some groups are not supported, or do not have much functionality implemented:: sage: G = SL(0, QQ) Traceback (most recent call last): ... ValueError: the degree must be at least 1 sage: SL2C = SL(2, CC); SL2C Special Linear Group of degree 2 over Complex Field with 53 bits of precision sage: SL2C.gens() Traceback (most recent call last): ... AttributeError: 'LinearMatrixGroup_generic_with_category' object has no attribute 'gens' """ if isinstance(gens[-1], dict): # hack for unpickling kwds.update(gens[-1]) gens = gens[:-1] check = kwds.get('check', True) if len(gens) == 1: if isinstance(gens[0], (list, tuple)): gens = list(gens[0]) else: try: gens = [g.matrix() for g in gens[0]] except AttributeError: pass if len(gens) == 0: raise ValueError('need at least one generator') gens = normalize_square_matrices(gens) if check and any(not g.is_invertible() for g in gens): raise ValueError('each generator must be an invertible matrix') MS = gens.universe() base_ring = MS.base_ring() degree = ZZ(MS.ncols()) # == MS.nrows() from sage.libs.gap.libgap import libgap try: gap_gens = [libgap(matrix_gen) for matrix_gen in gens] gap_group = libgap.Group(gap_gens) return FinitelyGeneratedMatrixGroup_gap(degree, base_ring, gap_group) except (TypeError, ValueError): return FinitelyGeneratedMatrixGroup_generic(degree, base_ring, gens)
def sturm_bound(self, weight=2): r""" Returns the Sturm bound for modular forms of the given weight and level this subgroup. INPUT: - ``weight`` - an integer `\geq 2` (default: 2) EXAMPLES:: sage: Gamma0(11).sturm_bound(2) 2 sage: Gamma0(389).sturm_bound(2) 65 sage: Gamma0(1).sturm_bound(12) 1 sage: Gamma0(100).sturm_bound(2) 30 sage: Gamma0(1).sturm_bound(36) 3 sage: Gamma0(11).sturm_bound() 2 sage: Gamma0(13).sturm_bound() 3 sage: Gamma0(16).sturm_bound() 4 sage: GammaH(16,[13]).sturm_bound() 8 sage: GammaH(16,[15]).sturm_bound() 16 sage: Gamma1(16).sturm_bound() 32 sage: Gamma1(13).sturm_bound() 28 sage: Gamma1(13).sturm_bound(5) 70 FURTHER DETAILS: This function returns a positive integer `n` such that the Hecke operators `T_1,\ldots, T_n` acting on *cusp forms* generate the Hecke algebra as a `\ZZ`-module when the character is trivial or quadratic. Otherwise, `T_1,\ldots,T_n` generate the Hecke algebra at least as a `\ZZ[\varepsilon]`-module, where `\ZZ[\varepsilon]` is the ring generated by the values of the Dirichlet character `\varepsilon`. Alternatively, this is a bound such that if two cusp forms associated to this space of modular symbols are congruent modulo `(\lambda, q^n)`, then they are congruent modulo `\lambda`. REFERENCES: - See the Agashe-Stein appendix to Lario and Schoof, *Some computations with Hecke rings and deformation rings*, Experimental Math., 11 (2002), no. 2, 303-311. - This result originated in the paper Sturm, *On the congruence of modular forms*, Springer LNM 1240, 275-280, 1987. REMARK: Kevin Buzzard pointed out to me (William Stein) in Fall 2002 that the above bound is fine for `\Gamma_1(N)` with character, as one sees by taking a power of `f`. More precisely, if `f \cong 0 \pmod{p}` for first `s` coefficients, then `f^r \cong 0 \pmod{p}` for first `sr` coefficients. Since the weight of `f^r` is `r\cdot k(f)`, it follows that if `s \geq b`, where `b` is the Sturm bound for `\Gamma_0(N)` at weight `k(f)`, then `f^r` has valuation large enough to be forced to be `0` at `r*k(f)` by Sturm bound (which is valid if we choose `r` correctly). Thus `f \cong 0 \pmod{p}`. Conclusion: For `\Gamma_1(N)` with fixed character, the Sturm bound is *exactly* the same as for `\Gamma_0(N)`. A key point is that we are finding `\ZZ[\varepsilon]` generators for the Hecke algebra here, not `\ZZ`-generators. So if one wants generators for the Hecke algebra over `\ZZ`, this bound must be suitably modified (and I'm not sure what the modification is). AUTHORS: - William Stein """ return ZZ((self.index() * weight / ZZ(12)).ceil())
def enumerate_totallyreal_fields_rel(F, m, B, a=[], verbose=0, return_seqs=False, return_pari_objects=True): r""" This function enumerates (primitive) totally real field extensions of degree `m>1` of the totally real field F with discriminant `d \leq B`; optionally one can specify the first few coefficients, where the sequence ``a`` corresponds to a polynomial by :: a[d]*x^n + ... + a[0]*x^(n-d) if ``length(a) = d+1``, so in particular always ``a[d] = 1``. .. note:: This is guaranteed to give all primitive such fields, and seems in practice to give many imprimitive ones. INPUT: - ``F`` -- number field, the base field - ``m`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``a`` -- list (default: []), the coefficient list to begin with - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. OUTPUT: - the list of fields with entries ``[d,fabs,f]``, where ``d`` is the discriminant, ``fabs`` is an absolute defining polynomial, and ``f`` is a defining polynomial relative to ``F``, sorted by discriminant. - if ``return_seqs`` is ``True``, then the first field of the list is a list containing the count of four items as explained below - the first entry gives the number of polynomials tested - the second entry gives the number of polynomials with its discriminant having a large enough square divisor - the third entry is the number of irreducible polynomials - the fourth entry is the number of irreducible polynomials with discriminant at most ``B`` EXAMPLES:: sage: ZZx = ZZ['x'] sage: F.<t> = NumberField(x^2-2) sage: enumerate_totallyreal_fields_rel(F, 1, 2000) [[1, [-2, 0, 1], xF - 1]] sage: enumerate_totallyreal_fields_rel(F, 2, 2000) [[1600, x^4 - 6*x^2 + 4, xF^2 + xF - 1]] sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_seqs=True) [[9, 6, 5, 0], [[1600, [4, 0, -6, 0, 1], [-1, 1, 1]]]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: type(enumerate_totallyreal_fields_rel(F, 2, 2000)[0][1]) <type 'cypari2.gen.Gen'> sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_pari_objects=False)[0][0].parent() Integer Ring sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_pari_objects=False)[0][2].parent() Univariate Polynomial Ring in xF over Number Field in t with defining polynomial x^2 - 2 sage: enumerate_totallyreal_fields_rel(F, 2, 2000, return_seqs=True)[1][0][1][0].parent() Rational Field AUTHORS: - John Voight (2007-11-01) """ if not isinstance(m, Integer): try: m = Integer(m) except TypeError: raise TypeError("cannot coerce m (= %s) to an integer" % m) if (m < 1): raise ValueError("m must be at least 1.") n = F.degree() * m # Initialize S = {} # dictionary of the form {(d, fabs): f, ...} dB_odlyzko = odlyzko_bound_totallyreal(n) dB = math.ceil(40000 * dB_odlyzko**n) counts = [0, 0, 0, 0] # Trivial case if m == 1: g = pari(F.defining_polynomial()).polrecip().Vec() if return_seqs: return [[0, 0, 0, 0], [1, [-1, 1], g]] elif return_pari_objects: return [[1, g, pari('xF-1')]] else: Px = PolynomialRing(QQ, 'xF') return [[ZZ(1), [QQ(_) for _ in g], Px.gen() - 1]] if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen f_out = [0] * m + [1] T = tr_data_rel(F, m, B, a) if verbose == 2: T.incr(f_out, verbose) else: T.incr(f_out) Fx = PolynomialRing(F, 'xF') nfF = pari( str(F.defining_polynomial()).replace('x', str(F.primitive_element()))) parit = pari(str(F.primitive_element())) while f_out[m] != 0: counts[0] += 1 if verbose: print("==>", f_out, end="") f_str = '' for i in range(len(f_out)): f_str += '(' + str(f_out[i]) + ')*x^' + str(i) if i < len(f_out) - 1: f_str += '+' nf = pari(f_str) if nf.poldegree('t') == 0: nf = nf.subst('x', 'x-t') nf = nf.polresultant(nfF, parit) d = nf.poldisc() #counts[0] += 1 if d > 0 and nf.polsturm() == n: da = int_has_small_square_divisor(Integer(d)) if d > dB or d <= B * da: counts[1] += 1 if nf.polisirreducible(): counts[2] += 1 [zk, d] = nf.nfbasis_d() if d <= B: if verbose: print("has discriminant", d, end="") # Find a minimal lattice element counts[3] += 1 ng = pari([nf, zk]).polredabs() # Check if K is contained in the list. if (d, ng) in S: if verbose: print("but is not new") else: if verbose: print("and is new!") S[(d, ng)] = Fx(f_out) else: if verbose: print("has discriminant", abs(d), "> B") else: if verbose: print("is not absolutely irreducible") else: if verbose: print("has discriminant", abs(d), "with no large enough square divisor") else: if verbose: if d == 0: print("is not squarefree") else: print("is not totally real") if verbose == 2: T.incr(f_out, verbose=verbose) else: T.incr(f_out) # In the application of Smyth's theorem above, we exclude finitely # many possibilities which we must now throw back in. if m == 2: if Fx([-1, 1, 1]).is_irreducible(): K = F.extension(Fx([-1, 1, 1]), 'tK') Kabs = K.absolute_field('tKabs') Kabs_pari = pari(Kabs.defining_polynomial()) d = K.absolute_discriminant() if abs(d) <= B: ng = Kabs_pari.polredabs() S[(d, ng)] = Fx([-1, 1, 1]) elif F.degree() == 2: for ff in [[1, -7, 13, -7, 1], [1, -8, 14, -7, 1]]: f = Fx(ff).factor()[0][0] K = F.extension(f, 'tK') Kabs = K.absolute_field('tKabs') Kabs_pari = pari(Kabs.defining_polynomial()) d = K.absolute_discriminant() if abs(d) <= B: ng = Kabs_pari.polredabs() S[(d, ng)] = f elif m == 3: if Fx([-1, 6, -5, 1]).is_irreducible(): K = F.extension(Fx([-1, 6, -5, 1]), 'tK') Kabs = K.absolute_field('tKabs') Kabs_pari = pari(Kabs.defining_polynomial()) d = K.absolute_discriminant() if abs(d) <= B: ng = Kabs_pari.polredabs() S[(d, ng)] = Fx([-1, 6, -5, 1]) # Convert S to a sorted list of triples [d, fabs, f], taking care # to use cmp() and not the comparison operators on PARI polynomials. S = [[s[0], s[1], t] for s, t in S.items()] S.sort(key=lambda x: (x[0], [QQ(x) for x in x[1].polrecip().Vec()])) # Now check for isomorphic fields weed_fields(S) # Output. if verbose: print("=" * 80) print("Polynomials tested: {}".format(counts[0])) print("Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print("Irreducible polynomials: {}".format(counts[2])) print("Polynomials with nfdisc <= B: {}".format(counts[3])) for i in range(len(S)): print(S[i]) if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [[ZZ(x) for x in counts], [[ s[0], [QQ(x) for x in s[1].polrecip().Vec()], s[2].coefficients(sparse=False) ] for s in S]] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[s[0], Px([QQ(_) for _ in s[1].list()]), s[2]] for s in S]
def epsilon(self, i): r""" Return `\varepsilon_i` of ``self``. EXAMPLES:: sage: S = crystals.OddNegativeRoots(['A', [2,2]]) sage: mg = S.module_generator() sage: [mg.epsilon(i) for i in S.index_set()] [0, 0, 0, 0, 0] sage: b = mg.f_string([0,1,0,-1,0,-1,-2,-2]); b {-e[-3]+e[1], -e[-3]+e[2], -e[-1]+e[1]} sage: [b.epsilon(i) for i in S.index_set()] [2, 0, 1, 0, 0] sage: b = mg.f_string([0,1,0,-1,0,-1,-2,-2,2,-1,0]); b {-e[-3]+e[1], -e[-3]+e[3], -e[-2]+e[1], -e[-1]+e[1]} sage: [b.epsilon(i) for i in S.index_set()] [1, 0, 1, 0, 1] TESTS:: sage: S = crystals.OddNegativeRoots(['A', [2,1]]) sage: def count_e(x, i): ....: ret = -1 ....: while x is not None: ....: x = x.e(i) ....: ret += 1 ....: return ret sage: for x in S: ....: for i in S.index_set(): ....: assert x.epsilon(i) == count_e(x, i) """ if i == 0: return ZZ.one() if (-1,1) in self.value else ZZ.zero() count = 0 ret = 0 if i < 0: lst = sorted(self.value, key=lambda x: (x[1], -x[0])) for val in lst: # We don't have to check val[1] because this is an odd root if val[0] == i - 1: if count == 0: ret += 1 else: count -= 1 elif val[0] == i: count += 1 else: # i > 0 lst = sorted(self.value, key=lambda x: (-x[0], -x[1])) for val in reversed(lst): # We don't have to check val[0] because this is an odd root if val[1] == i + 1: if count == 0: ret += 1 else: count -= 1 elif val[1] == i: count += 1 return ret
def _compute(self, verbose=False): """ Computes the list of curves, the matrix and prime-degree isogenies. EXAMPLES:: sage: K.<i> = QuadraticField(-1) sage: E = EllipticCurve(K, [0,0,0,0,1]) sage: C = E.isogeny_class() sage: C2 = C.copy() sage: C2._mat sage: C2._compute() sage: C2._mat [1 3 6 2] [3 1 2 6] [6 2 1 3] [2 6 3 1] sage: C2._compute(verbose=True) possible isogeny degrees: [2, 3] -actual isogeny degrees: {2, 3} -added curve #1 (degree 2)... -added tuple [0, 1, 2]... -added tuple [1, 0, 2]... -added curve #2 (degree 3)... -added tuple [0, 2, 3]... -added tuple [2, 0, 3]...... relevant degrees: [2, 3]... -now completing the isogeny class... -processing curve #1... -added tuple [1, 0, 2]... -added tuple [0, 1, 2]... -added curve #3... -added tuple [1, 3, 3]... -added tuple [3, 1, 3]... -processing curve #2... -added tuple [2, 3, 2]... -added tuple [3, 2, 2]... -added tuple [2, 0, 3]... -added tuple [0, 2, 3]... -processing curve #3... -added tuple [3, 2, 2]... -added tuple [2, 3, 2]... -added tuple [3, 1, 3]... -added tuple [1, 3, 3]...... isogeny class has size 4 Sorting permutation = {0: 1, 1: 2, 2: 0, 3: 3} Matrix = [1 3 6 2] [3 1 2 6] [6 2 1 3] [2 6 3 1] TESTS: Check that :trac:`19030` is fixed (codomains of reverse isogenies were wrong):: sage: K.<i> = NumberField(x^2+1) sage: E = EllipticCurve([1, i + 1, 1, -72*i + 8, 95*i + 146]) sage: C = E.isogeny_class() sage: curves = C.curves sage: isos = C.isogenies() sage: isos[0][3].codomain() == curves[3] True """ from sage.schemes.elliptic_curves.ell_curve_isogeny import fill_isogeny_matrix from sage.matrix.all import MatrixSpace from sage.sets.set import Set self._maps = None E = self.E.global_minimal_model(semi_global=True) degs = possible_isogeny_degrees(E) if verbose: import sys sys.stdout.write(" possible isogeny degrees: %s" % degs) sys.stdout.flush() isogenies = E.isogenies_prime_degree(degs) if verbose: sys.stdout.write(" -actual isogeny degrees: %s" % Set([phi.degree() for phi in isogenies])) sys.stdout.flush() # Add all new codomains to the list and collect degrees: curves = [E] ncurves = 1 degs = [] # tuples (i,j,l,phi) where curve i is l-isogenous to curve j via phi tuples = [] def add_tup(t): for T in [t, [t[1],t[0],t[2],0]]: if not T in tuples: tuples.append(T) if verbose: sys.stdout.write(" -added tuple %s..." % T[:3]) sys.stdout.flush() for phi in isogenies: E2 = phi.codomain() d = ZZ(phi.degree()) if not any([E2.is_isomorphic(E3) for E3 in curves]): curves.append(E2) if verbose: sys.stdout.write(" -added curve #%s (degree %s)..." % (ncurves,d)) sys.stdout.flush() add_tup([0,ncurves,d,phi]) ncurves += 1 if not d in degs: degs.append(d) if verbose: sys.stdout.write("... relevant degrees: %s..." % degs) sys.stdout.write(" -now completing the isogeny class...") sys.stdout.flush() i = 1 while i < ncurves: E1 = curves[i] if verbose: sys.stdout.write(" -processing curve #%s..." % i) sys.stdout.flush() isogenies = E1.isogenies_prime_degree(degs) for phi in isogenies: E2 = phi.codomain() d = phi.degree() js = [j for j,E3 in enumerate(curves) if E2.is_isomorphic(E3)] if js: # seen codomain already -- up to isomorphism j = js[0] if phi.codomain()!=curves[j]: iso = E2.isomorphism_to(curves[j]) phi.set_post_isomorphism(iso) assert phi.domain()==curves[i] and phi.codomain()==curves[j] add_tup([i,j,d,phi]) else: curves.append(E2) if verbose: sys.stdout.write(" -added curve #%s..." % ncurves) sys.stdout.flush() add_tup([i,ncurves,d,phi]) ncurves += 1 i += 1 if verbose: print("... isogeny class has size %s" % ncurves) # key function for sorting if E.has_rational_cm(): key_function = lambda E: (-E.cm_discriminant(),flatten([list(ai) for ai in E.ainvs()])) else: key_function = lambda E: flatten([list(ai) for ai in E.ainvs()]) self.curves = sorted(curves,key=key_function) perm = {ix: self.curves.index(Ex) for ix, Ex in enumerate(curves)} if verbose: print("Sorting permutation = %s" % perm) mat = MatrixSpace(ZZ,ncurves)(0) self._maps = [[0] * ncurves for _ in range(ncurves)] for i,j,l,phi in tuples: if phi!=0: mat[perm[i],perm[j]] = l self._maps[perm[i]][perm[j]] = phi self._mat = fill_isogeny_matrix(mat) if verbose: print("Matrix = %s" % self._mat) if not E.has_rational_cm(): self._qfmat = None return # In the CM case, we will have found some "horizontal" # isogenies of composite degree and would like to replace them # by isogenies of prime degree, mainly to make the isogeny # graph look better. We also construct a matrix whose entries # are not degrees of cyclic isogenies, but rather quadratic # forms (in 1 or 2 variables) representing the isogeny # degrees. For this we take a short cut: properly speaking, # when `\text{End}(E_1)=\text{End}(E_2)=O`, the set # `\text{Hom}(E_1,E_2)` is a rank `1` projective `O`-module, # hence has a well-defined ideal class associated to it, and # hence (using an identification between the ideal class group # and the group of classes of primitive quadratic forms of the # same discriminant) an equivalence class of quadratic forms. # But we currently only care about the numbers represented by # the form, i.e. which genus it is in rather than the exact # class. So it suffices to find one form of the correct # discriminant which represents one isogeny degree from `E_1` # to `E_2` in order to obtain a form which represents all such # degrees. if verbose: print("Creating degree matrix (CM case)") allQs = {} # keys: discriminants d # values: lists of equivalence classes of # primitive forms of discriminant d def find_quadratic_form(d,n): if not d in allQs: from sage.quadratic_forms.binary_qf import BinaryQF_reduced_representatives allQs[d] = BinaryQF_reduced_representatives(d, primitive_only=True) # now test which of the Qs represents n for Q in allQs[d]: if Q.solve_integer(n): return Q raise ValueError("No form of discriminant %d represents %s" %(d,n)) mat = self._mat qfmat = [[0 for i in range(ncurves)] for j in range(ncurves)] for i, E1 in enumerate(self.curves): for j, E2 in enumerate(self.curves): if j<i: qfmat[i][j] = qfmat[j][i] mat[i,j] = mat[j,i] elif i==j: qfmat[i][j] = [1] # mat[i,j] already 1 else: d = E1.cm_discriminant() if d != E2.cm_discriminant(): qfmat[i][j] = [mat[i,j]] # mat[i,j] already unique else: # horizontal isogeny q = find_quadratic_form(d,mat[i,j]) qfmat[i][j] = list(q) mat[i,j] = q.small_prime_value() self._mat = mat self._qfmat = qfmat if verbose: print("new matrix = %s" % mat) print("matrix of forms = %s" % qfmat)
def rho(self, g): r""" Calculate the action of the group element `g` on the type space. EXAMPLES:: sage: from sage.modular.local_comp.type_space import example_type_space sage: T = example_type_space(2) sage: m = T.rho([2,0,0,1]); m [-1 1 0 -1] [ 0 0 -1 1] [ 0 -1 -1 1] [ 1 -1 -2 2] sage: v = T.eigensymbol_subspace().basis()[0] sage: m * v == v True We test that it is a left action:: sage: T = example_type_space(0) sage: a = [0,5,4,3]; b = [0,2,3,5]; ab = [1,4,2,2] sage: T.rho(ab) == T.rho(a) * T.rho(b) True An odd level example:: sage: from sage.modular.local_comp.type_space import TypeSpace sage: T = TypeSpace(Newform('54a'), 3) sage: a = [0,1,3,0]; b = [2,1,0,1]; ab = [0,1,6,3] sage: T.rho(ab) == T.rho(a) * T.rho(b) True """ if not self.is_minimal(): raise NotImplementedError( "Group action on non-minimal type space not implemented") if self.u() == 0: # silly special case: rep is principal series or special, so SL2 # action on type space is trivial raise ValueError("Representation is not supercuspidal") p = self.prime() f = p**self.u() g = [ZZ(_) for _ in g] d = (g[0] * g[3] - g[2] * g[1]) # g is in S(K_0) (easy case) if d % f == 1: return self._rho_s(g) # g is in K_0, but not in S(K_0) if d % p != 0: try: a = self._a except AttributeError: self._discover_torus_action() a = self._a if not (f % 8): if d % 4 == 3: return ( self.rho([-g[0], g[1], -g[2], g[3]]) * self.t_space.star_involution().matrix().transpose()) i = 0 while (d * a**i) % f != 1: i += 1 if i > f: raise ArithmeticError return self._rho_s([a**i * g[0], g[1], a**i * g[2], g[3] ]) * self._amat**(-i) # det(g) is not a unit if (self.conductor() % 2 == 0): if all(x.valuation(p) > 0 for x in g): eps = self.form().character()(crt(1, p, f, self.tame_level())) return ~eps * p**(self.form().weight() - 2) * self.rho( [x // p for x in g]) else: raise ArithmeticError("g(={0}) not in K".format(g)) else: m = matrix(ZZ, 2, g) s = m.det().valuation(p) mm = (matrix(QQ, 2, [0, -1, p, 0])**(-s) * m).change_ring(ZZ) return self._unif_ramified()**s * self.rho(mm.list())
def possible_isogeny_degrees(E, verbose=False): r""" Return a list of primes `\ell` sufficient to generate the isogeny class of `E`. INPUT: - ``E`` -- An elliptic curve defined over a number field. OUTPUT: A finite list of primes `\ell` such that every curve isogenous to this curve can be obtained by a finite sequence of isogenies of degree one of the primes in the list. ALGORITHM: For curves without CM, the set may be taken to be the finite set of primes at which the Galois representation is not surjective, since the existence of an `\ell`-isogeny is equivalent to the image of the mod-`\ell` Galois representation being contained in a Borel subgroup. For curves with CM by the order `O` of discriminant `d`, the Galois representation is always non-surjective and the curve will admit `\ell`-isogenies for infinitely many primes `\ell`, but there are (of course) only finitely many codomains `E'`. The primes can be divided according to the discriminant `d'` of the CM order `O'` associated to `E`: either `O=O'`, or one contains the other with index `\ell`, since `\ell O\subset O'` and vice versa. Case (1): `O=O'`. The degrees of all isogenies between `E` and `E'` are precisely the integers represented by one of the classes of binary quadratic forms `Q` of discriminant `d`. Hence to obtain all possible isomorphism classes of codomain `E'`, we need only use one prime `\ell` represented by each such class `Q`. It would in fact suffice to use primes represented by forms which generate the class group. Here we simply omit the principal class and one from each pair of inverse classes, and include a prime represented by each of the remaining forms. Case (2): `[O':O]=\ell`: so `d=\ell^2d;`. We include all prime divisors of `d`. Case (3): `[O:O']=\ell`: we may assume that `\ell` does not divide `d` as we have already included these, so `\ell` either splits or is inert in `O`; the class numbers satisfy `h(O')=(\ell\pm1)h(O)` accordingly. We include all primes `\ell` such that `\ell\pm1` divides the degree `[K:\QQ]`. For curves with only potential CM we proceed as in the CM case, using `2[K:\QQ]` instead of `[K:\QQ]`. EXAMPLES: For curves without CM we determine the primes at which the mod `p` Galois representation is reducible, i.e. contained in a Borel subgroup:: sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees sage: E = EllipticCurve('11a1') sage: possible_isogeny_degrees(E) [5] We check that in this case `E` really does have rational `5`-isogenies:: sage: [phi.degree() for phi in E.isogenies_prime_degree()] [5, 5] Over an extension field:: sage: E3 = E.change_ring(CyclotomicField(3)) sage: possible_isogeny_degrees(E3) [5] sage: [phi.degree() for phi in E3.isogenies_prime_degree()] [5, 5] For curves with CM by a quadratic order of class number greater than `1`, we use the structure of the class group to only give one prime in each ideal class:: sage: pol = PolynomialRing(QQ,'x')([1,-3,5,-5,5,-3,1]) sage: L.<a> = NumberField(pol) sage: j = hilbert_class_polynomial(-23).roots(L,multiplicities=False)[0] sage: E = EllipticCurve(j=j) sage: from sage.schemes.elliptic_curves.isogeny_class import possible_isogeny_degrees sage: possible_isogeny_degrees(E, verbose=True) CM case, discriminant = -23 initial primes: {2} upward primes: {} downward ramified primes: {} downward split primes: {2, 3} downward inert primes: {5} primes generating the class group: [2] Complete set of primes: {2, 3, 5} [2, 3, 5] """ if E.has_cm(): d = E.cm_discriminant() if verbose: print("CM case, discriminant = %s" % d) from sage.libs.pari.all import pari from sage.sets.all import Set from sage.arith.all import kronecker_symbol n = E.base_field().absolute_degree() if not E.has_rational_cm(): n *= 2 divs = n.divisors() data = pari(d).quadclassunit() # This has 4 components: the class number, class group # structure (ignored), class group generators (as quadratic # forms) and regulator (=1 since d<0, ignored). h = data[0].sage() # We must have 2*h dividing n, and will need the quotient so # see if the j-invariants of any proper sub-orders could lie # in the same field n_over_2h = n//(2*h) # Collect possible primes. First put in 2, and also 3 for # discriminant -3 (special case because of units): L = Set([ZZ(2), ZZ(3)]) if d==-3 else Set([ZZ(2)]) if verbose: print("initial primes: %s" % L) # Step 1: "vertical" primes l such that the isogenous curve # has CM by an order whose index is l or 1/l times the index # of the order O of discriminant d. The latter case can only # happen when l^2 divides d. # Compute the ramified primes ram_l = d.odd_part().prime_factors() # if the CM is not rational we include all ramified primes, # which is simpler than using the class group later: if not E.has_rational_cm(): L1 = Set(ram_l) L += L1 if verbose: print("ramified primes: %s" % L1) else: # Find the "upward" primes (index divided by l): L1 = Set([l for l in ram_l if d.valuation(l)>1]) L += L1 if verbose: print("upward primes: %s" % L1) # Find the "downward" primes (index multiplied by l, class # number multiplied by l-kronecker_symbol(d,l)): # (a) ramified primes; the suborder has class number l*h, so l # must divide n/2h: L1 = Set([l for l in ram_l if l.divides(n_over_2h)]) L += L1 if verbose: print("downward ramified primes: %s" % L1) # (b) split primes; the suborder has class number (l-1)*h, so # l-1 must divide n/2h: L1 = Set([lm1+1 for lm1 in divs if (lm1+1).is_prime() and kronecker_symbol(d,lm1+1)==+1]) L += L1 if verbose: print("downward split primes: %s" % L1) # (c) inert primes; the suborder has class number (l+1)*h, so # l+1 must divide n/2h: L1 = Set([lp1-1 for lp1 in divs if (lp1-1).is_prime() and kronecker_symbol(d,lp1-1)==-1]) L += L1 if verbose: print("downward inert primes: %s" % L1) # Now find primes represented by each form of discriminant d. # In the rational CM case, we use all forms associated to # generators of the class group, otherwise only forms of order # 2: if E.has_rational_cm(): from sage.quadratic_forms.binary_qf import BinaryQF Qs = [BinaryQF(list(q)) for q in data[2]] L1 = [Q.small_prime_value() for Q in Qs] if verbose: print("primes generating the class group: %s" % L1) L += Set(L1) # Return sorted list if verbose: print("Complete set of primes: %s" % L) return sorted(list(L)) # Non-CM case if verbose: print("Non-CM case, using Galois representation") return E.galois_representation().reducible_primes()
def _compute(self): """ Computes the list of curves, and possibly the matrix and prime-degree isogenies (depending on the algorithm selected). EXAMPLES:: sage: isocls = EllipticCurve('48a1').isogeny_class('sage').copy() sage: isocls._mat sage: isocls._compute(); isocls._mat [0 2 2 2 0 0] [2 0 0 0 2 2] [2 0 0 0 0 0] [2 0 0 0 0 0] [0 2 0 0 0 0] [0 2 0 0 0 0] """ algorithm = self._algorithm from sage.matrix.all import MatrixSpace self._maps = None if algorithm == "database": try: label = self.E.cremona_label(space=False) except RuntimeError: raise RuntimeError("unable to find %s in the database" % self.E) db = sage.databases.cremona.CremonaDatabase() curves = db.isogeny_class(label) if len(curves) == 0: raise RuntimeError("unable to find %s in the database" % self.E) # All curves will have the same conductor and isogeny class, # and there are most 8 of them, so lexicographic sorting is okay. self.curves = tuple(sorted(curves, key = lambda E: E.cremona_label())) self._mat = None elif algorithm == "sage": curves = [self.E.minimal_model()] ijl_triples = [] l_list = None i = 0 while i<len(curves): E = curves[i] isogs = E.isogenies_prime_degree(l_list) for phi in isogs: Edash = phi.codomain() l = phi.degree() # look to see if Edash is new. Note that the # curves returned by isogenies_prime_degree() are # standard minimal models, so it suffices to check # equality rather than isomorphism here. try: j = curves.index(Edash) except ValueError: j = len(curves) curves.append(Edash) ijl_triples.append((i,j,l,phi)) if l_list is None: l_list = [ell for ell in set([ZZ(f.degree()) for f in isogs])] i += 1 self.curves = tuple(curves) ncurves = len(curves) self._mat = MatrixSpace(ZZ, ncurves)(0) self._maps = [[0] * ncurves for _ in range(ncurves)] for i,j,l,phi in ijl_triples: self._mat[i,j] = l self._maps[i][j]=phi else: raise ValueError("unknown algorithm '%s'" % algorithm)
def xgcd(self, other): """ Compute the extended gcd of ``self`` and ``other``. INPUT: - ``other`` -- an element with the same parent as ``self`` OUTPUT: A tuple ``(r, s, t)`` of elements in the parent of ``self`` such that ``r = s * self + t * other``. Since the computations are done over a field, ``r`` is zero if ``self`` and ``other`` are zero, and one otherwise. AUTHORS: - Julian Rueth (2012-10-19): moved here from :class:`sage.structure.element.FieldElement` EXAMPLES:: sage: K = GF(5) sage: K(2).xgcd(K(1)) (1, 3, 0) sage: K(0).xgcd(K(4)) (1, 0, 4) sage: K(1).xgcd(K(1)) (1, 1, 0) sage: GF(5)(0).xgcd(GF(5)(0)) (0, 0, 0) The xgcd of non-zero floating point numbers will be a triple of floating points. But if the input are two integral floating points the result is a floating point version of the standard gcd on `\ZZ`:: sage: xgcd(12.0, 8.0) (4.00000000000000, 1.00000000000000, -1.00000000000000) sage: xgcd(3.1, 2.98714) (1.00000000000000, 0.322580645161290, 0.000000000000000) sage: xgcd(0.0, 1.1) (1.00000000000000, 0.000000000000000, 0.909090909090909) """ P = self.parent() try: has_zero_char = P.characteristic() == 0 except (AttributeError, NotImplementedError): has_zero_char = False if has_zero_char: from sage.rings.integer_ring import ZZ try: return tuple(P(x) for x in ZZ(self).xgcd(ZZ(other))) except TypeError: pass if not self.is_zero(): return (P.one(), ~self, P.zero()) if not other.is_zero(): return (P.one(), P.zero(), ~other) # else both are 0 return (P.zero(), P.zero(), P.zero())
def dimension_cusp_forms(self, k=2): r""" Return the dimension of the space of weight k cusp forms for this group. This is given by a standard formula in terms of k and various invariants of the group; see Diamond + Shurman, "A First Course in Modular Forms", section 3.5 and 3.6. If k is not given, default to k = 2. For dimensions of spaces of cusp forms with character for Gamma1, use the standalone function dimension_cusp_forms(). For weight 1 cusp forms this function only works in cases where one can prove solely in terms of Riemann-Roch theory that there aren't any cusp forms (i.e. when the number of regular cusps is strictly greater than the degree of the canonical divisor). Otherwise a NotImplementedError is raised. EXAMPLE:: sage: Gamma1(31).dimension_cusp_forms(2) 26 sage: Gamma1(3).dimension_cusp_forms(1) 0 sage: Gamma1(4).dimension_cusp_forms(1) # irregular cusp 0 sage: Gamma1(31).dimension_cusp_forms(1) Traceback (most recent call last): ... NotImplementedError: Computation of dimensions of weight 1 cusp forms spaces not implemented in general """ k = ZZ(k) if k <= 0: return ZZ(0) if not (k % 2): # k even if k == 2: return self.genus() else: return (k - 1) * (self.genus() - 1) + ( k // ZZ(4)) * self.nu2() + (k // ZZ(3)) * self.nu3() + ( k // ZZ(2) - 1) * self.ncusps() else: # k odd if self.is_even(): return ZZ(0) else: e_reg = self.nregcusps() e_irr = self.nirregcusps() if k > 1: return (k - 1) * (self.genus() - 1) + ( k // ZZ(3)) * self.nu3() + (k - 2) / ZZ(2) * e_reg + ( k - 1) / ZZ(2) * e_irr else: if e_reg > 2 * self.genus() - 2: return ZZ(0) else: raise NotImplementedError, "Computation of dimensions of weight 1 cusp forms spaces not implemented in general"