def hecke_series_degree_bound(p,N,k,m): r""" Returns the ``Wan bound`` on the degree of the characteristic series of the Atkin operator on p-adic overconvergent modular forms of level `\Gamma_0(N)` and weight k when reduced modulo `p^m`. This bound depends only upon p, `k \pmod{p-1}`, and N. It uses Lemma 3.1 in [DW]_. INPUT: - ``p`` -- prime at least 5. - ``N`` -- positive integer not divisible by p. - ``k`` -- even integer. - ``m`` -- positive integer. OUTPUT: A non-negative integer. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import hecke_series_degree_bound sage: hecke_series_degree_bound(13,11,100,5) 39 REFERENCES: .. [DW] Daqing Wan, "Dimension variation of classical and p-adic modular forms", Invent. Math. 133, (1998) 449-463. """ k0 = k % (p-1) ds = [dimension_modular_forms(N, k0)] ms = [ds[0]] sum = 0 u = 1 ord = 0 while ord < m: ds.append(dimension_modular_forms(N,k0 + u*(p-1))) ms.append(ds[u] - ds[u-1]) sum = sum + u*ms[u] ord = floor(((p-1)/(p+1))*sum - ds[u]) u = u + 1 return (ds[u-1] - 1)
def hecke_series_degree_bound(p, N, k, m): r""" Returns the ``Wan bound`` on the degree of the characteristic series of the Atkin operator on p-adic overconvergent modular forms of level `\Gamma_0(N)` and weight k when reduced modulo `p^m`. This bound depends only upon p, `k \pmod{p-1}`, and N. It uses Lemma 3.1 in [DW]_. INPUT: - ``p`` -- prime at least 5. - ``N`` -- positive integer not divisible by p. - ``k`` -- even integer. - ``m`` -- positive integer. OUTPUT: A non-negative integer. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import hecke_series_degree_bound sage: hecke_series_degree_bound(13,11,100,5) 39 REFERENCES: .. [DW] Daqing Wan, "Dimension variation of classical and p-adic modular forms", Invent. Math. 133, (1998) 449-463. """ k0 = k % (p - 1) ds = [dimension_modular_forms(N, k0)] ms = [ds[0]] sum = 0 u = 1 ord = 0 while ord < m: ds.append(dimension_modular_forms(N, k0 + u * (p - 1))) ms.append(ds[u] - ds[u - 1]) sum = sum + u * ms[u] ord = floor(((p - 1) / (p + 1)) * sum - ds[u]) u = u + 1 return (ds[u - 1] - 1)
def complementary_spaces_modp(N, p, k0, n, elldash, LWBModp, bound): r""" Returns a list of lists of lists of lists ``[j,a]``. The pairs ``[j,a]`` encode the choice of the `a`-th element in the `j`-th list of the input ``LWBModp``, i.e., the `a`-th element in a particular basis modulo `(p,q^\text{elldash})` for the space of modular forms of level `\Gamma_0(N)` and weight `2(j+1)`. The list ``[[j_1,a_1],...,[j_r,a_r]]`` then encodes the product of the r modular forms associated to each ``[j_i,a_i]``; this has weight `k + (p-1)i` for some `0 \le i \le n`; here the i is such that this *list of lists* occurs in the ith list of the output. The ith list of the output thus encodes a choice of basis for the complementary space `W_i` which occurs in Step 2 of Algorithm 2 in [AGBL]_. The idea is that one searches for this space `W_i` first modulo `(p,q^\text{elldash})` and then, having found the correct products of generating forms, one can reconstruct these spaces modulo `(p^\text{mdash},q^\text{elldashp})` using the output of this function. (This idea is based upon a suggestion of John Voight.) INPUT: - ``N`` -- positive integer at least 2 and not divisible by p (level). - ``p`` -- prime at least 5. - ``k0`` -- integer in range 0 to `p-1`. - ``n,elldash`` -- positive integers. - ``LWBModp`` -- list of lists of `q`-expansions over `GF(p)`. - ``bound`` -- positive even integer (twice the length of the list ``LWBModp``). OUTPUT: - list of list of list of lists. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import random_low_weight_bases, complementary_spaces_modp sage: LWB = random_low_weight_bases(2,5,2,4,6) sage: LWBModp = [[f.change_ring(Zmod(5)) for f in x] for x in LWB] sage: complementary_spaces_modp(2,5,0,3,4,LWBModp,6) # random, indirect doctest [[[]], [[[0, 0], [0, 0]]], [[[0, 0], [2, 1]]], [[[0, 0], [0, 0], [0, 0], [2, 1]]]] """ CompSpacesCode = [] ell = dimension_modular_forms(N, k0 + n * (p - 1)) TotalBasisModp = matrix(GF(p), ell, elldash) # zero matrix for i in xrange(n + 1): NewBasisCodemi = random_new_basis_modp(N, p, k0 + i * (p - 1), LWBModp, TotalBasisModp, elldash, bound) # TotalBasisModp is passed by reference and updated in function CompSpacesCode.append(NewBasisCodemi) return CompSpacesCode
def complementary_spaces_modp(N,p,k0,n,elldash,LWBModp,bound): r""" Returns a list of lists of lists of lists ``[j,a]``. The pairs ``[j,a]`` encode the choice of the `a`-th element in the `j`-th list of the input ``LWBModp``, i.e., the `a`-th element in a particular basis modulo `(p,q^\text{elldash})` for the space of modular forms of level `\Gamma_0(N)` and weight `2(j+1)`. The list ``[[j_1,a_1],...,[j_r,a_r]]`` then encodes the product of the r modular forms associated to each ``[j_i,a_i]``; this has weight `k + (p-1)i` for some `0 \le i \le n`; here the i is such that this *list of lists* occurs in the ith list of the output. The ith list of the output thus encodes a choice of basis for the complementary space `W_i` which occurs in Step 2 of Algorithm 2 in [AGBL]_. The idea is that one searches for this space `W_i` first modulo `(p,q^\text{elldash})` and then, having found the correct products of generating forms, one can reconstruct these spaces modulo `(p^\text{mdash},q^\text{elldashp})` using the output of this function. (This idea is based upon a suggestion of John Voight.) INPUT: - ``N`` -- positive integer at least 2 and not divisible by p (level). - ``p`` -- prime at least 5. - ``k0`` -- integer in range 0 to `p-1`. - ``n,elldash`` -- positive integers. - ``LWBModp`` -- list of lists of `q`-expansions over `GF(p)`. - ``bound`` -- positive even integer (twice the length of the list ``LWBModp``). OUTPUT: - list of list of list of lists. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import random_low_weight_bases, complementary_spaces_modp sage: LWB = random_low_weight_bases(2,5,2,4,6) sage: LWBModp = [[f.change_ring(Zmod(5)) for f in x] for x in LWB] sage: complementary_spaces_modp(2,5,0,3,4,LWBModp,6) # random, indirect doctest [[[]], [[[0, 0], [0, 0]]], [[[0, 0], [2, 1]]], [[[0, 0], [0, 0], [0, 0], [2, 1]]]] """ CompSpacesCode = [] ell = dimension_modular_forms(N,k0 + n*(p-1)) TotalBasisModp = matrix(GF(p),ell,elldash); # zero matrix for i in xrange(n+1): NewBasisCodemi = random_new_basis_modp(N,p,k0 + i*(p-1),LWBModp,TotalBasisModp,elldash,bound) # TotalBasisModp is passed by reference and updated in function CompSpacesCode.append(NewBasisCodemi) return CompSpacesCode
def level1_UpGj(p, klist, m): r""" Returns a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` parameterised by the weights k in ``klist``. The matrix `A_k` is the finite square matrix which occurs on input p,k and m in Step 6 of Algorithm 1 in [AGBL]_. 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. - ``klist`` -- list of integers congruent modulo `(p-1)` (the weights). - ``m`` -- positive integer. OUTPUT: - list of square matrices. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import level1_UpGj sage: level1_UpGj(7,[100],5) [ [ 1 980 4802 0 0] [ 0 13727 14406 0 0] [ 0 13440 7203 0 0] [ 0 1995 4802 0 0] [ 0 9212 14406 0 0] ] """ # Step 1 t = cputime() k0 = klist[0] % (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) Alist = [] verbose("done step 4a", t) t = cputime() for k in klist: k = ZZ(k) # convert to sage integer kdiv = k // (p - 1) Gkdiv = G**kdiv u = [] for i in xrange(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 xrange(0, ell): for j in xrange(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) for i in xrange(0, ell): Ti = T[i] for j in xrange(0, ell): ej = Ti.parent()([e[j][l] for l in xrange(0, ell)]) lj = ZZ(ej[j]) A[i, j] = S(ZZ(Ti[j]) / lj) Ti = Ti - A[i, j] * ej Alist.append(MatrixSpace(Zmod(p**m), ell, ell)(A)) verbose("done step 6", t) return Alist
def compute_Wi(k, p, h, hj, E4, E6): r""" This function computes a list `W_i` of q-expansions, together with an auxilliary quantity `h^j` (see below) which is to be used on the next call of this function. (The precision is that of input q-expansions.) The list `W_i` is a certain subset of a basis of the modular forms of weight `k` and level 1. Suppose `(a, b)` is the pair of non-negative integers with `4a + 6b = k` and `a` minimal among such pairs. Then this space has a basis given by .. math:: \{ \Delta^j E_6^{b - 2j} E_4^a : 0 \le j < d\} where `d` is the dimension. What this function returns is the subset of the above basis corresponding to `e \le j < d` where `e` is the dimension of the space of modular forms of weight `k - (p-1)`. This set is a basis for the complement of the image of the weight `k - (p-1)` forms under multiplication by `E_{p-1}`. This function is used repeatedly in the construction of the Katz expansion basis. Hence considerable care is taken to reuse steps in the computation wherever possible: we keep track of powers of the form `h = \Delta / E_6^2`. INPUT: - ``k`` -- non-negative integer. - ``p`` -- prime at least 5. - ``h`` -- q-expansion of `h` (to some finite precision). - ``hj`` -- q-expansion of h^j where j is the dimension of the space of modular forms of level 1 and weight `k - (p-1)` (to same finite precision). - ``E4`` -- q-expansion of ``E_4`` (to same finite precision). - ``E6`` -- q-expansion of ``E_6`` (to same finite precision). The Eisenstein series q-expansions should be normalized to have constant term 1. OUTPUT: - list of q-expansions (to same finite precision), and q-expansion. EXAMPLES: sage: from sage.modular.overconvergent.hecke_series import compute_Wi sage: p = 17 sage: prec = 10 sage: k = 24 sage: S = Zmod(17^3) sage: E4 = eisenstein_series_qexp(4, prec, K=S, normalization="constant") sage: E6 = eisenstein_series_qexp(6, prec, K=S, normalization="constant") sage: h = delta_qexp(prec, K=S) / E6^2 sage: j = dimension_modular_forms(1, k - (p-1)) sage: hj = h**j sage: c = compute_Wi(k,p,h,hj,E4,E6); c ([q + 3881*q^2 + 4459*q^3 + 4665*q^4 + 2966*q^5 + 1902*q^6 + 1350*q^7 + 3836*q^8 + 1752*q^9 + O(q^10), q^2 + 4865*q^3 + 1080*q^4 + 4612*q^5 + 1343*q^6 + 1689*q^7 + 3876*q^8 + 1381*q^9 + O(q^10)], q^3 + 2952*q^4 + 1278*q^5 + 3225*q^6 + 1286*q^7 + 589*q^8 + 122*q^9 + O(q^10)) sage: c == ([delta_qexp(10) * E6^2, delta_qexp(10)^2], h**3) True """ # Define a and b a = k % 3 b = (k // 2) % 2 # Compute dimensions required for Miller basis d = dimension_modular_forms(1, k) - 1 e = dimension_modular_forms(1, k - (p - 1)) - 1 # This next line is a bit of a bottleneck, particularly when m is large but # p is small. It would be good to reuse values calculated on the previous # call here somehow. r = E6**(2 * d + b) * E4**a prec = E4.prec() # everything gets trucated to this precision # Construct basis for Wi Wi = [] for j in xrange(e + 1, d + 1): # compute aj = delta^j*E6^(2*(d-j) + b)*E4^a verbose("k = %s, computing Delta^%s E6^%s E4^%s" % (k, j, 2 * (d - j) + b, a), level=2) aj = (hj * r).truncate_powerseries(prec) hj = (hj * h).truncate_powerseries(prec) Wi.append(aj) return Wi, hj
def random_new_basis_modp(N, p, k, LWBModp, TotalBasisModp, elldash, bound): r""" Returns ``NewBasisCode``. Here `NewBasisCode` is a list of lists of lists ``[j,a]``. This encodes a choice of basis for the ith complementary space `W_i`, as explained in the documentation for the function :func:`complementary_spaces_modp`. INPUT: - ``N`` -- positive integer at least 2 and not divisible by p (level). - ``p`` -- prime at least 5. - ``k`` -- non-negative integer. - ``LWBModp`` -- list of list of q-expansions modulo `(p,q^\text{elldash})`. - ``TotalBasisModp`` -- matrix over GF(p). - ``elldash`` - positive integer. - ``bound`` -- positive even integer (twice the length of the list ``LWBModp``). OUTPUT: - A list of lists of lists ``[j,a]``. .. note:: As well as having a non-trivial return value, this function also modifies the input matrix ``TotalBasisModp``. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import random_low_weight_bases, complementary_spaces_modp sage: LWB = random_low_weight_bases(2,5,2,4,6) sage: LWBModp = [ [f.change_ring(GF(5)) for f in x] for x in LWB] sage: complementary_spaces_modp(2,5,2,3,4,LWBModp,4) # random, indirect doctest [[[[0, 0]]], [[[0, 0], [1, 1]]], [[[0, 0], [1, 0], [1, 1]]], [[[0, 0], [1, 0], [1, 1], [1, 1]]]] """ R = LWBModp[0][0].parent() # Case k0 + i(p-1) = 0 + 0(p-1) = 0 if k == 0: TotalBasisModp[0, 0] = 1 return [[]] # Case k = k0 + i(p-1) > 0 di = dimension_modular_forms(N, k) diminus1 = dimension_modular_forms(N, k - (p - 1)) mi = di - diminus1 NewBasisCode = [] rk = diminus1 for i in xrange(1, mi + 1): while (rk < diminus1 + i): # take random product of basis elements exps = random_solution(bound // 2, k // 2) TotalBasisi = R(1) TotalBasisiCode = [] for j in xrange(len(exps)): for l in xrange(exps[j]): a = ZZ.random_element(len(LWBModp[j])) TotalBasisi = TotalBasisi * LWBModp[j][a] TotalBasisiCode.append([j, a]) TotalBasisModp[rk] = [TotalBasisi[j] for j in range(elldash)] TotalBasisModp.echelonize() rk = TotalBasisModp.rank() NewBasisCode.append(TotalBasisiCode) # this choice increased the rank return NewBasisCode
def level1_UpGj(p,klist,m): r""" Returns a list `[A_k]` of square matrices over ``IntegerRing(p^m)`` parameterised by the weights k in ``klist``. The matrix `A_k` is the finite square matrix which occurs on input p,k and m in Step 6 of Algorithm 1 in [AGBL]_. 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. - ``klist`` -- list of integers congruent modulo `(p-1)` (the weights). - ``m`` -- positive integer. OUTPUT: - list of square matrices. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import level1_UpGj sage: level1_UpGj(7,[100],5) [ [ 1 980 4802 0 0] [ 0 13727 14406 0 0] [ 0 13440 7203 0 0] [ 0 1995 4802 0 0] [ 0 9212 14406 0 0] ] """ # Step 1 t = cputime() k0 = klist[0] % (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) Alist = [] verbose("done step 4a", t) t=cputime() for k in klist: k = ZZ(k) # convert to sage integer kdiv = k // (p-1) Gkdiv = G**kdiv u = [] for i in xrange(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 xrange(0,ell): for j in xrange(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) for i in xrange(0,ell): Ti = T[i] for j in xrange(0,ell): ej = Ti.parent()([e[j][l] for l in xrange(0,ell)]) lj = ZZ(ej[j]) A[i,j] = S(ZZ(Ti[j])/lj) Ti = Ti - A[i,j]*ej Alist.append(MatrixSpace(Zmod(p**m),ell,ell)(A)) verbose("done step 6", t) return Alist
def compute_Wi(k,p,h,hj,E4,E6): r""" This function computes a list `W_i` of q-expansions, together with an auxilliary quantity `h^j` (see below) which is to be used on the next call of this function. (The precision is that of input q-expansions.) The list `W_i` is a certain subset of a basis of the modular forms of weight `k` and level 1. Suppose `(a, b)` is the pair of non-negative integers with `4a + 6b = k` and `a` minimal among such pairs. Then this space has a basis given by .. math:: \{ \Delta^j E_6^{b - 2j} E_4^a : 0 \le j < d\} where `d` is the dimension. What this function returns is the subset of the above basis corresponding to `e \le j < d` where `e` is the dimension of the space of modular forms of weight `k - (p-1)`. This set is a basis for the complement of the image of the weight `k - (p-1)` forms under multiplication by `E_{p-1}`. This function is used repeatedly in the construction of the Katz expansion basis. Hence considerable care is taken to reuse steps in the computation wherever possible: we keep track of powers of the form `h = \Delta / E_6^2`. INPUT: - ``k`` -- non-negative integer. - ``p`` -- prime at least 5. - ``h`` -- q-expansion of `h` (to some finite precision). - ``hj`` -- q-expansion of h^j where j is the dimension of the space of modular forms of level 1 and weight `k - (p-1)` (to same finite precision). - ``E4`` -- q-expansion of ``E_4`` (to same finite precision). - ``E6`` -- q-expansion of ``E_6`` (to same finite precision). The Eisenstein series q-expansions should be normalized to have constant term 1. OUTPUT: - list of q-expansions (to same finite precision), and q-expansion. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import compute_Wi sage: p = 17 sage: prec = 10 sage: k = 24 sage: S = Zmod(17^3) sage: E4 = eisenstein_series_qexp(4, prec, K=S, normalization="constant") sage: E6 = eisenstein_series_qexp(6, prec, K=S, normalization="constant") sage: h = delta_qexp(prec, K=S) / E6^2 sage: j = dimension_modular_forms(1, k - (p-1)) sage: hj = h**j sage: c = compute_Wi(k,p,h,hj,E4,E6); c ([q + 3881*q^2 + 4459*q^3 + 4665*q^4 + 2966*q^5 + 1902*q^6 + 1350*q^7 + 3836*q^8 + 1752*q^9 + O(q^10), q^2 + 4865*q^3 + 1080*q^4 + 4612*q^5 + 1343*q^6 + 1689*q^7 + 3876*q^8 + 1381*q^9 + O(q^10)], q^3 + 2952*q^4 + 1278*q^5 + 3225*q^6 + 1286*q^7 + 589*q^8 + 122*q^9 + O(q^10)) sage: c == ([delta_qexp(10) * E6^2, delta_qexp(10)^2], h**3) True """ # Define a and b a = k % 3 b = (k // 2) % 2 # Compute dimensions required for Miller basis d = dimension_modular_forms(1, k) - 1 e = dimension_modular_forms(1, k-(p-1)) - 1 # This next line is a bit of a bottleneck, particularly when m is large but # p is small. It would be good to reuse values calculated on the previous # call here somehow. r = E6**(2*d + b) * E4**a prec = E4.prec() # everything gets trucated to this precision # Construct basis for Wi Wi = [] for j in xrange(e+1,d+1): # compute aj = delta^j*E6^(2*(d-j) + b)*E4^a verbose("k = %s, computing Delta^%s E6^%s E4^%s" % (k, j, 2*(d-j) + b, a), level=2) aj = (hj * r).truncate_powerseries(prec) hj = (hj * h).truncate_powerseries(prec) Wi.append(aj) return Wi,hj
def random_new_basis_modp(N,p,k,LWBModp,TotalBasisModp,elldash,bound): r""" Returns ``NewBasisCode``. Here `NewBasisCode` is a list of lists of lists ``[j,a]``. This encodes a choice of basis for the ith complementary space `W_i`, as explained in the documentation for the function :func:`complementary_spaces_modp`. INPUT: - ``N`` -- positive integer at least 2 and not divisible by p (level). - ``p`` -- prime at least 5. - ``k`` -- non-negative integer. - ``LWBModp`` -- list of list of q-expansions modulo `(p,q^\text{elldash})`. - ``TotalBasisModp`` -- matrix over GF(p). - ``elldash`` - positive integer. - ``bound`` -- positive even integer (twice the length of the list ``LWBModp``). OUTPUT: - A list of lists of lists ``[j,a]``. .. note:: As well as having a non-trivial return value, this function also modifies the input matrix ``TotalBasisModp``. EXAMPLES:: sage: from sage.modular.overconvergent.hecke_series import random_low_weight_bases, complementary_spaces_modp sage: LWB = random_low_weight_bases(2,5,2,4,6) sage: LWBModp = [ [f.change_ring(GF(5)) for f in x] for x in LWB] sage: complementary_spaces_modp(2,5,2,3,4,LWBModp,4) # random, indirect doctest [[[[0, 0]]], [[[0, 0], [1, 1]]], [[[0, 0], [1, 0], [1, 1]]], [[[0, 0], [1, 0], [1, 1], [1, 1]]]] """ R = LWBModp[0][0].parent() # Case k0 + i(p-1) = 0 + 0(p-1) = 0 if k == 0: TotalBasisModp[0,0] = 1 return [[]] # Case k = k0 + i(p-1) > 0 di = dimension_modular_forms(N, k) diminus1 = dimension_modular_forms(N, k-(p-1)) mi = di - diminus1 NewBasisCode = [] rk = diminus1 for i in xrange(1,mi+1): while (rk < diminus1 + i): # take random product of basis elements exps = random_solution(bound // 2, k // 2) TotalBasisi = R(1) TotalBasisiCode = [] for j in xrange(len(exps)): for l in xrange(exps[j]): a = ZZ.random_element(len(LWBModp[j])) TotalBasisi = TotalBasisi*LWBModp[j][a] TotalBasisiCode.append([j,a]) TotalBasisModp[rk] = [TotalBasisi[j] for j in range(elldash)] TotalBasisModp.echelonize() rk = TotalBasisModp.rank() NewBasisCode.append(TotalBasisiCode) # this choice increased the rank return NewBasisCode
def product_space(chi, k, weights=False, base_ring=None, verbose=False): r""" Computes all eisenstein series, and products of pairs of eisenstein series of lower weight, lying in the space of modular forms of weight $k$ and nebentypus $\chi$. INPUT: - chi - Dirichlet character, the nebentypus of the target space - k - an integer, the weight of the target space OUTPUT: - a matrix of coefficients of q-expansions, which are the products of Eisenstein series in M_k(chi). WARNING: It is only for principal chi that we know that the resulting space is the whole space of modular forms. """ if weights == False: weights = srange(1, k / 2 + 1) weight_dict = {} weight_dict[-1] = [w for w in weights if w % 2] # Odd weights weight_dict[1] = [w for w in weights if not w % 2] # Even weights try: N = chi.modulus() except AttributeError: if chi.parent() == ZZ: N = chi chi = DirichletGroup(N)[0] Id = DirichletGroup(1)[0] if chi(-1) != (-1)**k: raise ValueError('chi(-1)!=(-1)^k') sturm = ModularForms(N, k).sturm_bound() + 1 if N > 1: target_dim = dimension_modular_forms(chi, k) else: target_dim = dimension_modular_forms(1, k) D = DirichletGroup(N) # product_space should ideally be called over number fields. Over complex # numbers the exact linear algebra solutions might not exist. if base_ring == None: base_ring = CyclotomicField(euler_phi(N)) Q = PowerSeriesRing(base_ring, 'q') q = Q.gen() d = len(D) prim_chars = [phi.primitive_character() for phi in D] divs = divisors(N) products = Matrix(base_ring, []) indexlist = [] rank = 0 if verbose: print(D) print('Sturm bound', sturm) #TODO: target_dim needs refinment in the case of weight 2. print('Target dimension', target_dim) for i in srange(0, d): # First character phi = prim_chars[i] M1 = phi.conductor() for j in srange(0, d): # Second character psi = prim_chars[j] M2 = psi.conductor() if not M1 * M2 in divs: continue parity = psi(-1) * phi(-1) for t1 in divs: if not M1 * M2 * t1 in divs: continue #TODO: THE NEXT CONDITION NEEDS TO BE CORRECTED. THIS IS JUST A TEST if phi.bar() == psi and not ( k == 2): #and i==0 and j==0 and t1==1): E = eisenstein_series_at_inf(phi, psi, k, sturm, t1, base_ring).padded_list() try: products.T.solve_right(vector(base_ring, E)) except ValueError: products = Matrix(products.rows() + [E]) indexlist.append([k, i, j, t1]) rank += 1 if verbose: print('Added ', [k, i, j, t1]) print('Rank is now', rank) if rank == target_dim: return products, indexlist for t in divs: if not M1 * M2 * t1 * t in divs: continue for t2 in divs: if not M1 * M2 * t1 * t2 * t in divs: continue for l in weight_dict[parity]: if l == 1 and phi.is_odd(): continue if i == 0 and j == 0 and (l == 2 or l == k - 2): continue #TODO: THE NEXT CONDITION NEEDS TO BE REMOVED. THIS IS JUST A TEST if l == 2 or l == k - 2: continue E1 = eisenstein_series_at_inf( phi, psi, l, sturm, t1 * t, base_ring) E2 = eisenstein_series_at_inf( phi**(-1), psi**(-1), k - l, sturm, t2 * t, base_ring) #If chi is non-principal this needs to be changed to be something like chi*phi^(-1) instead of phi^(-1) E = (E1 * E2 + O(q**sturm)).padded_list() try: products.T.solve_right(vector(base_ring, E)) except ValueError: products = Matrix(products.rows() + [E]) indexlist.append([l, k - l, i, j, t1, t2, t]) rank += 1 if verbose: print('Added ', [l, k - l, i, j, t1, t2, t]) print('Rank', rank) if rank == target_dim: return products, indexlist return products, indexlist