Example #1
0
    def minimal_twist(self):
        r"""
        Return a newform (not necessarily unique) which is a twist of the
        original form `f` by a Dirichlet character of `p`-power conductor, and
        which has minimal level among such twists of `f`.

        An error will be raised if `f` is already minimal.

        EXAMPLE::

            sage: from sage.modular.local_comp.type_space import TypeSpace, example_type_space
            sage: T = example_type_space(1)
            sage: T.form().q_expansion(12)
            q - q^2 + 2*q^3 + q^4 - 2*q^6 - q^8 + q^9 + O(q^12)
            sage: g = T.minimal_twist()
            sage: g.q_expansion(12)
            q - q^2 - 2*q^3 + q^4 + 2*q^6 + q^7 - q^8 + q^9 + O(q^12)
            sage: g.level()
            14
            sage: TypeSpace(g, 7).is_minimal()
            True

        Test that :trac:`13158` is fixed::

            sage: f = Newforms(256,names='a')[0]
            sage: T = TypeSpace(f,2)
            sage: g = T.minimal_twist(); g
            q - a*q^3 + O(q^6)
            sage: g.level()
            64
        """
        if self.is_minimal():
            raise ValueError("Form is already minimal")

        NN = self.form().level()
        V = self.t_space
        A = V.ambient()

        while not V.is_submodule(A.new_submodule()):
            NN = NN / self.prime()
            D1 = A.degeneracy_map(NN, 1)
            Dp = A.degeneracy_map(NN, self.prime())
            A = D1.codomain()
            vecs = [D1(v).element()
                    for v in V.basis()] + [Dp(v).element() for v in V.basis()]
            VV = A.free_module().submodule(vecs)
            V = A.submodule(VV, check=False)

        D = V.decomposition()[0]
        if len(D.star_eigenvalues()) == 2:
            D = D.sign_submodule(1)
        D._set_sign(D.star_eigenvalues()[0])
        M = ModularForms(D.group(), D.weight())
        ff = Newform(M, D, names='a')
        return ff
    def __maass_lifts(self, k, precision, return_value) :
        r"""
        Return the Fourier expansion of all Maass forms of weight `k`.
        """
        result = []
        
        if k < 4 or k % 2 != 0 :
            return []
                
        mf = ModularForms(1,k).echelon_basis()
        cf = ModularForms(1,k + 2).echelon_basis()[1:]
        integrality_factor = 2*k * bernoulli(k).denominator()

        for c in [(integrality_factor * mf[0],0)] \
                     + [ (f,0) for f in mf[1:] ] + [ (0,g) for g in cf ] :
            if return_value == "lifts" :
                result.append(SiegelModularFormG2MaassLift(c[0],c[1], precision, True))
            else :
                result.append(c)
        
        return result
Example #3
0
def getElliptModFormsBasisMatrix(level, weight, precision):
	"""
	Calculates the Echelon basis matrix of the Elliptic modular forms space.

	INPUT:

	- `level` -- The level for the modular group `\Gamma = \Gamma_0(level)`.

	- `weight` -- The weight of the Elliptic modular forms.

	- `precision` -- The precision of the Elliptic modular form Fourier expansions.

	OUTPUT:

	- A tuple `(dim,m)`, where `m` is a matrix which is the Echelon basis matrix of
	  the Elliptic modular forms over `\Gamma_0(level)` with weight `weight`
	  such that `m.ncols() == precision`.
	  `dim` is the dimension of the modular forms. You should check that `m.rank() == dim`,
	  otherwise it might not make sense to work with `m`.
	"""

	cacheIdx = (level, weight)
	if cacheIdx in ellipBaseMatrixCache and ellipBaseMatrixCache[cacheIdx][1] >= precision:
		fe_expansion_matrix_l = ellipBaseMatrixCache[cacheIdx][0]
		cut_matrix = fe_expansion_matrix_l[:,:precision]
		dim = fe_expansion_matrix_l.rank()
		return dim, cut_matrix
	n = precision
	n = max(10, n)
	mf = ModularForms(Gamma0(level), weight)
	dim = mf.dimension()
	while True:
		fe_expansion_matrix_l = matrix(QQ, [b.qexp(n).padded_list(n) for b in mf.basis()])
		fe_expansion_matrix_l.echelonize()
		if fe_expansion_matrix_l.rank() == dim: break
		n += 10
	assert fe_expansion_matrix_l.rank() == dim, "%i != %i" % (fe_expansion_matrix_l.rank(), dim)
	ellipBaseMatrixCache[cacheIdx] = (fe_expansion_matrix_l, n)
	cut_matrix = fe_expansion_matrix_l[:,:precision]
	return dim, cut_matrix
Example #4
0
def _all_weak_jacobi_forms_by_taylor_expansion(index, weight, precision):
    """
    TESTS:
    
    We compute the Fourier expansion of a Jacobi form of weight `4` and index `2`.  This
    is denoted by ``d``.  Moreover, we span the space of all Jacobi forms of weight `8` and
    index `2`.  Multiplying the weight `4` by the Eisenstein series of weight `4` must
    yield an element of the weight `8` space.  Note that the multiplication is done using
    a polynomial ring, since no native multiplication for Jacobi forms is implemented.
    
    ::
    
        sage: from psage.modform.jacobiforms.jacobiformd1nn_fourierexpansion import *
        sage: from psage.modform.jacobiforms.jacobiformd1nn_fegenerators import _all_weak_jacobi_forms_by_taylor_expansion
        sage: from psage.modform.fourier_expansion_framework import *
        sage: prec = 20
        sage: d = JacobiFormD1NNFilter(prec, 2)
        sage: f = _all_weak_jacobi_forms_by_taylor_expansion(2, 4, prec)[0]
        sage: (g1,g2) = tuple(_all_weak_jacobi_forms_by_taylor_expansion(2, 8, prec))
        sage: em = ExpansionModule([g1, g2])
        sage: P.<q, zeta> = PolynomialRing(QQ, 2)
        sage: fp = sum(f[k] * q**k[0] * zeta**k[1] for k in JacobiFormD1NNFilter(prec, 2, reduced = False))
        sage: mf4 = ModularForms(1, 4).0.qexp(prec).polynomial()
        sage: h = mf4 * fp
        sage: eh = EquivariantMonoidPowerSeries(g1.parent(), {g1.parent().characters().gen(0) : dict((k, h[k[0],k[1]]) for k in d)}, d)
        sage: em.coordinates(eh.truncate(5), in_base_ring = False)
        [7/66, 4480]
        
    According to this we express ``eh`` in terms of the basis of the weight `8` space.
    
    ::
    
        sage: neh = eh - em.0.fourier_expansion() * 7 / 66 - em.1.fourier_expansion() * 4480
        sage: neh.coefficients()
        {(18, 0): 0, (12, 1): 0, (9, 1): 0, (3, 0): 0, (11, 2): 0, (8, 0): 0, (16, 2): 0, (2, 1): 0, (15, 1): 0, (6, 2): 0, (14, 0): 0, (19, 0): 0, (5, 1): 0, (7, 2): 0, (4, 0): 0, (1, 2): 0, (12, 2): 0, (9, 0): 0, (8, 1): 0, (18, 2): 0, (15, 0): 0, (17, 2): 0, (14, 1): 0, (11, 1): 0, (18, 1): 0, (5, 0): 0, (2, 2): 0, (10, 0): 0, (4, 1): 0, (1, 1): 0, (3, 2): 0, (0, 0): 0, (13, 2): 0, (8, 2): 0, (7, 1): 0, (6, 0): 0, (17, 1): 0, (11, 0): 0, (19, 2): 0, (16, 0): 0, (10, 1): 0, (4, 2): 0, (1, 0): 0, (14, 2): 0, (0, 1): 0, (13, 1): 0, (7, 0): 0, (15, 2): 0, (12, 0): 0, (9, 2): 0, (6, 1): 0, (3, 1): 0, (16, 1): 0, (2, 0): 0, (19, 1): 0, (5, 2): 0, (17, 0): 0, (13, 0): 0, (10, 2): 0}
    """
    modular_form_bases = \
      [ ModularForms(1, weight + 2*i) \
          .echelon_basis()[(0 if i == 0 else 1):]
        for i in xrange(index + 1) ]

    factory = JacobiFormD1NNFactory(precision, index)

    return [
        weak_jacbi_form_by_taylor_expansion(
            i * [0] + [modular_form_bases[i][j]] + (index - i) * [0],
            precision,
            True,
            weight=weight,
            factory=factory)
        for (i, j) in _theta_decomposition_indices(index, weight)
    ]
Example #5
0
    def _test__by_taylor_expansion(q_precision, weight, jacobi_index):
        r"""
        Run tests that validate by_taylor_expansions for various indices and weights.
        
        TESTS::
            
            sage: from psage.modform.jacobiforms.jacobiformd1nn_fegenerators import *
            sage: JacobiFormD1NNFactory_class._test__by_taylor_expansion(100, 10, 2)
            sage: JacobiFormD1NNFactory_class._test__by_taylor_expansion(20, 11, 2)
            sage: JacobiFormD1NNFactory_class._test__by_taylor_expansion(50, 9, 3)      # long time 
            sage: JacobiFormD1NNFactory_class._test__by_taylor_expansion(50, 10, 10)    # long time
            sage: JacobiFormD1NNFactory_class._test__by_taylor_expansion(30, 7, 15)     # long time  
        """
        from sage.rings import big_oh

        prec = JacobiFormD1NNFilter(q_precision, jacobi_index)
        factory = JacobiFormD1NNFactory(prec)
        R = PowerSeriesRing(ZZ, 'q')
        q = R.gen(0)

        if weight % 2 == 0:
            nmb_modular_forms = jacobi_index + 1
            start_weight = weight
        else:
            nmb_modular_forms = jacobi_index - 1
            start_weight = weight + 1

        modular_forms = list()
        for (i, k) in enumerate(
                range(start_weight, start_weight + 2 * nmb_modular_forms, 2)):
            modular_forms += [[lambda p: big_oh.O(q**p)
                               for _ in range(i)] + [b.qexp] + [
                                   lambda p: big_oh.O(q**p)
                                   for _ in range(nmb_modular_forms - 1 - i)
                               ] for b in ModularForms(1, k).echelon_basis()]

        for (fs_index, fs) in enumerate(modular_forms):
            expansion = factory.by_taylor_expansion(fs, weight, True)
            taylor_coefficients = JacobiFormD1NNFactory_class._test__jacobi_taylor_coefficients(
                expansion, weight, prec)
            predicted_taylor_coefficients = JacobiFormD1NNFactory_class._test__jacobi_predicted_taylor_coefficients(
                fs, q_precision)

            for (i, (proj, f)) in enumerate(
                    zip(taylor_coefficients, predicted_taylor_coefficients)):
                if f != proj:
                    raise AssertionError(
                        "{0}-th Taylor coefficient of the {1}-th Jacobi form is not correct. Expansions are\n  {2}\nand\n {3}"
                        .format(i, fs_index, proj, f))
Example #6
0
def _all_weak_taylor_coefficients(weight, index):
    r"""
    A product basis of the echelon bases of 
    
    - `M_k, M_{k + 2}, ..., M_{k + 2 m}` etc. if ``weight`` is even,
    
    - `M_{k + 1}, ..., M_{k + 2 m - 3}` if ``weight`` is odd.
    
    INPUT:
    
    - ``weight`` -- An integer.
    
    - ``index`` -- A non-negative integer.
    
    TESTS::
    
        sage: from psage.modform.jacobiforms.jacobiformd1nn_fegenerators import _all_weak_taylor_coefficients
        sage: _all_weak_taylor_coefficients(12, 1)
        [[<bound method ModularFormElement.qexp of 1 + 196560*q^2 + 16773120*q^3 + 398034000*q^4 + 4629381120*q^5 + O(q^6)>, <function <lambda> at ...>], [<bound method ModularFormElement.qexp of q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 + O(q^6)>, <function <lambda> at ...>], [<function <lambda> at ...>, <bound method ModularFormElement.qexp of 1 - 24*q - 196632*q^2 - 38263776*q^3 - 1610809368*q^4 - 29296875024*q^5 + O(q^6)>]]
    """
    R = PowerSeriesRing(ZZ, 'q')
    q = R.gen()

    if weight % 2 == 0:
        nmb_modular_forms = index + 1
        start_weight = weight
    else:
        nmb_modular_forms = index - 1
        start_weight = weight + 1

    modular_forms = list()
    for (i, k) in enumerate(
            range(start_weight, start_weight + 2 * nmb_modular_forms, 2)):
        modular_forms += [[lambda p: big_oh.O(q**p)
                           for _ in range(i)] + [b.qexp] + [
                               lambda p: big_oh.O(q**p)
                               for _ in range(nmb_modular_forms - 1 - i)
                           ] for b in ModularForms(1, k).echelon_basis()]

    return modular_forms
    def _I12(self, precision) :
        Delta = ModularForms(1,12).gen(0)
        assert Delta == ModularForms(1,12).cuspidal_subspace().gen(0)
 
        return SiegelModularFormG2MaassLift(lambda p: Delta.qexp(p), 0, precision, True, weight = 12)
 def _I10(self, precision) :
     # we use a standard generator, since its evaluation is much faster
     Delta = ModularForms(1,12).gen(0)
     assert Delta == ModularForms(1,12).cuspidal_subspace().gen(0)
     
     return SiegelModularFormG2MaassLift(0, lambda p: -(Delta.qexp(p)), precision, True, weight = 10)
    def _I6(self, precision) :
        E6 = ModularForms(1,6).gen(0)
 
        return SiegelModularFormG2MaassLift(lambda p: -84*(E6.qexp(p)), 0, precision, True, weight = 6)
 def _I4(self, precision) :
     E4 = ModularForms(1,4).gen(0)
         
     return SiegelModularFormG2MaassLift(lambda p: 60*(E4.qexp(p)), 0, precision, True, weight = 4)
    def _D_3_eisensteinseries(self, i):
        """
        Here we calculate the needed vector valued Eisenstein series of weights 3
        and 5 up to precision self._qexp_precision().
        The components of these Eisensteinseries are elliptic modular forms with
        respect to `\Gamma_1(36)` and `\Gamma(6)`, respectively. More precisely
        they lie in the subspace of Eisenstein series.
        
        INPUT:
            - i -- Integer; The parameter 0 returns the weight 3 case, 1 the weight 5 case. 
        
        OUTPUT:
            - Element of self.integral_power_series_ring().
        
        NOTE:
            With the help of some calculated Fourier coefficients it is hence possible
            to represent the components as linear combinations of elliptic modular
            forms, which is faster than calculating them directly via certain other
            formulas.

        TESTS::
            sage: fac = HermitianModularFormD2Factory(2,-3)
            sage: fac._D_3_eisensteinseries(0)
            {(1, 0): 27*q^4 + 216*q^10 + 459*q^16 + O(q^19), (0, 0): 1 + 72*q^6 + 270*q^12 + 720*q^18 + O(q^24), (-1, 0): 27*q^4 + 216*q^10 + 459*q^16 + O(q^19)}
            sage: fac._D_3_eisensteinseries(1)
            {(1, 0): -45*q^4 - 1872*q^10 - 11565*q^16 + O(q^19), (0, 0): 1 - 240*q^6 - 3690*q^12 - 19680*q^18 + O(q^24), (-1, 0): -45*q^4 - 1872*q^10 - 11565*q^16 + O(q^19)}
            sage: fac._D_3_eisensteinseries(2)
            Traceback (most recent call last):
            ...
            NotImplementedError: Only weight 3 and 5 implemented. Parameter i should be 0 or 1.
        """
        R = self.integral_power_series_ring()
        q = R.gen(0)

        # weight 3 case
        if i == 0:
            # the factors for the linear combinations were calculated by comparing
            # sufficiently many Fourier coefficients
            linear_combination_factors = ((1, 72, 270, 720, 936, 2160, 2214,
                                           3600, 4590, 6552, 5184, 10800, 9360,
                                           12240, 13500, 17712, 14760, 25920,
                                           19710, 26064, 28080, 36000, 25920,
                                           47520, 37638, 43272, 45900, 59040,
                                           46800, 75600, 51840, 69264, 73710,
                                           88560, 62208, 108000, 85176, 97740,
                                           122400, 88128),
                                          (0, 0, 0, 0, 27, 0, 0, 0, 0, 0, 216,
                                           0, 0, 0, 0, 0, 459, 0, 0, 0, 0, 0,
                                           1080, 0, 0, 0, 0, 0, 1350, 0, 0, 0,
                                           0, 0, 2592, 0, 0, 0, 0, 2808))
            echelon_basis_eisenstein_subspace_gamma_6_weight_3 = \
                          ModularForms(Gamma1(36),3).eisenstein_subspace() \
                            .q_echelon_basis(6*(self._qexp_precision() - 1) + 1)
            echelon_basis_eisenstein_subspace_gamma1_36_weight_3 = \
                          ModularForms(Gamma1(36),3).eisenstein_subspace() \
                            .q_echelon_basis(self._qexp_precision())

            e1 = R(
                sum(
                    map(operator.mul, linear_combination_factors[0],
                        echelon_basis_eisenstein_subspace_gamma1_36_weight_3)))
            e1 = e1.subs({q: q**6})
            e2 = R(
                sum(
                    map(operator.mul, linear_combination_factors[1],
                        echelon_basis_eisenstein_subspace_gamma_6_weight_3)))

            return {(0, 0): e1, (1, 0): e2, (-1, 0): e2}
        # weight 5 case
        elif i == 1:
            # the factors for the linear combinations were calculated by comparing sufficiently
            # many Fourier coefficients
            linear_combination_factors = (
                (1, -240, -3690, -19680, -57840, -153504, -295290, -576480,
                 -948330, -1594320, -2246400, -3601440, -4742880, -6854880,
                 -8863380, -12284064, -14803440, -20545920, -23914890,
                 -31277280, -36994464, -47271360, -52704000, -68840640,
                 -75889530, -93600240, -105393780, -129140160, -138931680,
                 -173990880, -184204800, -221645280, -242776170, -288203040,
                 -300672000, -368716608, -384231120, -480888180, -562100160,
                 -577324800),
                (0, 0, 0, 0, -45, 0, 0, 0, 0, 0, -1872, 0, 0, 0, 0, 0, -11565,
                 0, 0, 0, 0, 0, -43920, 0, 0, 0, 0, 0, -108090, 0, 0, 0, 0, 0,
                 -250560, 0, 0, 0, 0, -451152))
            echelon_basis_eisenstein_subspace_gamma_6_weight_5 = \
                          ModularForms(Gamma1(36),5).eisenstein_subspace() \
                            .q_echelon_basis(6*(self._qexp_precision() - 1)  + 1)
            echelon_basis_eisenstein_subspace_gamma1_36_weight_5 = \
                          ModularForms(Gamma1(36),5).eisenstein_subspace() \
                            .q_echelon_basis(self._qexp_precision())

            e1 = R(
                sum(
                    map(operator.mul, linear_combination_factors[0],
                        echelon_basis_eisenstein_subspace_gamma1_36_weight_5)))
            e1 = e1.subs({q: q**6})
            e2 = R(
                sum(
                    map(operator.mul, linear_combination_factors[1],
                        echelon_basis_eisenstein_subspace_gamma_6_weight_5)))

            return {(0, 0): e1, (1, 0): e2, (-1, 0): e2}
        else:
            raise NotImplementedError( "Only weight 3 and 5 implemented. " + \
                                       "Parameter i should be 0 or 1." )
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