def decomposition(self, B, verbose=False): """ Return Hecke decomposition of self using Hecke operators T_p coprime to the level with norm(p) <= B. """ # TODO: rewrite to use primes_of_bounded_norm so that we # iterate through primes ordered by *norm*, which is # potentially vastly faster. Delete these functions # involving characteristic! p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level()) T = self.hecke_matrix(p) D = T.decomposition() while len([X for X in D if not X[1]]) > 0: p = next_prime_of_characteristic_coprime_to(p, self.level()) if p.norm() > B: break if verbose: print p.norm() T = self.hecke_matrix(p) D2 = [] for X in D: if X[1]: D2.append(X) else: if verbose: print T.restrict(X[0]).fcp() for Z in T.decomposition_of_subspace(X[0]): D2.append(Z) D = D2 D = [self.subspace(X[0]) for X in D] D.sort() S = Sequence(D, immutable=True, cr=True, universe=int, check=False) return S
def new_decomposition(self, verbose=False): """ Return complete irreducible Hecke decomposition of new subspace of self. """ V = self.degeneracy_matrix().kernel() p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level()) T = self.hecke_matrix(p) D = T.decomposition_of_subspace(V) while len([X for X in D if not X[1]]) > 0: p = next_prime_of_characteristic_coprime_to(p, self.level()) if verbose: print p.norm() T = self.hecke_matrix(p) D2 = [] for X in D: if X[1]: D2.append(X) else: if verbose: print T.restrict(X[0]).fcp() for Z in T.decomposition_of_subspace(X[0]): D2.append(Z) D = D2 D = [self.subspace(X[0]) for X in D] D.sort() S = Sequence(D, immutable=True, cr=True, universe=int, check=False) return S
def ideals_of_norm(v): """ INPUT: - `v` -- integer >= 2, or list of integers >= 2 OUTPUT: - list of ideals of all ideals that have norm in v that is >= 2. EXAMPLES:: sage: sage.modular.hilbert.sqrt5_tables.ideals_of_norm(4) [Fractional ideal (2)] sage: sage.modular.hilbert.sqrt5_tables.ideals_of_norm([9,11]) [Fractional ideal (3), Fractional ideal (-3*a + 1), Fractional ideal (-3*a + 2)] sage: sage.modular.hilbert.sqrt5_tables.ideals_of_norm([4*5]) [Fractional ideal (-4*a + 2)] sage: sage.modular.hilbert.sqrt5_tables.ideals_of_norm(4*5) [Fractional ideal (-4*a + 2)] """ try: v = list(v) except TypeError: v = [Integer(v)] z = F.ideals_of_bdd_norm(max(v)) return sum([z[n] for n in v if n > 1], [])
def find_mod2pow_splitting(i): P = F.primes_above(2)[0] if i == 1: R = ResidueRing(P, 1) M = MatrixSpace(R, 2) return [M(matrix_lift(a).list()) for a in find_mod2_splitting()] R = ResidueRing(P, i) M = MatrixSpace(R, 2) # arbitrary lift wbar = [M(matrix_lift(a).list()) for a in find_mod2pow_splitting(i - 1)] # Find lifts of wbar[1] and wbar[2] that have square -1 k = P.residue_field() Mk = MatrixSpace(k, 2) t = 2**(i - 1) s = M(t) L = [] for j in [1, 2]: C = Mk(matrix_lift(wbar[j]**2 + M(1)) / t) A = Mk(matrix_lift(wbar[j])) # Find all matrices B in Mk such that AB+BA=C. L.append([ wbar[j] + s * M(matrix_lift(B)) for B in Mk if A * B + B * A == C ]) g = M(F.gen()) t = M(t) two = M(2) ginv = M(F.gen()**(-1)) for w1 in L[0]: for w2 in L[1]: w0 = ginv * (two * g * wbar[3] - w1 - w2 - w1 * w2) w3 = g * w0 + w2 * w1 if w0 * w0 != w0 - M(1): continue if w3 * w3 != M(-1): continue return w0, w1, w2, w3 raise ValueError
def next_prime_not_dividing(P, I): while True: p = P.smallest_integer() if p == 1: Q = F.ideal(2) elif p % 5 in [2,3]: # inert Q = F.primes_above(next_prime(p))[0] elif p == 5: Q = F.ideal(7) else: # p split A = F.primes_above(p) if A[0] == P: Q = A[1] else: Q = F.primes_above(next_prime(p))[0] if not Q.divides(I): return Q else: P = Q # try again
def find_mod2pow_splitting(i): P = F.primes_above(2)[0] if i == 1: R = ResidueRing(P, 1) M = MatrixSpace(R, 2) return [M(matrix_lift(a).list()) for a in find_mod2_splitting()] R = ResidueRing(P, i) M = MatrixSpace(R, 2) # arbitrary lift wbar = [M(matrix_lift(a).list()) for a in find_mod2pow_splitting(i - 1)] # Find lifts of wbar[1] and wbar[2] that have square -1 k = P.residue_field() Mk = MatrixSpace(k, 2) t = 2 ** (i - 1) s = M(t) L = [] for j in [1, 2]: C = Mk(matrix_lift(wbar[j] ** 2 + M(1)) / t) A = Mk(matrix_lift(wbar[j])) # Find all matrices B in Mk such that AB+BA=C. L.append([wbar[j] + s * M(matrix_lift(B)) for B in Mk if A * B + B * A == C]) g = M(F.gen()) t = M(t) two = M(2) ginv = M(F.gen() ** (-1)) for w1 in L[0]: for w2 in L[1]: w0 = ginv * (two * g * wbar[3] - w1 - w2 - w1 * w2) w3 = g * w0 + w2 * w1 if w0 * w0 != w0 - M(1): continue if w3 * w3 != M(-1): continue return w0, w1, w2, w3 raise ValueError
def elliptic_curve_factors(self): D = [X for X in self.new_decomposition() if X.dimension() == 1] # Have to get rid of the Eisenstein factor p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level()) while True: q = p.residue_field().cardinality() + 1 E = [A for A in D if A.hecke_matrix(p)[0,0] == q] if len(E) == 0: break elif len(E) == 1: D = [A for A in D if A != E[0]] break else: p = next_prime_of_characteristic_coprime_to(p, self.level()) return Sequence([EllipticCurveFactor(X, number) for number, X in enumerate(D)], immutable=True, cr=True, universe=int, check=False)
def find_mod2_splitting(): """ Return elements w0, w1, w2, w3 that determine a mod-2 splitting of the Hamilton quaternion algebra over Q(sqrt(5)). OUTPUT: - four 2x2 matrices over the residue field of Q(sqrt(5)) of order 4. EXAMPLES:: sage: import sage.modular.hilbert.sqrt5_fast_python sage: w = sage.modular.hilbert.sqrt5_fast_python.find_mod2_splitting(); w ( [ 1 abar] [ 0 abar + 1] [abar + 1 abar] [1 1] [abar + 1 0], [ abar 0], [ abar abar + 1], [0 1] ) sage: w[1]^2, w[2]^2 ( [1 0] [1 0] [0 1], [0 1] ) sage: span([vector(z.list()) for z in w]).dimension() 4 """ P = F.primes_above(2)[0] k = P.residue_field() M = MatrixSpace(k,2) V = k**4 g = k.gen() # image of golden ratio m1 = M(-1) sqrt_minus_1 = [(w, V(w.list())) for w in M if w*w == m1] one = M(1) v_one = V(one.list()) for w1, v1 in sqrt_minus_1: for w2, v2 in sqrt_minus_1: w0 = (g-1)*(w1+w2 - w1*w2) w3 = g*w0 + w2*w1 if w0*w0 != w0 - 1: continue if w3*w3 != -1: continue if V.span([w0.list(),v1,v2,w3.list()]).dimension() == 4: return w0, w1, w2, w3
def test_reduced_rep(B=50): """ Unit test: run a simple consistency check that reduced_rep is working sensibly. INPUT: - `B` -- positive integer OUTPUT: - None (assertion raised if something detected wrong) EXAMPLES:: sage: sage.modular.hilbert.sqrt5_tables.test_reduced_rep() """ a = F.gen() z = -45 * a + 28 v = [reduced_rep(a ** i * z) for i in range(-B, B)] assert len(set(v)) == 1
def find_mod2_splitting(): P = F.primes_above(2)[0] k = P.residue_field() M = MatrixSpace(k, 2) V = k ** 4 g = k.gen() # image of golden ratio m1 = M(-1) sqrt_minus_1 = [(w, V(w.list())) for w in M if w * w == m1] one = M(1) v_one = V(one.list()) for w1, v1 in sqrt_minus_1: for w2, v2 in sqrt_minus_1: w0 = (g - 1) * (w1 + w2 - w1 * w2) w3 = g * w0 + w2 * w1 if w0 * w0 != w0 - 1: continue if w3 * w3 != -1: continue if V.span([w0.list(), v1, v2, w3.list()]).dimension() == 4: return w0, w1, w2, w3
def ideals_of_bounded_norm(B): r""" Return all ideals in the ring of integers of Q(sqrt(5)) with norm bigger than 1 and <= B. INPUT: - `B` -- positive integer OUTPUT: - list of ideals EXAMPLES:: sage: v = sage.modular.hilbert.sqrt5_tables.ideals_of_bounded_norm(11); v [Fractional ideal (2), Fractional ideal (-2*a + 1), Fractional ideal (3), Fractional ideal (-3*a + 1), Fractional ideal (-3*a + 2)] sage: [I.norm() for I in v] [4, 5, 9, 11, 11] """ return sum([v for n, v in F.ideals_of_bdd_norm(B).iteritems() if n != 1], [])
def find_mod2_splitting(): P = F.primes_above(2)[0] k = P.residue_field() M = MatrixSpace(k, 2) V = k**4 g = k.gen() # image of golden ratio m1 = M(-1) sqrt_minus_1 = [(w, V(w.list())) for w in M if w * w == m1] one = M(1) v_one = V(one.list()) for w1, v1 in sqrt_minus_1: for w2, v2 in sqrt_minus_1: w0 = (g - 1) * (w1 + w2 - w1 * w2) w3 = g * w0 + w2 * w1 if w0 * w0 != w0 - 1: continue if w3 * w3 != -1: continue if V.span([w0.list(), v1, v2, w3.list()]).dimension() == 4: return w0, w1, w2, w3
def next_prime_of_characteristic_coprime_to(P, I): p = next_prime(P.smallest_integer()) N = ZZ(I.norm()) while N%p == 0: p = next_prime(p) return F.primes_above(p)[0]
def find_mod2pow_splitting(i): """ Return elements w0, w1, w2, w3 that determine a mod-`2^i` splitting of the Hamilton quaternion algebra over Q(sqrt(5)). INPUT: - `i` -- positive integer OUTPUT: - four 2x2 matrices over a residue ring of Q(sqrt(5)) of characteristic a power of 2 EXAMPLES:: sage: import sage.modular.hilbert.sqrt5_fast_python sage: w = sage.modular.hilbert.sqrt5_fast_python.find_mod2pow_splitting(1); w ( [ 1 g] [ 0 1 + g] [1 + g g] [1 1] [1 + g 0], [ g 0], [ g 1 + g], [0 1] ) sage: w = sage.modular.hilbert.sqrt5_fast_python.find_mod2pow_splitting(2); w ( [ 3 2 + 3*g] [ 0 1 + 3*g] [3 + 3*g 3*g] [ 1 + g 2], [ g 0], [ 3*g 1 + g], [3 + 2*g 3 + 2*g] [ 2 1 + 2*g] ) sage: w[1]^2 [3 0] [0 3] sage: w[2]^2 [3 0] [0 3] sage: w = sage.modular.hilbert.sqrt5_fast_python.find_mod2pow_splitting(4) sage: w[1]^2 [15 0] [ 0 15] sage: w[2]^2 [15 0] [ 0 15] sage: w[1], w[2] ( [ 0 1 + 15*g] [15 + 15*g 12 + 7*g] [ g 0], [ 4 + 11*g 1 + g] ) TESTS: The power must be positive:: sage: sage.modular.hilbert.sqrt5_fast_python.find_mod2pow_splitting(0) Traceback (most recent call last): ... ValueError: i must be positive """ P = F.primes_above(2)[0] i = ZZ(i) if i <= 0: raise ValueError, "i must be positive" if i == 1: R = ResidueRing(P, 1) M = MatrixSpace(R,2) return tuple([M(matrix_lift(a).list()) for a in find_mod2_splitting()]) R = ResidueRing(P, i) M = MatrixSpace(R,2) # arbitrary lift wbar = [M(matrix_lift(a).list()) for a in find_mod2pow_splitting(i-1)] # Find lifts of wbar[1] and wbar[2] that have square -1 k = P.residue_field() Mk = MatrixSpace(k, 2) t = 2**(i-1) s = M(t) L = [] for j in [1,2]: C = Mk(matrix_lift(wbar[j]**2 + M(1)) / t) A = Mk(matrix_lift(wbar[j])) # Find all matrices B in Mk such that AB+BA=C. L.append([wbar[j]+s*M(matrix_lift(B)) for B in Mk if A*B + B*A == C]) g = M(F.gen()) t = M(t) two = M(2) ginv = M(F.gen()**(-1)) for w1 in L[0]: for w2 in L[1]: w0 = ginv*(two*g*wbar[3] -w1 -w2 - w1*w2) w3 = g*w0 + w2*w1 if w0*w0 != w0 - M(1): continue if w3*w3 != M(-1): continue return w0, w1, w2, w3 raise ValueError
def dimensions(v, filename=None): """ Compute dimensions of spaces of Hilbert modular forms for all the levels in v. The format is: Norm dimension generator time INPUT: - `v` -- list of positive integers - ``filename`` -- optional string; if given, output is also written to that file (in addition to stdout). OUTPUT: - appends to table with above format and rows corresponding to the ideals of Q(sqrt(5)) with norm in v, and optionally creates a file EXAMPLES:: sage: from sage.modular.hilbert.sqrt5_tables import dimensions sage: out = dimensions([1..40]) 4 1 2 ... 5 1 -2*a+1 ... 9 1 3 ... 11 1 -3*a+1 ... 11 1 -3*a+2 ... 16 1 4 ... 19 1 -4*a+1 ... 19 1 -4*a+3 ... 20 1 -4*a+2 ... 25 1 5 ... 29 1 a-6 ... 29 1 -a-5 ... 31 2 5*a-3 ... 31 2 5*a-2 ... 36 2 6 ... sage: out = dimensions([36, 4]) 36 2 6 ... 4 1 2 ... Test writing to a file:: sage: if os.path.exists('tmp_table.txt'): os.unlink('tmp_table.txt') sage: out = dimensions([36, 4], 'tmp_table.txt') 36 2 6 ... 4 1 2 ... sage: '36 2 6' in open('tmp_table.txt').read() True sage: open('tmp_table.txt').read().count('\n') 2 sage: os.unlink('tmp_table.txt') """ if len(v) == 0: return "" F = open(filename, "a") if filename else None out = "" for N in ideals_of_norm(v): t = cputime() H = IcosiansModP1ModN(N) tm = "%.2f" % cputime(t) s = "%s %s %s %s" % (N.norm(), H.cardinality(), no_space(reduced_gen(N)), tm) print s out += s + "\n" if F: F.write(s + "\n") F.flush() return out
def charpolys(v, B, filename=None): """ Compute characteristic polynomials of T_P for primes P with norm <= B coprime to the level, for all spaces of Hilbert modular forms for all the levels in v. INPUT: - `v` -- list of positive integers - `B` -- positive integer - ``filename`` -- optional string; if given, output is also written to that file (in addition to stdout). OUTPUT: - outputs a table with rows corresponding to the ideals of Q(sqrt(5)) with norm in v, and optionally creates a file EXAMPLES:: sage: from sage.modular.hilbert.sqrt5_tables import charpolys sage: out = charpolys([1..20], 10) 4 2 ... [(5,x-6),(3,x-10)] 5 -2*a+1 ... [(2,x-5),(3,x-10)] 9 3 ... [(2,x-5),(5,x-6)] 11 -3*a+1 ... [(2,x-5),(5,x-6),(3,x-10)] 11 -3*a+2 ... [(2,x-5),(5,x-6),(3,x-10)] 16 4 ... [(5,x-6),(3,x-10)] 19 -4*a+1 ... [(2,x-5),(5,x-6),(3,x-10)] 19 -4*a+3 ... [(2,x-5),(5,x-6),(3,x-10)] 20 -4*a+2 ... [(3,x-10)] sage: out = charpolys([20, 11], 10) 20 -4*a+2 ... [(3,x-10)] 11 -3*a+1 ... [(2,x-5),(5,x-6),(3,x-10)] 11 -3*a+2 ... [(2,x-5),(5,x-6),(3,x-10)] Test writing to a file:: sage: if os.path.exists('tmp_table.txt'): os.unlink('tmp_table.txt') sage: out = charpolys([20, 11], 10, 'tmp_table.txt') 20 -4*a+2 ... [(3,x-10)] 11 -3*a+1 ... [(2,x-5),(5,x-6),(3,x-10)] 11 -3*a+2 ... [(2,x-5),(5,x-6),(3,x-10)] sage: r = open('tmp_table.txt').read() sage: 'x-10' in r True sage: r.count('\n') 3 sage: os.unlink('tmp_table.txt') """ if len(v) == 0: return "" out = "" F = open(filename, "a") if filename else None P = [p for p in ideals_of_bounded_norm(B) if p.is_prime()] for N in ideals_of_norm(v): t = cputime() H = IcosiansModP1ModN(N) T = [ (p.smallest_integer(), H.hecke_matrix(p).fcp()) for p in P if gcd(Integer(p.norm()), Integer(N.norm())) == 1 ] tm = "%.2f" % cputime(t) s = "%s %s %s %s" % (N.norm(), no_space(reduced_gen(N)), tm, no_space(T)) print s out += s + "\n" if F: F.write(s + "\n") F.flush() return out
def rational_newforms(v, B=100, filename=None, ncpu=1): """ Return system of Hecke eigenvalues corresponding to rational newforms of level whose norm is in v. Compute the Hecke eigenvalues a_P for all good primes P with norm < B. INPUT: - `v` -- list of integers - `B` -- positive integer - ``filename`` -- optional filename - ``ncpu`` -- positive integer (default: 1); if > 1 then use ncpu simultaneous processes. Note that that displayed output during the computation and to the file may be out of order. OUTPUT: - outputs a table with rows corresponding to the ideals of Q(sqrt(5)) with norm in v, and optionally creates a file Table columns: norm_of_level generator_of_level number time_for_level a_P a_P ... EXAMPLES:: sage: from sage.modular.hilbert.sqrt5_tables import rational_newforms sage: out = rational_newforms([1..76], B=20) 31 5*a-3 0 ... -3 -2 2 4 -4 -4 4 31 5*a-2 0 ... -3 -2 2 -4 4 4 -4 36 6 0 ... ? -4 ? 2 2 0 0 41 a-7 0 ... -2 -1 -4 -2 5 -1 6 41 a+6 0 ... -2 -1 -4 5 -2 6 -1 45 -6*a+3 0 ... -3 ? ? -4 -4 4 4 49 7 0 ... 0 -4 5 -3 -3 0 0 55 a+7 0 ... -1 ? -2 ? 0 8 -4 55 -a+8 0 ... -1 ? -2 0 ? -4 8 64 8 0 ... 0 -2 2 -4 -4 4 4 71 a-9 0 ... -1 0 -2 0 0 -4 2 71 a+8 0 ... -1 0 -2 0 0 2 -4 76 -8*a+2 0 ... ? -3 1 3 -6 ? -7 76 -8*a+2 1 ... ? 1 -5 -3 2 ? 5 76 -8*a+6 0 ... ? -3 1 -6 3 -7 ? 76 -8*a+6 1 ... ? 1 -5 2 -3 5 ? Test writing to a file:: sage: if os.path.exists('tmp_table.txt'): os.unlink('tmp_table.txt') sage: out = rational_newforms([1..36], 20,'tmp_table.txt') 31 5*a-3 0 ... -3 -2 2 4 -4 -4 4 31 5*a-2 0 ... -3 -2 2 -4 4 4 -4 36 6 0 ... ? -4 ? 2 2 0 0 sage: r = open('tmp_table.txt').read() sage: r.count('\n') 3 sage: os.unlink('tmp_table.txt') """ if len(v) == 0: return "" if ncpu < 1: raise ValueError, "ncpu must be >= 1" F = open(filename, "a") if filename else None if ncpu > 1: from sage.all import parallel @parallel(ncpu) def f(N): return rational_newforms([N], B, filename=None, ncpu=1) d = {} for X in f(v): N = X[0][0] ans = X[1].strip() if ans: d[N] = ans if F: F.write(ans + "\n") return "\n".join(d[N] for N in sorted(d.keys())) out = "" from sqrt5_hmf import QuaternionicModule for N in ideals_of_norm(v): t = cputime() H = QuaternionicModule(N) EC = H.rational_newforms() tm = "%.2f" % cputime(t) for i, E in enumerate(EC): v = E.aplist(B) data = [N.norm(), no_space(reduced_gen(N)), i, tm, " ".join([no_space(x) for x in v])] s = " ".join([str(x) for x in data]) print s out += s + "\n" if F: F.write(s + "\n") F.flush() return out