示例#1
0
    def local_coordinates_at_nonweierstrass(self, P, prec=20, name='t'):
        """
        For a non-Weierstrass point `P = (a,b)` on the hyperelliptic
        curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`,
        where `t = x - a` is the local parameter.

        INPUT:

        - ``P = (a, b)`` -- a non-Weierstrass point on self
        - ``prec`` --  desired precision of the local coordinates
        - ``name`` -- gen of the power series ring (default: ``t``)

        OUTPUT:

        `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x - a`
        is the local parameter at `P`

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x)
            sage: P = H(1,6)
            sage: x,y = H.local_coordinates_at_nonweierstrass(P,prec=5)
            sage: x
            1 + t + O(t^5)
            sage: y
            6 + t - 7/2*t^2 - 1/2*t^3 - 25/48*t^4 + O(t^5)
            sage: Q = H(-2,12)
            sage: x,y = H.local_coordinates_at_nonweierstrass(Q,prec=5)
            sage: x
            -2 + t + O(t^5)
            sage: y
            12 - 19/2*t - 19/32*t^2 + 61/256*t^3 - 5965/24576*t^4 + O(t^5)

        AUTHOR:

            - Jennifer Balakrishnan (2007-12)
        """
        d = P[1]
        if d == 0:
            raise TypeError(
                "P = %s is a Weierstrass point. Use local_coordinates_at_weierstrass instead!"
                % P)
        pol = self.hyperelliptic_polynomials()[0]
        L = PowerSeriesRing(self.base_ring(), name)
        t = L.gen()
        L.set_default_prec(prec)
        K = PowerSeriesRing(L, 'x')
        pol = K(pol)
        x = K.gen()
        b = P[0]
        f = pol(t + b)
        for i in range((RR(log(prec) / log(2))).ceil()):
            d = (d + f / d) / 2
        return t + b + O(t**(prec)), d + O(t**(prec))
    def local_coordinates_at_nonweierstrass(self, P, prec=20, name='t'):
        """
        For a non-Weierstrass point `P = (a,b)` on the hyperelliptic
        curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`,
        where `t = x - a` is the local parameter.

        INPUT:

        - ``P = (a, b)`` -- a non-Weierstrass point on self
        - ``prec`` --  desired precision of the local coordinates
        - ``name`` -- gen of the power series ring (default: ``t``)

        OUTPUT:

        `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x - a`
        is the local parameter at `P`

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x)
            sage: P = H(1,6)
            sage: x,y = H.local_coordinates_at_nonweierstrass(P,prec=5)
            sage: x
            1 + t + O(t^5)
            sage: y
            6 + t - 7/2*t^2 - 1/2*t^3 - 25/48*t^4 + O(t^5)
            sage: Q = H(-2,12)
            sage: x,y = H.local_coordinates_at_nonweierstrass(Q,prec=5)
            sage: x
            -2 + t + O(t^5)
            sage: y
            12 - 19/2*t - 19/32*t^2 + 61/256*t^3 - 5965/24576*t^4 + O(t^5)

        AUTHOR:

            - Jennifer Balakrishnan (2007-12)
        """
        d = P[1]
        if d == 0:
            raise TypeError("P = %s is a Weierstrass point. Use local_coordinates_at_weierstrass instead!"%P)
        pol = self.hyperelliptic_polynomials()[0]
        L = PowerSeriesRing(self.base_ring(), name)
        t = L.gen()
        L.set_default_prec(prec)
        K = PowerSeriesRing(L, 'x')
        pol = K(pol)
        x = K.gen()
        b = P[0]
        f = pol(t+b)
        for i in range((RR(log(prec)/log(2))).ceil()):
            d = (d + f/d)/2
        return t+b+O(t**(prec)), d + O(t**(prec))
示例#3
0
 def get_action_data(self, g, K=None):
     a, b, c, d = g.list()
     prec = self._prec
     if K is None:
         if hasattr(a, 'lift'):
             a, b, c, d = a.lift(), b.lift(), c.lift(), d.lift()
             p = g.parent().base_ring().prime()
             K = ZpCA(p, prec)
         else:
             K = g.parent().base_ring()
     Ps = PowerSeriesRing(K, 't', default_prec=prec)
     z = Ps.gen()
     zz = (d * z - b) / (-c * z + a)
     zz_ps0 = Ps(zz).add_bigoh(prec)
     if self._dlog:
         zz_ps = ((a * d - b * c) * (-c * z + a)**-2).add_bigoh(prec)
     else:
         zz_ps = Ps(1).add_bigoh(prec)
     if self.is_additive():
         M = Matrix(ZZ, prec, prec, 0)
         for j in range(prec):
             for i, aij in enumerate(zz_ps.list()):
                 M[i, j] = aij
             if j < prec - 1:  # Don't need the last multiplication
                 zz_ps = (zz_ps0 * zz_ps).add_bigoh(prec)
             else:
                 return M
     else:
         ans = [Ps(1), zz_ps]
         for _ in range(prec - 1):
             zz_ps = (zz_ps0 * zz_ps).add_bigoh(prec)
             ans.append(zz_ps)
         return ans
def eis_H(a, b, N, k, Q=None, t=1, prec=10):
    if Q == None:
        Q = PowerSeriesRing(CyclotomicField(N), 'q')
    R = Q.base_ring()
    zetaN = R.zeta(N)
    q = Q.gen()
    a = ZZ(a % N)
    b = ZZ(b % N)
    s = 0

    if k == 1:
        if a == 0 and not b == 0:
            s = -QQ(1) / QQ(2) * (1 + zetaN**b) / (1 - zetaN**b)
        elif b == 0 and not a == 0:
            s = -QQ(1) / QQ(2) * (1 + zetaN**a) / (1 - zetaN**a)
        elif a != 0 and b != 0:
            s = -QQ(1) / QQ(2) * ((1 + zetaN**a) / (1 - zetaN**a) +
                                  (1 + zetaN**b) / (1 - zetaN**b))
    elif k > 1:
        s = hurwitz_hat(-b, N, 1 - k, zetaN)

    for m in srange(1, prec / t):
        for n in srange(1, prec / t / m + 1):
            s += (zetaN**(-a * m - b * n) +
                  (-1)**k * zetaN**(a * m + b * n)) * n**(k - 1) * q**(m * n)
    return s + O(q**floor(prec))
    def _test__jacobi_predicted_taylor_coefficients(fs, q_precision) :
        r"""
        Given a list of power series, which are the corrected Taylor coefficients
        of a Jacobi form, return the renormalized uncorrected ones, assuming that
        all but one `f` vanish.
        
        INPUT:
        
        - ``fs`` -- A list of power series.
        
        - ``q_precision`` -- An integer.
        
        OUPUT:
        
        - A list of power series.
        
        TESTS:
        
        See jacobi_form_by_taylor_expansion.
        """
        from sage.rings.arith import gcd
        
        R = PowerSeriesRing(ZZ, 'q'); q = R.gen(0)
        
        diff = lambda f: f.derivative().shift(1)
        normalize = lambda f: f / gcd(f.list()) if f != 0 else f
        diffnorm = lambda f,l: normalize(reduce(lambda a, g: g(a), l*[diff], f))

        taylor_coefficients = list()
        allf = R(0)
        for f in fs :
            allf = f(q_precision) + diffnorm(allf, 1)            
            taylor_coefficients.append(allf)

        return taylor_coefficients
    def pad(self, target_param_level):
        """
        When constructed the q-expansions are in the default parameter $q_N$,
        where $N$ is the level of the form.  When taking products, the
        parameters should be in $q_{N'}$, where $N'$ is the target level.
        This helper function peforms that renormalization by padding with zeros.
        """
        try:
            assert (target_param_level % self.param_level == 0)
        except AssertionError:
            print(
                "target parameter level should be a multiple of current parameter level"
            )
            return

        shift = int(target_param_level / self.param_level)
        R = self.series.base_ring()
        Q = PowerSeriesRing(R, 'q' + str(target_param_level))
        qN = Q.gen()
        s = 0
        for i in range(self.series.prec() * shift):
            if i % shift == 0:
                s += self.series[int(i / shift)] * qN**i
            else:
                s += 0 * qN**i
        s += O(qN**(self.series.prec() * shift + shift - 1))
        self.series = s
        self.param_level = target_param_level
示例#7
0
def form_acting_matrix_on_dist(p,M,k,a,b,c,d):
	"""forms a large M x M matrix say G such that if v is the vector of moments of a distribution mu, then v*G is the vector of moments of mu|[a,b;c,d]"""

#	print("Checking...")
#	print(a,b,c,d)
#	print(p)

	assert (a%p != 0) and (c%p == 0), "acting by bad matrix"

	R=PowerSeriesRing(QQ,'y',default_prec=M)
	y=R.gen()

	scale=(b+d*y)/(a+c*y)
	t=((a+c*y)**k).truncate(M)

	A = []
	for i in range(0,M):
		temp1=t.list();
		d=len(temp1)
		for j in range(d,M):
			temp1 = temp1 + [0]	
		#while len(temp1)>M:
		#	temp1.pop()
		A = A + [temp1]	
		t=(t*scale).truncate(M)
	q=p**M
	B=Matrix(QQ,A).transpose()
	for r in range(0,M):
		for c in range(0,M):
			#B[r,c]=B[r,c]%(p**(M-c))
			B[r,c]=B[r,c]%(q)

	return B
def eis_G(a, b, N, k, Q=None, t=1, prec=20):
    if Q == None:
        Q = PowerSeriesRing(QQ, 'q')
    R = Q.base_ring()
    q = Q.gen()
    a = ZZ(a % N)
    b = ZZ(b % N)
    s = 0

    if k == 1:
        if a == 0 and not b == 0:
            s = QQ(1) / QQ(2) - QQ(b % N) / QQ(N)
        elif b == 0 and not a == 0:
            s = QQ(1) / QQ(2) - QQ(a % N) / QQ(N)
    elif k > 1:
        if b == 0:
            s = -N**(k - 1) * ber_pol(QQ(a % N) / QQ(N), k) / QQ(k)

    #If a == 0 or b ==0 the loop has to start at 1
    starta, startb = 0, 0
    if a == 0:
        starta = 1
    if b == 0:
        startb = 1

    for v in srange(starta, (prec / t + a) / N):
        for w in srange(startb, (prec / t / abs((-a + v * N)) + b) / N + 1):
            s += q**(t * (a + v * N) * (b + w * N)) * (a + v * N)**(k - 1)
            if (-a + v * N) > 0 and (-b + w * N) > 0:
                s += (-1)**k * q**(t * (-a + v * N) *
                                   (-b + w * N)) * (-a + v * N)**(k - 1)
    return s + O(q**floor(prec))
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 eis_phipsi(phi, psi, k, prec=10, t=1, cmplx=False):
    r"""
    Return Fourier expansion of Eisenstein series at the cusp oo.

    INPUT:

    - ``phi`` -- Dirichlet character.
    - ``psi`` -- Dirichlet character.
    - ``k`` -- integer, the weight of the Eistenstein series.
    - ``prec`` -- integer (default: 10).
    - ``t`` -- integer (default: 1).

    OUTPUT:

    The Fourier expansion of the Eisenstein series $E_k^{\phi,\psi, t}$ (as
    defined by [Diamond-Shurman]).

    EXAMPLES:
    sage: phi = DirichletGroup(3)[1]
    sage: psi = DirichletGroup(5)[1]
    sage: E = eisenstein_series_at_inf(phi, psi, 4)
    """
    N1, N2 = phi.level(), psi.level()
    N = N1 * N2
    #The Fourier expansion of the Eisenstein series at infinity is in the field Q(zeta_Ncyc)
    Ncyc = lcm([euler_phi(N1), euler_phi(N2)])
    if cmplx == True:
        CC = ComplexField(53)
        pi = ComplexField().pi()
        I = ComplexField().gen()
        R = CC
        zeta = CC(exp(2 * pi * I / Ncyc))
    else:
        R = CyclotomicField(Ncyc)
        zeta = R.zeta(Ncyc)
        phi, psi = phi.base_extend(R), psi.base_extend(R)
    Q = PowerSeriesRing(R, 'q')
    q = Q.gen()
    s = O(q**prec)

    #Weight 2 with trivial characters is calculated separately
    if k == 2 and phi.conductor() == 1 and psi.conductor() == 1:
        if t == 1:
            raise TypeError('E_2 is not a modular form.')
        s = 1 / 24 * (t - 1)
        for m in srange(1, prec):
            for n in srange(1, prec / m + 1):
                s += n * (q**(m * n) - t * q**(m * n * t))
        return s + O(q**prec)

    if psi.level() == 1 and k == 1:
        s -= phi.bernoulli(k) / k
    elif phi.level() == 1:
        s -= psi.bernoulli(k) / k

    for m in srange(1, prec / t):
        for n in srange(1, prec / t / m + 1):
            s += 2 * phi(m) * psi(n) * n**(k - 1) * q**(m * n * t)
    return s + O(q**prec)
示例#11
0
 def _repr_(self):
     r"""
     Returns the representation of self as a string.
     """
     R = PowerSeriesRing(self._parent._R,default_prec=self._depth,name='z')
     z = R.gen()
     s = str(sum([R(self._val[ii,0]*z**ii) for ii in range(self._depth)]))
     return s
def _sa_coefficients_lambda_(K, beta=0):
    r"""
    Return the coefficients `\lambda_{k, \ell}(\beta)` used in singularity analysis.

    INPUT:

    - ``K`` -- an integer.

    - ``beta`` -- (default: `0`) the order of the logarithmic
      singularity.

    OUTPUT:

    A dictionary mapping pairs of indices to rationals.

    .. SEEALSO::

        :meth:`~AsymptoticExpansionGenerators.SingularityAnalysis`

    TESTS::

        sage: from sage.rings.asymptotic.asymptotic_expansion_generators \
        ....:     import _sa_coefficients_lambda_
        sage: _sa_coefficients_lambda_(3)
        {(0, 0): 1,
         (1, 1): -1,
         (1, 2): 1/2,
         (2, 2): 1,
         (2, 3): -5/6,
         (2, 4): 1/8,
         (3, 3): -1,
         (3, 4): 13/12,
         (4, 4): 1}
        sage: _sa_coefficients_lambda_(3, beta=1)
        {(0, 0): 1,
         (1, 1): -2,
         (1, 2): 1/2,
         (2, 2): 3,
         (2, 3): -4/3,
         (2, 4): 1/8,
         (3, 3): -4,
         (3, 4): 29/12,
         (4, 4): 5}
    """
    from sage.rings.laurent_series_ring import LaurentSeriesRing
    from sage.rings.power_series_ring import PowerSeriesRing
    from sage.rings.rational_field import QQ

    V = LaurentSeriesRing(QQ, names='v', default_prec=K)
    v = V.gen()
    T = PowerSeriesRing(V, names='t', default_prec=2*K-1)
    t = T.gen()

    S = (t - (1+1/v+beta) * (1+v*t).log()).exp()
    return dict(((k + L.valuation(), ell), c)
                for ell, L in enumerate(S.list())
                for k, c in enumerate(L.list()))
示例#13
0
    def _compute_acting_matrix(self, g, M):
        r"""
        

        INPUT:

        - ``g`` -- an instance of
          :class:`sage.matrices.matrix_integer_2x2.Matrix_integer_2x2`
          or :class:`sage.matrix.matrix_generic_dense.Matrix_generic_dense`

        - ``M`` -- a positive integer giving the precision at which
          ``g`` should act.

        OUTPUT:

        - 

        EXAMPLES::

            sage: from sage.modular.pollack_stevens.distributions import Distributions, Symk
        """
        #tim = verbose("Starting")
        a, b, c, d = self._adjuster(g)
        # if g.parent().base_ring().is_exact():
        #     self._check_mat(a, b, c, d)
        k = self._k
        if g.parent().base_ring() is ZZ:
            if self._symk:
                base_ring = QQ
            else:
                base_ring = Zmod(self._p**M)
        else:
            base_ring = self.underlying_set().base_ring()
        #cdef Matrix B = matrix(base_ring,M,M)
        B = matrix(base_ring,M,M) #
        if M == 0:
            return B.change_ring(self.codomain().base_ring())
        R = PowerSeriesRing(base_ring, 'y', default_prec = M)
        y = R.gen()
        #tim = verbose("Checked, made R",tim)
        # special case for small precision, large weight
        scale = (b+d*y)/(a+c*y)
        t = (a+c*y)**k # will already have precision M
        #cdef long row, col #
        #tim = verbose("Made matrix",tim)
        for col in range(M):
            for row in range(M):
                B.set_unsafe(row, col, t[row])
            t *= scale
        #verbose("Finished loop",tim)
        # the changering here is annoying, but otherwise we have to change ring each time we multiply
        B = B.change_ring(self.codomain().base_ring())
        if self._character is not None:
            B *= self._character(a)
        if self._dettwist is not None:
            B *= (a*d - b*c)**(self._dettwist)
        return B
def _sa_coefficients_lambda_(K, beta=0):
    r"""
    Return the coefficients `\lambda_{k, \ell}(\beta)` used in singularity analysis.

    INPUT:

    - ``K`` -- an integer.

    - ``beta`` -- (default: `0`) the order of the logarithmic
      singularity.

    OUTPUT:

    A dictionary mapping pairs of indices to rationals.

    .. SEEALSO::

        :meth:`~AsymptoticExpansionGenerators.SingularityAnalysis`

    TESTS::

        sage: from sage.rings.asymptotic.asymptotic_expansion_generators \
        ....:     import _sa_coefficients_lambda_
        sage: _sa_coefficients_lambda_(3)
        {(0, 0): 1,
         (1, 1): -1,
         (1, 2): 1/2,
         (2, 2): 1,
         (2, 3): -5/6,
         (2, 4): 1/8,
         (3, 3): -1,
         (3, 4): 13/12,
         (4, 4): 1}
        sage: _sa_coefficients_lambda_(3, beta=1)
        {(0, 0): 1,
         (1, 1): -2,
         (1, 2): 1/2,
         (2, 2): 3,
         (2, 3): -4/3,
         (2, 4): 1/8,
         (3, 3): -4,
         (3, 4): 29/12,
         (4, 4): 5}
    """
    from sage.rings.laurent_series_ring import LaurentSeriesRing
    from sage.rings.power_series_ring import PowerSeriesRing
    from sage.rings.rational_field import QQ

    V = LaurentSeriesRing(QQ, names='v', default_prec=K)
    v = V.gen()
    T = PowerSeriesRing(V, names='t', default_prec=2 * K - 1)
    t = T.gen()

    S = (t - (1 + 1 / v + beta) * (1 + v * t).log()).exp()
    return dict(((k + L.valuation(), ell), c) for ell, L in enumerate(S.list())
                for k, c in enumerate(L.list()))
示例#15
0
def faithful_generating_function(normal_subgroups, character_dims):
    R = PowerSeriesRing(ZZ, 'x', 200)  ## FIX ME!
    x = R.gen()
    f = R(0)
    for N, Ndata in normal_subgroups.items():
        count, quotient, mobius = Ndata
        f += count * mobius * prod(
            [(1 - x**d).inverse_of_unit()**m
             for (d, m) in character_dims[quotient].items()])
    return f
示例#16
0
    def local_coordinates_at_weierstrass(self, P, prec=20, name='t'):
        """
        For a finite Weierstrass point on the hyperelliptic
        curve `y^2 = f(x)`, returns `(x(t), y(t))` such that
        `(y(t))^2 = f(x(t))`, where `t = y` is the local parameter.

        INPUT:

        - ``P`` -- a finite Weierstrass point on self
        - ``prec`` -- desired precision of the local coordinates
        - ``name`` -- gen of the power series ring (default: `t`)

        OUTPUT:

        `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = y`
        is the local parameter at `P`

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x)
            sage: A = H(4, 0)
            sage: x, y = H.local_coordinates_at_weierstrass(A, prec=7)
            sage: x
            4 + 1/360*t^2 - 191/23328000*t^4 + 7579/188956800000*t^6 + O(t^7)
            sage: y
            t + O(t^7)
            sage: B = H(-5, 0)
            sage: x, y = H.local_coordinates_at_weierstrass(B, prec=5)
            sage: x
            -5 + 1/1260*t^2 + 887/2000376000*t^4 + O(t^5)
            sage: y
            t + O(t^5)

        AUTHOR:
            - Jennifer Balakrishnan (2007-12)

            - Francis Clarke (2012-08-26)
        """
        if P[1] != 0:
            raise TypeError(
                "P = %s is not a finite Weierstrass point. Use local_coordinates_at_nonweierstrass instead!"
                % P)
        L = PowerSeriesRing(self.base_ring(), name)
        t = L.gen()
        pol = self.hyperelliptic_polynomials()[0]
        pol_prime = pol.derivative()
        b = P[0]
        t2 = t**2
        c = b + t2 / pol_prime(b)
        c = c.add_bigoh(prec)
        for _ in range(int(1 + log(prec, 2))):
            c -= (pol(c) - t2) / pol_prime(c)
        return (c, t.add_bigoh(prec))
示例#17
0
 def _repr_(self):
     r"""
     Returns the representation of self as a string.
     """
     R = PowerSeriesRing(self._parent._R,
                         default_prec=self._depth,
                         name='z')
     z = R.gen()
     s = str(sum([R(self._val[ii, 0] * z**ii)
                  for ii in range(self._depth)]))
     return s
    def local_coordinates_at_weierstrass(self, P, prec=20, name='t'):
        """
        For a finite Weierstrass point on the hyperelliptic
        curve `y^2 = f(x)`, returns `(x(t), y(t))` such that
        `(y(t))^2 = f(x(t))`, where `t = y` is the local parameter.

        INPUT:

        - ``P`` -- a finite Weierstrass point on self
        - ``prec`` -- desired precision of the local coordinates
        - ``name`` -- gen of the power series ring (default: `t`)

        OUTPUT:

        `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = y`
        is the local parameter at `P`

        EXAMPLES::

            sage: R.<x> = QQ['x']
            sage: H = HyperellipticCurve(x^5-23*x^3+18*x^2+40*x)
            sage: A = H(4, 0)
            sage: x, y = H.local_coordinates_at_weierstrass(A, prec=7)
            sage: x
            4 + 1/360*t^2 - 191/23328000*t^4 + 7579/188956800000*t^6 + O(t^7)
            sage: y
            t + O(t^7)
            sage: B = H(-5, 0)
            sage: x, y = H.local_coordinates_at_weierstrass(B, prec=5)
            sage: x
            -5 + 1/1260*t^2 + 887/2000376000*t^4 + O(t^5)
            sage: y
            t + O(t^5)

        AUTHOR:
            - Jennifer Balakrishnan (2007-12)

            - Francis Clarke (2012-08-26)
        """
        if P[1] != 0:
            raise TypeError("P = %s is not a finite Weierstrass point. Use local_coordinates_at_nonweierstrass instead!"%P)
        L = PowerSeriesRing(self.base_ring(), name)
        t = L.gen()
        pol = self.hyperelliptic_polynomials()[0]
        pol_prime = pol.derivative()
        b = P[0]
        t2  = t**2
        c = b + t2/pol_prime(b)
        c = c.add_bigoh(prec)
        for _ in range(1 + log(prec, 2)):
            c -= (pol(c) - t2)/pol_prime(c)
        return (c, t.add_bigoh(prec))
def eisenstein_series_at_inf(phi, psi, k, prec=10, t=1, base_ring=None):
    r"""
    Return Fourier expansion of Eistenstein series at a cusp.

    INPUT:

    - ``phi`` -- Dirichlet character.
    - ``psi`` -- Dirichlet character.
    - ``k`` -- integer, the weight of the Eistenstein series.
    - ``prec`` -- integer (default: 10).
    - ``t`` -- integer (default: 1).

    OUTPUT:

    The Fourier expansion of the Eisenstein series $E_k^{\phi,\psi, t}$ (as
    defined by [Diamond-Shurman]) at the specific cusp.

    EXAMPLES:
    sage: phi = DirichletGroup(3)[1]
    sage: psi = DirichletGroup(5)[1]
    sage: E = eisenstein_series_at_inf(phi, psi, 4)
    """
    N1, N2 = phi.level(), psi.level()
    N = N1 * N2
    #The Fourier expansion of the Eisenstein series at infinity is in the field Q(zeta_Ncyc)
    Ncyc = lcm([euler_phi(N1), euler_phi(N2)])
    if base_ring == None:
        base_ring = CyclotomicField(Ncyc)
    Q = PowerSeriesRing(base_ring, 'q')
    q = Q.gen()
    s = O(q**prec)

    #Weight 2 with trivial characters is calculated separately
    if k == 2 and phi.conductor() == 1 and psi.conductor() == 1:
        if t == 1:
            raise TypeError('E_2 is not a modular form.')
        s = 1 / 24 * (t - 1)
        for m in srange(1, prec):
            for n in srange(1, prec / m):
                s += n * (q**(m * n) - t * q**(m * n * t))
        return s + O(q**prec)

    if psi.level() == 1 and k == 1:
        s -= phi.bernoulli(k) / k
    elif phi.level() == 1:
        s -= psi.bernoulli(k) / k

    for m in srange(1, prec / t):
        for n in srange(1, prec / t / m + 1):
            s += 2 * base_ring(phi(m)) * base_ring(
                psi(n)) * n**(k - 1) * q**(m * n * t)
    return s + O(q**prec)
示例#20
0
    def _test__jacobi_taylor_coefficients(expansion, weight, prec=None):
        r"""
        Compute the renormalized Taylor coefficients of
        a Jacobi form.
        
        INPUT:
        
        - ``expansion`` -- A Fourier expansion or a dictionary with corresponding keys.
        
        - ``weight`` -- An integer.
        
        - ``prec`` -- A filter for Fourier expansions, of if ``expansion`` is a Fourier expansion
                      possibly ``None``.
        
        OUTPUT:
        
        - A list of power series in `q`.
        """
        from sage.rings.arith import gcd

        if prec is None:
            prec = expansion.precision()
        jacobi_index = prec.jacobi_index()
        q_precision = prec.index()
        R = PowerSeriesRing(ZZ, 'q')
        q = R.gen(0)

        weak_prec = JacobiFormD1NNFilter(prec, reduced=False, weak_forms=True)
        indices = JacobiFormD1NNIndices(jacobi_index)

        projs = list()
        for pw in (range(0, 2 * jacobi_index + 1, 2) if weight %
                   2 == 0 else range(1, 2 * jacobi_index - 1, 2)):
            proj = dict((n, 0) for n in range(q_precision))
            for (n, r) in weak_prec:
                ((nred, rred), sign) = indices.reduce((n, r))
                try:
                    proj[n] += (sign * r)**pw * expansion[(nred, rred)]
                except (KeyError, ValueError):
                    pass

            projs.append(proj)

        gcd_projs = [gcd(proj.values()) for proj in projs]
        gcd_projs = [g if g != 0 else 1 for g in gcd_projs]
        projs = [sorted(proj.iteritems()) for proj in projs]
        projs = [
            R([c for (_, c) in proj]).add_bigoh(q_precision) / gcd_proj
            for (proj, gcd_proj) in zip(projs, gcd_projs)
        ]

        return projs
def eis_E(cv, dv, N, k, Q=None, param_level=1, prec=10):
    r"""
    Computes the coefficient of the Eisenstein series for $\Gamma(N)$.
    Not intended to be called by user.
    INPUT:
    - cv - int, the first coordinate of the vector determining the \Gamma(N)
      Eisenstein series
    - dv - int, the second coordinate of the vector determining the \Gamma(N)
      Eisenstein series
    - N - int, the level of the Eisenstein series to be computed
    - k - int, the weight of the Eisenstein seriess to be computed
    - Q - power series ring, the ring containing the q-expansion to be computed
    - param_level - int, the parameter of the returned series will be
      q_{param_level}
    - prec - int, the precision.  The series in q_{param_level} will be truncated
      after prec coefficients
    OUTPUT:
    - an element of the ring Q, which is the Fourier expansion of the Eisenstein
      series
    """
    if Q == None:
        Q = PowerSeriesRing(CyclotomicField(N), 'q')
    R = Q.base_ring()
    zetaN = R.zeta(N)
    q = Q.gen()

    cv = cv % N
    dv = dv % N

    #if dv == 0 and cv == 0 and k == 2:
    #   raise ValueError("E_2 is not a modular form")

    if k == 1:
        if cv == 0 and dv == 0:
            raise ValueError("that shouldn't have happened...")
        elif cv == 0 and dv != 0:
            s = QQ(1) / QQ(2) * (1 + zetaN**dv) / (1 - zetaN**dv)
        elif cv != 0:
            s = QQ(1) / QQ(2) - QQ(cv) / QQ(N) + floor(QQ(cv) / QQ(N))
    elif k > 1:
        if cv == 0:
            s = hurwitz_hat(QQ(dv), QQ(N), 1 - k, zetaN)
        else:
            s = 0
    for n1 in xrange(1, prec):  # this is n/m in DS
        for n2 in xrange(1, prec / n1 + 1):  # this is m in DS
            if Mod(n1, N) == Mod(cv, N):
                s += n2**(k - 1) * zetaN**(dv * n2) * q**(n1 * n2)
            if Mod(n1, N) == Mod(-cv, N):
                s += (-1)**k * n2**(k - 1) * zetaN**(-dv * n2) * q**(n1 * n2)
    return s + O(q**floor(prec))
示例#22
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))
示例#23
0
    def _compute_acting_matrix(self, g, M):
        r"""
        

        INPUT:

        - ``g`` -- an instance of
          :class:`sage.matrices.matrix_integer_2x2.Matrix_integer_2x2`
          or :class:`sage.matrix.matrix_generic_dense.Matrix_generic_dense`

        - ``M`` -- a positive integer giving the precision at which
          ``g`` should act.

        OUTPUT:

        - 
        """
        #tim = verbose("Starting")
        a, b, c, d = self._adjuster(g)
        # if g.parent().base_ring().is_exact():
        #     self._check_mat(a, b, c, d)
        #k = self._k
        base_ring = self.domain().base_ring()
        #cdef Matrix B = matrix(base_ring,M,M)
        B = matrix(base_ring, M, M) #
        if M == 0:
            return B.change_ring(self.codomain().base_ring())
        R = PowerSeriesRing(base_ring, 'y', default_prec = M)
        y = R.gen()
        #tim = verbose("Checked, made R",tim)
        # special case for small precision, large weight
        scale = (b+d*y)/(a+c*y)
        #t = (a+c*y)**k # will already have precision M
        t = R.one()
        #cdef long row, col #
        #tim = verbose("Made matrix",tim)
        for col in range(M):
            for row in range(M):
                #B.set_unsafe(row, col, t[row])
                B[row, col] = t[row]
            t *= scale
        #verbose("Finished loop",tim)
        # the change_ring here is annoying, but otherwise we have to change ring each time we multiply
        B = B.change_ring(self.codomain().base_ring())
        #if self._character is not None:
        #    B *= self._character(a)
        if self._dettwist is not None:
            B *= (a*d - b*c) ** (self._dettwist)
        return B
示例#24
0
    def _repr_(self):
        r"""
        This returns the representation of self as a string.

        EXAMPLES:

        This example illustrates ...

        ::

        """
        R=PowerSeriesRing(self._parent._R,default_prec=self._depth,name='z')
        z=R.gen()
        s=str(sum([R(self._val[ii,0]*z**ii) for ii in range(self._depth)]))
        return s
示例#25
0
    def _test__jacobi_torsion_point(phi, weight, torsion_point):
        r"""
        Given a list of power series, which are the corrected Taylor coefficients
        of a Jacobi form, return the specialization to ``torsion_point``.
        
        INPUT:
        
        - ``phi`` -- A Fourier expansion of a Jacobi form.
        
        - ``weight`` -- An integer.
        
        - ``torsion_point`` -- A rational.
        
        OUPUT:
        
        - A power series.
        
        TESTS:
                
        See jacobi_form_by_taylor_expansion.
        
            sage: from psage.modform.jacobiforms.jacobiformd1nn_fegenerators import *
            sage: from psage.modform.jacobiforms.jacobiformd1nn_types import *
            sage: precision = 50
            sage: weight = 10; index = 7
            sage: phis = [jacobi_form_by_taylor_expansion(i, JacobiFormD1NNFilter(precision, index), weight) for i in range(JacobiFormD1NN_Gamma(weight, index)._rank(QQ))]
            sage: fs = [JacobiFormD1NNFactory_class._test__jacobi_torsion_point(phi, weight, 2/3) for phi in phis]
            sage: fs_vec = [vector(f.padded_list(precision)) for f in fs]
            sage: mf_span = span([vector(b.qexp(precision).padded_list(precision)) for b in ModularForms(GammaH(9, [4]), weight).basis()])
            sage: all(f_vec in mf_span for f_vec in fs_vec)
            True
        
        FIXME: The case of torsion points of order 5, which should lead to forms for Gamma1(25) fails even in the simplest case.
        """
        from sage.rings.all import CyclotomicField

        K = CyclotomicField(QQ(torsion_point).denominator())
        zeta = K.gen()
        R = PowerSeriesRing(K, 'q')
        q = R.gen(0)

        ch = JacobiFormD1WeightCharacter(weight)

        coeffs = dict((n, QQ(0)) for n in range(phi.precision().index()))
        for (n, r) in phi.precision().monoid_filter():
            coeffs[n] += zeta**r * phi[(ch, (n, r))]

        return PowerSeriesRing(K, 'q')(coeffs)
    def _test__jacobi_taylor_coefficients(expansion, weight, prec = None) :
        r"""
        Compute the renormalized Taylor coefficients of
        a Jacobi form.
        
        INPUT:
        
        - ``expansion`` -- A Fourier expansion or a dictionary with corresponding keys.
        
        - ``weight`` -- An integer.
        
        - ``prec`` -- A filter for Fourier expansions, of if ``expansion`` is a Fourier expansion
                      possibly ``None``.
        
        OUTPUT:
        
        - A list of power series in `q`.
        """
        from sage.rings.arith import gcd
        
        if prec is None :
            prec = expansion.precision()
        jacobi_index = prec.jacobi_index()
        q_precision = prec.index()
        R = PowerSeriesRing(ZZ, 'q'); q = R.gen(0)
                
        weak_prec = JacobiFormD1NNFilter(prec, reduced = False, weak_forms = True)
        indices = JacobiFormD1NNIndices(jacobi_index)

        projs = list()
        for pw in (range(0, 2 * jacobi_index + 1, 2) if weight % 2 == 0 else range(1, 2 * jacobi_index - 1, 2)) :
            proj = dict( (n, 0) for n in range(q_precision) )
            for (n, r) in weak_prec :
                ((nred, rred), sign) = indices.reduce((n,r))
                try :
                    proj[n] +=  (sign * r)**pw * expansion[(nred, rred)]
                except (KeyError, ValueError) :
                    pass
            
            projs.append(proj)

        gcd_projs = [gcd(proj.values()) for proj in projs]
        gcd_projs = [g if g != 0 else 1 for g in gcd_projs]
        projs = [sorted(proj.iteritems()) for proj in projs]
        projs = [ R([c for (_, c) in proj]).add_bigoh(q_precision) / gcd_proj
                  for (proj, gcd_proj) in zip(projs, gcd_projs) ]
        
        return projs
示例#27
0
class MeromorphicFunctions(Parent, CachedRepresentation):
    Element = MeromorphicFunctionsElement

    def __init__(self, K, additive=False):
        Parent.__init__(self)
        self._additive = additive
        self._base_ring = K
        self._prec = K.precision_cap()
        self._Ps = PowerSeriesRing(self._base_ring,
                                   names='t',
                                   default_prec=self._prec)
        t = self._Ps.gen()
        p = K.prime()
        self._Ps_local_variable = lambda Q: 1 - t / Q
        self._unset_coercions_used()
        self.register_action(Scaling(ZZ, self))
        self.register_action(MatrixAction(MatrixSpace(K, 2, 2), self))

    @cached_method
    def get_action_data(self, g):
        a, b, c, d = g.list()
        K = g.parent().base_ring()
        R = PolynomialRing(K, 'z')
        Ps = PowerSeriesRing(K, 't', default_prec=self._prec)
        z = R.gen()
        zz = (d * z - b) / (-c * z + a)
        zz_ps = Ps(zz).add_bigoh(self._prec)
        ans = [Ps(1), zz_ps]
        for _ in range(self._prec - 1):
            ans.append((zz_ps * ans[-1]).add_bigoh(self._prec))
        return ans

    def is_additive(self):
        return self._additive

    def base_ring(self):
        return self._base_ring

    def power_series_ring(self):
        return self._Ps

    def _element_constructor_(self, data):
        return self.element_class(self, data)

    def _repr_(self):
        return "Meromorphic %s Functions over %s" % (
            'Additive' if self._additive else 'Multiplicative',
            self._base_ring)
    def _test__jacobi_torsion_point(phi, weight, torsion_point) :
        r"""
        Given a list of power series, which are the corrected Taylor coefficients
        of a Jacobi form, return the specialization to ``torsion_point``.
        
        INPUT:
        
        - ``phi`` -- A Fourier expansion of a Jacobi form.
        
        - ``weight`` -- An integer.
        
        - ``torsion_point`` -- A rational.
        
        OUPUT:
        
        - A power series.
        
        TESTS:
                
        See jacobi_form_by_taylor_expansion.
        
            sage: from psage.modform.jacobiforms.jacobiformd1nn_fegenerators import *
            sage: from psage.modform.jacobiforms.jacobiformd1nn_types import *
            sage: precision = 50
            sage: weight = 10; index = 7
            sage: phis = [jacobi_form_by_taylor_expansion(i, JacobiFormD1NNFilter(precision, index), weight) for i in range(JacobiFormD1NN_Gamma(weight, index)._rank(QQ))]
            sage: fs = [JacobiFormD1NNFactory_class._test__jacobi_torsion_point(phi, weight, 2/3) for phi in phis]
            sage: fs_vec = [vector(f.padded_list(precision)) for f in fs]
            sage: mf_span = span([vector(b.qexp(precision).padded_list(precision)) for b in ModularForms(GammaH(9, [4]), weight).basis()])
            sage: all(f_vec in mf_span for f_vec in fs_vec)
            True
        
        FIXME: The case of torsion points of order 5, which should lead to forms for Gamma1(25) fails even in the simplest case.
        """
        from sage.rings.all import CyclotomicField
        
        K = CyclotomicField(QQ(torsion_point).denominator()); zeta = K.gen()
        R = PowerSeriesRing(K, 'q'); q = R.gen(0)

        ch = JacobiFormD1WeightCharacter(weight)
    
        coeffs = dict( (n, QQ(0)) for n in range(phi.precision().index()) )
        for (n, r) in phi.precision().monoid_filter() :
            coeffs[n] += zeta**r * phi[(ch, (n,r))]
        
        return PowerSeriesRing(K, 'q')(coeffs) 
示例#29
0
    def _repr_(self):
        r"""
        This returns the representation of self as a string.

        EXAMPLES:

        This example illustrates ...

        ::

        """
        R = PowerSeriesRing(self._parent._R,
                            default_prec=self._depth,
                            name='z')
        z = R.gen()
        s = str(sum([R(self._val[ii, 0] * z**ii)
                     for ii in range(self._depth)]))
        return s
def eis_F(cv, dv, N, k, Q=None, prec=10, t=1):
    """
    Computes the coefficient of the Eisenstein series for $\Gamma(N)$.
    Not indented to be called by user.
    INPUT:
    - cv - int, the first coordinate of the vector determining the \Gamma(N)
      Eisenstein series
    - dv - int, the second coordinate of the vector determining the \Gamma(N)
      Eisenstein series
    - N - int, the level of the Eisenstein series to be computed
    - k - int, the weight of the Eisenstein seriess to be computed
    - Q - power series ring, the ring containing the q-expansion to be computed
    - param_level - int, the parameter of the returned series will be
      q_{param_level}
    - prec - int, the precision.  The series in q_{param_level} will be truncated
      after prec coefficients
    OUTPUT:
    - an element of the ring Q, which is the Fourier expansion of the Eisenstein
      series
    """
    if Q == None:
        Q = PowerSeriesRing(CyclotomicField(N), 'q{}'.format(N))
    R = Q.base_ring()
    zetaN = R.zeta(N)
    q = Q.gen()
    s = 0
    if k == 1:
        if cv % N == 0 and dv % N != 0:
            s = QQ(1) / QQ(2) * (1 + zetaN**dv) / (1 - zetaN**dv)
        elif cv % N != 0:
            s = QQ(1) / QQ(2) - QQ(cv) / QQ(N) + floor(QQ(cv) / QQ(N))
    elif k > 1:
        s = -ber_pol(QQ(cv) / QQ(N) - floor(QQ(cv) / QQ(N)), k) / QQ(k)
    for n1 in xrange(1, ceil(prec / QQ(t))):  # this is n/m in DS
        for n2 in xrange(1,
                         ceil(prec / QQ(t) / QQ(n1)) + 1):  # this is m in DS
            if Mod(n1, N) == Mod(cv, N):
                s += N**(1 - k) * n1**(k - 1) * zetaN**(dv * n2) * q**(t * n1 *
                                                                       n2)
            if Mod(n1, N) == Mod(-cv, N):
                s += (-1)**k * N**(1 - k) * n1**(k - 1) * zetaN**(
                    -dv * n2) * q**(t * n1 * n2)
    return s + O(q**floor(prec))
示例#31
0
def herm_modform_space_dim(D, HermWeight):
	"""
	Calculates and returns the dimension of the vector space
	of Hermitian modular forms of weight `HermWeight` over \Gamma,
	where \Gamma = \Sp_2(\curlO) and \curlO is the maximal order
	of \QQ(\sqrt{D}).
	"""

	if D == -3:
		# dern2003graded, Thm 7
		R = PowerSeriesRing(ZZ, name="t", default_prec = HermWeight + 1)
		t = R.gen()
		dims = (1 + t**45) / (1 - t**4 ) / ( 1 - t**6 ) / ( 1 - t**9 ) / ( 1 - t**10 ) / ( 1 - t**12 )
		return dims[HermWeight]
	#elif D == -4:
		# dern2003graded, Corollary 9 and Lemma 3
		# TODO...
		#R = PowerSeriesRing(ZZ, name="t", default_prec = HermWeight + 1)
		#t = R.an_element()
	else:
		raise NotImplementedError, "dimension calculation of Hermitian modular form with D = %i not implemented" % D
示例#32
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 _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) )
示例#34
0
    def _test__jacobi_predicted_taylor_coefficients(fs, q_precision):
        r"""
        Given a list of power series, which are the corrected Taylor coefficients
        of a Jacobi form, return the renormalized uncorrected ones, assuming that
        all but one `f` vanish.
        
        INPUT:
        
        - ``fs`` -- A list of power series.
        
        - ``q_precision`` -- An integer.
        
        OUPUT:
        
        - A list of power series.
        
        TESTS:
        
        See jacobi_form_by_taylor_expansion.
        """
        from sage.rings.arith import gcd

        R = PowerSeriesRing(ZZ, 'q')
        q = R.gen(0)

        diff = lambda f: f.derivative().shift(1)
        normalize = lambda f: f / gcd(f.list()) if f != 0 else f
        diffnorm = lambda f, l: normalize(
            reduce(lambda a, g: g(a), l * [diff], f))

        taylor_coefficients = list()
        allf = R(0)
        for f in fs:
            allf = f(q_precision) + diffnorm(allf, 1)
            taylor_coefficients.append(allf)

        return taylor_coefficients
 def apply_triangular_matrix(self, delta):
     """
     If self is a q-expansion and q = exp(2*pi*I*tau), then applying a
     triangular matrix [[a,b],[0,d]] to tau gives a well-defined
     q-expansion as long as the base ring contains zeta_d.
     Warning: The correct slash-action would also multiply with det(delta)^k/2 but we carefully avoid that in all applications of apply_triangular_matrix.
     """
     R = self.series.base_ring()
     a, b, d = delta[0][0], delta[0][1], delta[1][1]
     """
     Reduce the fraction a/d
     """
     a2, d2 = (a / d).numerator(), (a / d).denominator()
     b2, d3 = (b / d).numerator(), (b / d).denominator()
     zetad = R.zeta(d3 * self.param_level)
     Q = PowerSeriesRing(R, 'q' + str(self.param_level * d2))
     qNd = Q.gen()
     s = 0
     for i in range(self.series.prec()):
         s += self.series[i] * zetad**(i * b2) * qNd**(i * a2)
     s = s + O(qNd**(self.series.prec()))
     s = s * d**(-self.weight)
     return QExpansion(self.level * d2, self.weight, s,
                       self.param_level * d2)
    def prune(self, tar_param_level):
        """
        Prunes terms from the q-expansion to return a series in the desired
        parameter
        """
        try:
            assert (self.param_level % tar_param_level == 0)
        except AssertionError:
            print(
                "target parameter level should be a divsor of current parameter level"
            )
            return

        R = self.series.base_ring()
        Q = PowerSeriesRing(R, 'q' + str(tar_param_level))
        q = Q.gen()

        s = 0
        for i in range(self.series.prec() * tar_param_level //
                       self.param_level):
            s += self.series[i * (self.param_level // tar_param_level)] * q**i
        s += O(q**(self.series.prec() * tar_param_level // self.param_level))
        self.series = s
        self.param_level = tar_param_level
示例#37
0
class MPowerSeriesRing_generic(PowerSeriesRing_generic, Nonexact):
    r"""
    A multivariate power series ring.  This class is implemented as a
    single variable power series ring in the variable ``T`` over a
    multivariable polynomial ring in the specified generators.  Each
    generator ``g`` of the multivariable polynomial ring (called the
    "foreground ring") is mapped to ``g*T`` in the single variable power series
    ring (called the "background ring").  The background power series ring
    is used to do arithmetic and track total-degree precision.  The
    foreground polynomial ring is used to display elements.

    For usage and examples, see above, and :meth:`PowerSeriesRing`.
    """
    ### methods from PowerSeriesRing_generic that we *don't* override:
    #
    # variable_names_recursive : works just fine
    #
    # __contains__ : works just fine
    #
    # base_extend : works just fine
    #
    # is_exact : works just fine
    #
    # random_element : works just fine
    #
    # is_field : works just fine
    #
    # is_finite : works just fine
    #
    # __setitem__ : works just fine
    #
    #
    #### notes
    #
    # sparse setting may not be implemented completely
    Element = MPowerSeries
    def __init__(self, base_ring, num_gens, name_list,
                 order='negdeglex', default_prec=10, sparse=False):
        """
        Initializes a multivariate power series ring.  See PowerSeriesRing
        for complete documentation.

        INPUT

            - ``base_ring`` - a commutative ring

            - ``num_gens`` - number of generators

            - ``name_list`` - List of indeterminate names or a single name.
                If a single name is given, indeterminates will be this name
                followed by a number from 0 to num_gens - 1.  If a list is
                given, these will be the indeterminate names and the length
                of the list must be equal to num_gens.

            - ``order`` - ordering of variables; default is
              negative degree lexicographic

            - ``default_prec`` - The default total-degree precision for
              elements.  The default value of default_prec is 10.

            - ``sparse`` - whether or not power series are sparse

        EXAMPLES::

            sage: R.<t,u,v> = PowerSeriesRing(QQ)
            sage: g = 1 + v + 3*u*t^2 - 2*v^2*t^2
            sage: g = g.add_bigoh(5); g
            1 + v + 3*t^2*u - 2*t^2*v^2 + O(t, u, v)^5
            sage: g in R
            True

        TESTS:

        By :trac:`14084`, the multi-variate power series ring belongs to the
        category of integral domains, if the base ring does::

            sage: P = ZZ[['x','y']]
            sage: P.category()
            Category of integral domains
            sage: TestSuite(P).run()

        Otherwise, it belongs to the category of commutative rings::

            sage: P = Integers(15)[['x','y']]
            sage: P.category()
            Category of commutative rings
            sage: TestSuite(P).run()

        """
        order = TermOrder(order,num_gens)
        self._term_order = order
        if not base_ring.is_commutative():
            raise TypeError("Base ring must be a commutative ring.")
        n = int(num_gens)
        if n < 0:
            raise ValueError("Multivariate Polynomial Rings must have more than 0 variables.")
        self._ngens = n
        self._has_singular = False #cannot convert to Singular by default
        # Multivariate power series rings inherit from power series rings. But
        # apparently we can not call their initialisation. Instead, initialise
        # CommutativeRing and Nonexact:
        CommutativeRing.__init__(self, base_ring, name_list, category =
                                 _IntegralDomains if base_ring in
                                 _IntegralDomains else _CommutativeRings)
        Nonexact.__init__(self, default_prec)

        # underlying polynomial ring in which to represent elements
        self._poly_ring_ = PolynomialRing(base_ring, self.variable_names(), sparse=sparse, order=order)
        # because sometimes PowerSeriesRing_generic calls self.__poly_ring
        self._PowerSeriesRing_generic__poly_ring = self._poly_ring()

        # background univariate power series ring
        self._bg_power_series_ring = PowerSeriesRing(self._poly_ring_, 'Tbg', sparse=sparse, default_prec=default_prec)
        self._bg_indeterminate = self._bg_power_series_ring.gen()

        self._is_sparse = sparse
        self._params = (base_ring, num_gens, name_list,
                         order, default_prec, sparse)
        self._populate_coercion_lists_()

    def _repr_(self):
        """
        Prints out a multivariate power series ring.

        EXAMPLES::

            sage: R.<x,y> = PowerSeriesRing(GF(17))
            sage: R  #indirect doctest
            Multivariate Power Series Ring in x, y over Finite Field of size 17
            sage: R.rename('my multivariate power series ring')
            sage: R
            my multivariate power series ring
        """
        if self.ngens() == 0:
            generators_rep = "no variables"
        else:
            generators_rep = ", ".join(self.variable_names())

        s = "Multivariate Power Series Ring in %s over %s"%(generators_rep, self.base_ring())
        if self.is_sparse():
            s = 'Sparse ' + s
        return s

    def _latex_(self):
        """
        Returns latex representation of power series ring

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'v'); M
            Multivariate Power Series Ring in v0, v1, v2, v3 over Rational Field
            sage: M._latex_()
            '\\Bold{Q}[[v_{0}, v_{1}, v_{2}, v_{3}]]'
        """
        generators_latex = ", ".join(self.latex_variable_names())
        return "%s[[%s]]"%(latex.latex(self.base_ring()), generators_latex)

    def is_integral_domain(self, proof=False):
        """
        Return True if the base ring is an integral domain; otherwise
        return False.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'v'); M
            Multivariate Power Series Ring in v0, v1, v2, v3 over Rational Field
            sage: M.is_integral_domain()
            True
        """
        return self.base_ring().is_integral_domain()

    def is_noetherian(self, proof=False):
        """
        Power series over a Noetherian ring are Noetherian.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'v'); M
            Multivariate Power Series Ring in v0, v1, v2, v3 over Rational Field
            sage: M.is_noetherian()
            True

            sage: W = PowerSeriesRing(InfinitePolynomialRing(ZZ,'a'),2,'x,y')
            sage: W.is_noetherian()
            False
        """
        return self.base_ring().is_noetherian()

    def term_order(self):
        """
        Print term ordering of self.  Term orderings are implemented by the
        TermOrder class.

        EXAMPLES::

            sage: M.<x,y,z> = PowerSeriesRing(ZZ,3);
            sage: M.term_order()
            Negative degree lexicographic term order
            sage: m = y*z^12 - y^6*z^8 - x^7*y^5*z^2 + x*y^2*z + M.O(15); m
            x*y^2*z + y*z^12 - x^7*y^5*z^2 - y^6*z^8 + O(x, y, z)^15

            sage: N = PowerSeriesRing(ZZ,3,'x,y,z', order="deglex");
            sage: N.term_order()
            Degree lexicographic term order
            sage: N(m)
            -x^7*y^5*z^2 - y^6*z^8 + y*z^12 + x*y^2*z + O(x, y, z)^15
        """
        return self._term_order

    def characteristic(self):
        """
        Return characteristic of base ring, which is characteristic of self.

        EXAMPLES::

            sage: H = PowerSeriesRing(GF(65537),4,'f'); H
            Multivariate Power Series Ring in f0, f1, f2, f3 over
            Finite Field of size 65537
            sage: H.characteristic()
            65537
        """
        return self.base_ring().characteristic()

    def construction(self):
        """
        Returns a functor F and base ring R such that F(R) == self.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'f'); M
            Multivariate Power Series Ring in f0, f1, f2, f3 over Rational Field

            sage: (c,R) = M.construction(); (c,R)
            (Completion[('f0', 'f1', 'f2', 'f3')],
            Multivariate Polynomial Ring in f0, f1, f2, f3 over Rational Field)
            sage: c
            Completion[('f0', 'f1', 'f2', 'f3')]
            sage: c(R)
            Multivariate Power Series Ring in f0, f1, f2, f3 over Rational Field
            sage: c(R) == M
            True
        """
        from sage.categories.pushout import CompletionFunctor
        return (CompletionFunctor(self._names, self.default_prec()),
                self._poly_ring())

    def change_ring(self, R):
        """
        Returns the power series ring over R in the same variable as self.
        This function ignores the question of whether the base ring of self
        is or can extend to the base ring of R; for the latter, use
        base_extend.

        EXAMPLES::

            sage: R.<t,u,v> = PowerSeriesRing(QQ); R
            Multivariate Power Series Ring in t, u, v over Rational Field
            sage: R.base_extend(RR)
            Multivariate Power Series Ring in t, u, v over Real Field with
            53 bits of precision
            sage: R.change_ring(IntegerModRing(10))
            Multivariate Power Series Ring in t, u, v over Ring of integers
            modulo 10
            sage: R.base_extend(IntegerModRing(10))
            Traceback (most recent call last):
            ...
            TypeError: no base extension defined

            sage: S = PowerSeriesRing(GF(65537),2,'x,y'); S
            Multivariate Power Series Ring in x, y over Finite Field of size
            65537
            sage: S.change_ring(GF(5))
            Multivariate Power Series Ring in x, y over Finite Field of size 5
        """
        return PowerSeriesRing(R, names = self.variable_names(), default_prec = self.default_prec())

    def remove_var(self, *var):
        """
        Remove given variable or sequence of variables from self.

        EXAMPLES::

            sage: A.<s,t,u> = PowerSeriesRing(ZZ)
            sage: A.remove_var(t)
            Multivariate Power Series Ring in s, u over Integer Ring
            sage: A.remove_var(s,t)
            Power Series Ring in u over Integer Ring

            sage: M = PowerSeriesRing(GF(5),5,'t'); M
            Multivariate Power Series Ring in t0, t1, t2, t3, t4 over
            Finite Field of size 5
            sage: M.remove_var(M.gens()[3])
            Multivariate Power Series Ring in t0, t1, t2, t4 over Finite
            Field of size 5

        Removing all variables results in the base ring::

            sage: M.remove_var(*M.gens())
            Finite Field of size 5

        """
        vars = list(self.variable_names())
        for v in var:
            vars.remove(str(v))
        if len(vars) == 0:
            return self.base_ring()
        return PowerSeriesRing(self.base_ring(), names=vars)

    ## this is defined in PowerSeriesRing_generic
    # def __call__(self, f, prec=infinity):
    #     """
    #     Coerce object to this multivariate power series ring.
    #     """
    #     return

    def _coerce_impl(self, f):
        """
        Return the canonical coercion of ``f`` into this multivariate power
        series ring, if one is defined, or raise a TypeError.

        The rings that canonically coerce to this multivariate power series
        ring are:

            - this ring itself

            - a polynomial or power series ring in the same variables or a
              subset of these variables (possibly empty), over any base
              ring that canonically coerces into the base ring of this ring

        EXAMPLES::

            sage: R.<t,u,v> = PowerSeriesRing(QQ); R
            Multivariate Power Series Ring in t, u, v over Rational Field
            sage: S1.<t,v> = PolynomialRing(ZZ); S1
            Multivariate Polynomial Ring in t, v over Integer Ring
            sage: f1 = -t*v + 2*v^2 + v; f1
            -t*v + 2*v^2 + v
            sage: R(f1)
            v - t*v + 2*v^2
            sage: S2.<u,v> = PowerSeriesRing(ZZ); S2
            Multivariate Power Series Ring in u, v over Integer Ring
            sage: f2 = -2*v^2 + 5*u*v^2 + S2.O(6); f2
            -2*v^2 + 5*u*v^2 + O(u, v)^6
            sage: R(f2)
            -2*v^2 + 5*u*v^2 + O(t, u, v)^6

            sage: R2 = R.change_ring(GF(2))
            sage: R2(f1)
            v + t*v
            sage: R2(f2)
            u*v^2 + O(t, u, v)^6

        TESTS::

            sage: R.<t,u,v> = PowerSeriesRing(QQ)
            sage: S1.<t,v> = PolynomialRing(ZZ)
            sage: f1 = S1.random_element()
            sage: g1 = R._coerce_impl(f1)
            sage: f1.parent() == R
            False
            sage: g1.parent() == R
            True

        """

        P = f.parent()
        if is_MPolynomialRing(P) or is_MPowerSeriesRing(P) \
               or is_PolynomialRing(P) or is_PowerSeriesRing(P):
            if set(P.variable_names()).issubset(set(self.variable_names())):
                if self.has_coerce_map_from(P.base_ring()):
                    return self(f)
        else:
            return self._coerce_try(f,[self.base_ring()])

    def _is_valid_homomorphism_(self, codomain, im_gens):
        """
        Replacement for method of PowerSeriesRing_generic.

        To be valid, a homomorphism must send generators to elements of
        positive valuation or to nilpotent elements.

        Note that the method is_nilpotent doesn't (as of sage 4.4) seem to
        be defined for obvious examples (matrices, quotients of polynomial
        rings).

        EXAMPLES::

            sage: R.<a,b,c> = PowerSeriesRing(Zmod(8)); R
            Multivariate Power Series Ring in a, b, c over Ring of integers
            modulo 8
            sage: M = PowerSeriesRing(ZZ,3,'x,y,z');
            sage: M._is_valid_homomorphism_(R,[a,c,b])
            True

            sage: M._is_valid_homomorphism_(R,[0,c,b])
            True

        2 is nilpotent in `ZZ/8`, but 3 is not::

            sage: M._is_valid_homomorphism_(R,[2,c,b])
            True
            sage: M._is_valid_homomorphism_(R,[3,c,b])
            False

        Over `ZZ`, 2 is not nilpotent::

            sage: S = R.change_ring(ZZ); S
            Multivariate Power Series Ring in a, b, c over Integer Ring
            sage: M._is_valid_homomorphism_(S,[a,c,b])
            True
            sage: M._is_valid_homomorphism_(S,[0,c,b])
            True
            sage: M._is_valid_homomorphism_(S,[2,c,b])
            False

            sage: g = [S.random_element(10)*v for v in S.gens()]
            sage: M._is_valid_homomorphism_(S,g)
            True
        """
        try:
            im_gens = [codomain(v) for v in im_gens]
        except TypeError:
            raise TypeError("The given generator images do not coerce to codomain.")

        if len(im_gens) is not self.ngens():
            raise ValueError("You must specify the image of each generator.")
        if all(v == 0 for v in im_gens):
            return True
        if is_MPowerSeriesRing(codomain) or is_PowerSeriesRing(codomain):
            try:
                B = all(v.valuation() > 0 or v.is_nilpotent() for v in im_gens)
            except NotImplementedError:
                B = all(v.valuation() > 0 for v in im_gens)
            return B
        if is_CommutativeRing(codomain):
            return all(v.is_nilpotent() for v in im_gens)

    def _coerce_map_from_(self, P):
        """
        The rings that canonically coerce to this multivariate power series
        ring are:

            - this ring itself

            - a polynomial or power series ring in the same variables or a
              subset of these variables (possibly empty), over any base
              ring that canonically coerces into this ring

            - any ring that coerces into the foreground polynomial ring of this ring

        EXAMPLES::

            sage: A = GF(17)[['x','y']]
            sage: A.has_coerce_map_from(ZZ)
            True
            sage: A.has_coerce_map_from(ZZ['x'])
            True
            sage: A.has_coerce_map_from(ZZ['y','x'])
            True
            sage: A.has_coerce_map_from(ZZ[['x']])
            True
            sage: A.has_coerce_map_from(ZZ[['y','x']])
            True
            sage: A.has_coerce_map_from(ZZ['x','z'])
            False
            sage: A.has_coerce_map_from(GF(3)['x','y'])
            False
            sage: A.has_coerce_map_from(Frac(ZZ['y','x']))
            False

        TESTS::

            sage: M = PowerSeriesRing(ZZ,3,'x,y,z');
            sage: M._coerce_map_from_(M)
            True
            sage: M._coerce_map_from_(M.remove_var(x))
            True
            sage: M._coerce_map_from_(PowerSeriesRing(ZZ,x))
            True
            sage: M._coerce_map_from_(PolynomialRing(ZZ,'x,z'))
            True
            sage: M._coerce_map_from_(PolynomialRing(ZZ,0,''))
            True
            sage: M._coerce_map_from_(ZZ)
            True

            sage: M._coerce_map_from_(Zmod(13))
            False
            sage: M._coerce_map_from_(PolynomialRing(ZZ,2,'x,t'))
            False
            sage: M._coerce_map_from_(PolynomialRing(Zmod(11),2,'x,y'))
            False

            sage: P = PolynomialRing(ZZ,3,'z')
            sage: H = PowerSeriesRing(P,4,'f'); H
            Multivariate Power Series Ring in f0, f1, f2, f3 over Multivariate Polynomial Ring in z0, z1, z2 over Integer Ring
            sage: H._coerce_map_from_(P)
            True
            sage: H._coerce_map_from_(P.remove_var(P.gen(1)))
            True
            sage: H._coerce_map_from_(PolynomialRing(ZZ,'z2,f0'))
            True

        """
        if is_MPolynomialRing(P) or is_MPowerSeriesRing(P) \
                   or is_PolynomialRing(P) or is_PowerSeriesRing(P):
            if set(P.variable_names()).issubset(set(self.variable_names())):
                if self.has_coerce_map_from(P.base_ring()):
                    return True

        return self._poly_ring().has_coerce_map_from(P)

    def _element_constructor_(self,f,prec=None):
        """
        TESTS::

            sage: M = PowerSeriesRing(ZZ,5,'t');
            sage: t = M.gens();
            sage: m = -2*t[0]*t[3]^6*t[4] - 12*t[0]^2*t[3]*t[4]^6 + t[1]*t[2]*t[3]^4*t[4]^3 + M.O(10)
            sage: M._element_constructor_(m)
            -2*t0*t3^6*t4 - 12*t0^2*t3*t4^6 + t1*t2*t3^4*t4^3 +
            O(t0, t1, t2, t3, t4)^10
            sage: R = PolynomialRing(ZZ,5,'t')
            sage: t = R.gens()
            sage: p = -4*t[0]*t[4] + t[1]^2 + t[1]*t[2] - 6*t[2]*t[4] - t[3]*t[4]
            sage: M._element_constructor_(p)
            -4*t0*t4 + t1^2 + t1*t2 - 6*t2*t4 - t3*t4
            sage: p.parent()
            Multivariate Polynomial Ring in t0, t1, t2, t3, t4 over Integer Ring
            sage: M._element_constructor_(p).parent()
            Multivariate Power Series Ring in t0, t1, t2, t3, t4 over
            Integer Ring
        """
        if prec is None:
            try:
                prec = f.prec()
            except AttributeError:
                prec = infinity
        return self.element_class(parent=self, x=f, prec=prec)

    def __cmp__(self, other):
        """
        Compare this multivariate power series ring to something else.

        Power series rings are considered equal if the base ring, variable
        names, and default truncation precision are the same.  Note that we
        don't compare term-ordering.

        First the base rings are compared, then the variable names, then
        the default precision.

        EXAMPLES::

            sage: R.<t,u> = PowerSeriesRing(ZZ)
            sage: S.<t,u> = PowerSeriesRing(ZZ)
            sage: R is S
            True
            sage: R == S
            True
            sage: S.<t,u> = PowerSeriesRing(ZZ, default_prec=30)
            sage: R == S
            False
            sage: PowerSeriesRing(QQ,3,'t') == PowerSeriesRing(ZZ,3,'t')
            False
            sage: PowerSeriesRing(QQ,5,'t') == 5
            False
        """
        if not isinstance(other, MPowerSeriesRing_generic):
            return -1
        c = cmp(self.base_ring(), other.base_ring())
        if c: return c
        c = cmp(self.variable_names(), other.variable_names())
        if c: return c
        c = cmp(self.default_prec(), other.default_prec())
        if c: return c
        return 0

    def laurent_series_ring(self):
        """
        Laruent series not yet implemented for multivariate power series rings

        TESTS::

            sage: M = PowerSeriesRing(ZZ,3,'x,y,z');
            sage: M.laurent_series_ring()
            Traceback (most recent call last):
            ...
            NotImplementedError: Laurent series not implemented for
            multivariate power series.
        """
        raise NotImplementedError("Laurent series not implemented for multivariate power series.")

    def _poly_ring(self, x=None):
        """
        Return the underlying polynomial ring used to represent elements of
        this power series ring.  If given an input x, returns x coerced
        into this polynomial ring.

        EXAMPLES::

            sage: R.<t,u> = PowerSeriesRing(QQ)
            sage: R._poly_ring()
            Multivariate Polynomial Ring in t, u over Rational Field
            sage: R._poly_ring(2).parent()
            Multivariate Polynomial Ring in t, u over Rational Field
        """
        if x is None:
            return self._poly_ring_
        else:
            return self._poly_ring_(x)

    def _mpoly_ring(self, x=None):
        """
        Same as _poly_ring

        TESTS::

            sage: R.<t,u> = PowerSeriesRing(QQ)
            sage: R._mpoly_ring()
            Multivariate Polynomial Ring in t, u over Rational Field
            sage: R._mpoly_ring(2).parent()
            Multivariate Polynomial Ring in t, u over Rational Field
        """
        return self._poly_ring(x)

    def _bg_ps_ring(self, x=None):
        """
        Return the background univariate power series ring.  If given an
        input x, returns x coerced into this power series ring.

        EXAMPLES::

            sage: R.<t,u> = PowerSeriesRing(QQ)
            sage: R._bg_ps_ring()
            Power Series Ring in Tbg over Multivariate Polynomial Ring in
            t, u over Rational Field
            sage: R._bg_ps_ring(4).parent() == R
            False

        """
        if x is None:
            return self._bg_power_series_ring
        else:
            return self._bg_power_series_ring(x)

    def is_sparse(self):
        """
        Is self sparse?

        EXAMPLES::

            sage: M = PowerSeriesRing(ZZ,3,'s,t,u'); M
            Multivariate Power Series Ring in s, t, u over Integer Ring
            sage: M.is_sparse()
            False
            sage: N = PowerSeriesRing(ZZ,3,'s,t,u',sparse=True); N
            Sparse Multivariate Power Series Ring in s, t, u over Integer Ring
            sage: N.is_sparse()
            True
        """
        return self._is_sparse

    def is_dense(self):
        """
        Is self dense? (opposite of sparse)

        EXAMPLES::

            sage: M = PowerSeriesRing(ZZ,3,'s,t,u'); M
            Multivariate Power Series Ring in s, t, u over Integer Ring
            sage: M.is_dense()
            True
            sage: N = PowerSeriesRing(ZZ,3,'s,t,u',sparse=True); N
            Sparse Multivariate Power Series Ring in s, t, u over Integer Ring
            sage: N.is_dense()
            False
        """
        return not self.is_sparse()

    def gen(self, n=0):
        """
        Return the nth generator of self.

        EXAMPLES::

            sage: M = PowerSeriesRing(ZZ,10,'v');
            sage: M.gen(6)
            v6
        """
        if n < 0 or n >= self._ngens:
            raise ValueError("Generator not defined.")
        #return self(self._poly_ring().gens()[int(n)])
        return self.element_class(parent=self,x=self._poly_ring().gens()[int(n)], is_gen=True)

    def ngens(self):
        """
        Return number of generators of self.

        EXAMPLES::

            sage: M = PowerSeriesRing(ZZ,10,'v');
            sage: M.ngens()
            10
        """
        return self._ngens

    def prec_ideal(self):
        """
        Return the ideal which determines precision; this is the ideal
        generated by all of the generators of our background polynomial
        ring.

        EXAMPLES::

            sage: A.<s,t,u> = PowerSeriesRing(ZZ)
            sage: A.prec_ideal()
            Ideal (s, t, u) of Multivariate Polynomial Ring in s, t, u over
            Integer Ring
        """
        return self._poly_ring().ideal(self._poly_ring().gens())

    def bigoh(self,prec):
        """
        Return big oh with precision ``prec``.  The function ``O`` does the same thing.

        EXAMPLES::

            sage: T.<a,b> = PowerSeriesRing(ZZ,2); T
            Multivariate Power Series Ring in a, b over Integer Ring
            sage: T.bigoh(10)
            0 + O(a, b)^10
            sage: T.O(10)
            0 + O(a, b)^10
        """
        return self(0).O(prec)

    def O(self,prec):
        """
        Return big oh with precision ``prec``.  This function is an alias for ``bigoh``.

        EXAMPLES::

            sage: T.<a,b> = PowerSeriesRing(ZZ,2); T
            Multivariate Power Series Ring in a, b over Integer Ring
            sage: T.O(10)
            0 + O(a, b)^10
            sage: T.bigoh(10)
            0 + O(a, b)^10
        """
        return self.bigoh(prec)

    def _send_to_bg(self,f):
        """
        Send an element of the foreground polynomial ring to the background
        power series ring.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'f')
            sage: f = M._poly_ring().gens()
            sage: bg = M._send_to_bg((f[0] + f[2] + 2)**2); bg
            4 + (4*f0 + 4*f2)*Tbg + (f0^2 + 2*f0*f2 + f2^2)*Tbg^2

            sage: M._send_to_bg(bg)
            Traceback (most recent call last):
            ...
            TypeError: Cannot coerce input to polynomial ring.
        """
        try:
            f = self._poly_ring(f)
        except TypeError:
            raise TypeError("Cannot coerce input to polynomial ring.")
        fg_to_bg_dict = dict((v,v*self._bg_ps_ring().gen())
                             for v in self._poly_ring().gens())
        return self._bg_ps_ring(f.subs(fg_to_bg_dict))

    def _send_to_fg(self,f):
        """
        Send an element of the background univariate power series ring to
        the foreground multivariate polynomial ring.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'f')
            sage: f = M._poly_ring().gens()
            sage: bg = M._send_to_bg((f[0] + f[2] + 2)**2); bg
            4 + (4*f0 + 4*f2)*Tbg + (f0^2 + 2*f0*f2 + f2^2)*Tbg^2
            sage: bg.parent()
            Power Series Ring in Tbg over Multivariate Polynomial Ring in f0, f1,
            f2, f3 over Rational Field
            sage: fg = M._send_to_fg(bg); fg
            4 + 4*f0 + 4*f2 + f0^2 + 2*f0*f2 + f2^2
            sage: fg.parent()
            Multivariate Polynomial Ring in f0, f1, f2, f3 over Rational Field
            sage: fg = M._send_to_fg(bg.add_bigoh(3)); fg
            4 + 4*f0 + 4*f2 + f0^2 + 2*f0*f2 + f2^2
            sage: fg = M._send_to_fg(bg.add_bigoh(2)); fg
            4 + 4*f0 + 4*f2
        """
        return self._poly_ring(f.polynomial().subs({self._bg_indeterminate:1}))
示例#38
0
def _dimension_Gamma_2(wt_range, j, group='Gamma(2)'):
    """
    Return the dict
    {(k-> partition ->  [ d(k), e(k), c(k)] for k in wt_range]},
    where d(k), e(k), c(k) are the dimensions
    of the $p$-canonical part of $M_{k,j}( \Gamma(2))$ and its subspaces of
    Non-cusp forms and Cusp forms.
    """

    partitions = [
        u'6', u'51', u'42', u'411', u'33', u'321', u'311', u'222', u'2211',
        u'21111', u'111111'
    ]

    if is_odd(j):
        dct = dict(
            (k, dict((h, [0, 0, 0]) for h in partitions)) for k in wt_range)
        for k in dct:
            dct[k]['All'] = [0, 0, 0]
        partitions.insert(0, 'All')
        return partitions, dct

    if 'Sp4(Z)' == group and 2 == j and wt_range[0] < 4:
        wt_range1 = [k for k in wt_range if k < 4]
        wt_range2 = [k for k in wt_range if k >= 4]
        print wt_range1, wt_range2
        if wt_range2 != []:
            headers, dct = _dimension_Gamma_2(wt_range2, j, group)
        else:
            headers, dct = ['Total', 'Non cusp', 'Cusp'], {}
        for k in wt_range1:
            dct[k] = dict([(h, 0) for h in headers])
        return headers, dct

    if j >= 2 and wt_range[0] < 4:
        raise NotImplementedError(
            'Dimensions of \(M_{k,j}\) for \(k<4\) and even \(j\ge 2\) not implemented'
        )

    query = {'sym_power': str(j), 'group': 'Gamma(2)', 'space': 'total'}
    db_total = fetch(query)
    assert db_total, '%s: Data not available' % query
    query['space'] = 'cusp'
    db_cusp = fetch(query)
    assert db_cusp, '%s: Data not available' % query

    P = PowerSeriesRing(IntegerRing(),
                        default_prec=wt_range[-1] + 1,
                        names=('t', ))
    t = P.gen()
    total = dict()
    cusp = dict()
    for p in partitions:
        total[p] = eval(db_total[p])
        cusp[p] = eval(db_cusp[p])
    # total = dict( ( p, eval(db_total[p])) for p in partitions)
    # cusp = dict( ( p, eval(db_cusp[p])) for p in partitions)

    if 'Gamma(2)' == group:
        dct = dict(
            (k,
             dict((p, [total[p][k], total[p][k] - cusp[p][k], cusp[p][k]])
                  for p in partitions)) for k in wt_range)
        for k in dct:
            dct[k]['All'] = [
                sum(dct[k][p][j] for p in dct[k]) for j in range(3)
            ]

        partitions.insert(0, 'All')
        headers = partitions

    elif 'Gamma1(2)' == group:
        ps = {
            '3': ['6', '42', '222'],
            '21': ['51', '42', '321'],
            '111': ['411', '33']
        }

        dct = dict((k,
                    dict((p, [
                        sum(total[q][k] for q in ps[p]),
                        sum(total[q][k] - cusp[q][k] for q in ps[p]),
                        sum(cusp[q][k] for q in ps[p]),
                    ]) for p in ps)) for k in wt_range)
        for k in dct:
            dct[k]['All'] = [
                sum(dct[k][p][j] for p in dct[k]) for j in range(3)
            ]

        headers = ps.keys()
        headers.sort(reverse=True)
        headers.insert(0, 'All')

    elif 'Gamma0(2)' == group:
        headers = ['Total', 'Non cusp', 'Cusp']
        ps = ['6', '42', '222']
        dct = dict((k, {
            'Total': sum(total[p][k] for p in ps),
            'Non cusp': sum(total[p][k] - cusp[p][k] for p in ps),
            'Cusp': sum(cusp[p][k] for p in ps)
        }) for k in wt_range)

    elif 'Sp4(Z)' == group:
        headers = ['Total', 'Non cusp', 'Cusp']
        p = '6'
        dct = dict((k, {
            'Total': total[p][k],
            'Non cusp': total[p][k] - cusp[p][k],
            'Cusp': cusp[p][k]
        }) for k in wt_range)
    else:
        raise NotImplemetedError('Dimension for %s not implemented' % group)

    return headers, dct
示例#39
0
def e_phipsi(phi,
             psi,
             k,
             t=1,
             prec=5,
             mat=Matrix([[1, 0], [0, 1]]),
             base_ring=None):
    r"""
    Computes the Eisenstein series attached to the characters psi, phi as
    defined on p129 of Diamond--Shurman hit by mat \in \SL_2(\Z)
    INPUT:
     - psi, a primitive Dirichlet character
     - phi, a primitive Dirichlet character
     - k, int -- the weight
     - t, int -- the shift
     - prec, the desired absolute precision, can be fractional. The expansion will be up to O(q_w^(floor(w*prec))), where w is the width of the cusp.
     - mat, a matrix - typically taking $i\infty$ to some other cusp
     - base_ring, a ring - the ring in which the modular forms are defined
    OUTPUT:
     - an instance of QExpansion
    """
    chi2 = phi
    chi1 = psi
    try:
        assert (QQ(chi1(-1)) * QQ(chi2(-1)) == (-1)**k)
    except AssertionError:
        print("Parity of characters must match parity of weight")
        return None
    N1 = chi1.level()
    N2 = chi2.level()
    N = t * N1 * N2
    mat2, Tn = find_correct_matrix(mat, N)
    #By construction gamma = mat2 * Tn * mat**(-1) is in Gamma0(N) so if E is our Eisenstein series we can evaluate E|mat = chi(gamma) * E|mat2*Tn.
    #Since E|mat2 has a Fourier expansion in qN, the matrix Tn acts as a a twist. The value c_gamma = chi(gamma) is calculated below.
    #The point of swapping mat with mat2 is that mat2 satisfies C|N, C>0, (A,N)=1 and N|B and our formulas for the coefficients require this condition.
    A, B, C, D = mat2.list()
    gamma = mat2 * Tn * mat**(-1)
    if base_ring == None:
        Nbig = lcm(N, euler_phi(N1 * N2))
        base_ring = CyclotomicField(Nbig, 'zeta' + str(Nbig))
        zetaNbig = base_ring.gen()
        zetaN = zetaNbig**(Nbig / N)
    else:
        zetaN = base_ring.zeta(N)
    g = gcd(t, C)
    g1 = gcd(N1 * g, C)
    g2 = gcd(N2 * g, C)
    #Resulting Eisenstein series will have Fourier expansion in q_N**(g1g2)=q_(N/gcd(N,g1g2))**(g1g2/gcd(N,g1g2))
    Q = PowerSeriesRing(base_ring, 'q' + str(N / gcd(N, g1 * g2)))
    qN = Q.gen()
    zeta_Cg = zetaN**(N / (C / g))
    zeta_tmp = zeta_Cg**(inverse_mod(-A * ZZ(t / g), C / g))
    #Calculating a few values that will be used repeatedly
    chi1bar_vals = [base_ring(chi1.bar()(i)) for i in range(N1)]
    cp_list1 = [i for i in range(N1) if gcd(i, N1) == 1]
    chi2bar_vals = [base_ring(chi2.bar()(i)) for i in range(N2)]
    cp_list2 = [i for i in range(N2) if gcd(i, N2) == 1]

    #Computation of the Fourier coefficients
    ser = O(qN**floor(prec * N / gcd(N, g1 * g2)))
    for n in range(1, ceil(prec * N / QQ(g1 * g2)) + 1):
        f = 0
        for m in divisors(n) + list(map(lambda x: -x, divisors(n))):
            a = 0
            for r1 in cp_list1:
                b = 0
                if ((C / g1) * r1 - QQ(n) / QQ(m)) % ((N1 * g) / g1) == 0:
                    for r2 in cp_list2:
                        if ((C / g2) * r2 - m) % ((N2 * g) / g2) == 0:
                            b += chi2bar_vals[r2] * zeta_tmp**(
                                (n / m - (C / g1) * r1) / ((N1 * g) / g1) *
                                (m - (C / g2) * r2) / ((N2 * g) / g2))
                    a += chi1bar_vals[r1] * b
            a *= sign(m) * m**(k - 1)
            f += a
        f *= zetaN**(inverse_mod(A, N) * (g1 * g2) / C * n)
        #The additional factor zetaN**(n*Tn[0][1]) comes from the twist by Tn
        ser += zetaN**(n * g1 * g2 * Tn[0][1]) * f * qN**(n * (
            (g1 * g2) / gcd(N, g1 * g2)))

    #zk(chi1, chi2, c)
    gauss1 = base_ring(gauss_sum_corr(chi1.bar()))
    gauss2 = base_ring(gauss_sum_corr(chi2.bar()))
    zk = 2 * (N2 * t / QQ(g2))**(k - 1) * (t / g) * gauss1 * gauss2
    #The following is a temporary fix for a bug in sage
    if base_ring == CC:
        G = DirichletGroup(N1 * N2, CC)
        G[0]  #Otherwise chi1.bar().extend(N1*N2).base_extend(CC) or chi2.bar().extend(N1*N2).base_extend(CC) will produce an error
    #Constant term
    #c_gamma comes from replacing mat with mat2.
    c_gamma = chi1bar_vals[gamma[1][1] % N1] * chi2bar_vals[gamma[1][1] % N2]
    if N1.divides(C) and ZZ(C / N1).divides(t) and gcd(t / (C / N1), N1) == 1:
        ser += (-1)**(k - 1) * gauss2 / QQ(
            N2 * (g2 / g)**(k - 1)) * chi1bar_vals[(-A * t / g) % N1] * Sk(
                chi1.bar().extend(N1 * N2).base_extend(base_ring) *
                chi2.extend(N1 * N2).base_extend(base_ring), k)
    elif k == 1 and N2.divides(C) and ZZ(C / N2).divides(t) and gcd(
            t / (C / N2), N2) == 1:
        ser += gauss1 / QQ(N1) * chi2bar_vals[(-A * t / g) % N2] * Sk(
            chi1.extend(N1 * N2).base_extend(base_ring) *
            chi2.bar().extend(N1 * N2).base_extend(base_ring), k)
    return QExpansion(
        N, k,
        2 / zk * c_gamma * ser + O(qN**floor(prec * N / gcd(N, g1 * g2))),
        N / gcd(N, g1 * g2))
示例#40
0
def _dimension_Gamma_2(wt_range, j, group="Gamma(2)"):
    """
    Return the dict
    {(k-> partition ->  [ d(k), e(k), c(k)] for k in wt_range]},
    where d(k), e(k), c(k) are the dimensions
    of the $p$-canonical part of $M_{k,j}( \Gamma(2))$ and its subspaces of
    Non-cusp forms and Cusp forms.
    """

    partitions = [u"6", u"51", u"42", u"411", u"33", u"321", u"311", u"222", u"2211", u"21111", u"111111"]

    if is_odd(j):
        dct = dict((k, dict((h, [0, 0, 0]) for h in partitions)) for k in wt_range)
        for k in dct:
            dct[k]["All"] = [0, 0, 0]
        partitions.insert(0, "All")
        return partitions, dct

    if "Sp4(Z)" == group and 2 == j and wt_range[0] < 4:
        wt_range1 = [k for k in wt_range if k < 4]
        wt_range2 = [k for k in wt_range if k >= 4]
        #        print wt_range1, wt_range2
        if wt_range2 != []:
            headers, dct = _dimension_Gamma_2(wt_range2, j, group)
        else:
            headers, dct = ["Total", "Non cusp", "Cusp"], {}
        for k in wt_range1:
            dct[k] = dict([(h, 0) for h in headers])
        return headers, dct

    if j >= 2 and wt_range[0] < 4:
        raise NotImplementedError("Dimensions of \(M_{k,j}\) for \(k<4\) and even \(j\ge 2\) not implemented")

    query = {"sym_power": str(j), "group": "Gamma(2)", "space": "total"}
    db_total = fetch(query)
    assert db_total, "%s: Data not available" % query
    query["space"] = "cusp"
    db_cusp = fetch(query)
    assert db_cusp, "%s: Data not available" % query

    P = PowerSeriesRing(IntegerRing(), default_prec=wt_range[-1] + 1, names=("t",))
    t = P.gen()
    total = dict()
    cusp = dict()
    for p in partitions:
        total[p] = eval(db_total[p])
        cusp[p] = eval(db_cusp[p])
    # total = dict( ( p, eval(db_total[p])) for p in partitions)
    # cusp = dict( ( p, eval(db_cusp[p])) for p in partitions)

    if "Gamma(2)" == group:
        dct = dict(
            (k, dict((p, [total[p][k], total[p][k] - cusp[p][k], cusp[p][k]]) for p in partitions)) for k in wt_range
        )
        for k in dct:
            dct[k]["All"] = [sum(dct[k][p][j] for p in dct[k]) for j in range(3)]

        partitions.insert(0, "All")
        headers = partitions

    elif "Gamma1(2)" == group:
        ps = {"3": ["6", "42", "222"], "21": ["51", "42", "321"], "111": ["411", "33"]}

        dct = dict(
            (
                k,
                dict(
                    (
                        p,
                        [
                            sum(total[q][k] for q in ps[p]),
                            sum(total[q][k] - cusp[q][k] for q in ps[p]),
                            sum(cusp[q][k] for q in ps[p]),
                        ],
                    )
                    for p in ps
                ),
            )
            for k in wt_range
        )
        for k in dct:
            dct[k]["All"] = [sum(dct[k][p][j] for p in dct[k]) for j in range(3)]

        headers = ps.keys()
        headers.sort(reverse=True)
        headers.insert(0, "All")

    elif "Gamma0(2)" == group:
        headers = ["Total", "Non cusp", "Cusp"]
        ps = ["6", "42", "222"]
        dct = dict(
            (
                k,
                {
                    "Total": sum(total[p][k] for p in ps),
                    "Non cusp": sum(total[p][k] - cusp[p][k] for p in ps),
                    "Cusp": sum(cusp[p][k] for p in ps),
                },
            )
            for k in wt_range
        )

    elif "Sp4(Z)" == group:
        headers = ["Total", "Non cusp", "Cusp"]
        p = "6"
        dct = dict(
            (k, {"Total": total[p][k], "Non cusp": total[p][k] - cusp[p][k], "Cusp": cusp[p][k]}) for k in wt_range
        )
    else:
        raise NotImplemetedError("Dimension for %s not implemented" % group)

    return headers, dct
示例#41
0
    def _rank(self, K):
        """
        Return the dimension of the level N space of given weight. 
        """
        if not K is QQ:
            raise NotImplementedError

        if not self.__level.is_prime():
            raise NotImplementedError

        if self.__weight % 2 != 0:
            raise NotImplementedError("Only even weights available")

        N = self.__level

        if N == 1:
            ## By Igusa's theorem on the generators of the graded ring
            P = PowerSeriesRing(ZZ, 't')
            t = P.gen(0)

            dims = 1 / ((1 - t**4) * (1 - t**6) * (1 - t**10) *
                        (1 - t**12)).add_bigoh(self.__weight + 1)

            return dims[self.__weight]
        if N == 2:
            ## As in Ibukiyama, Onodera - On the graded ring of modular forms of the Siegel
            ## paramodular group of level 2, Proposition 2
            P = PowerSeriesRing(ZZ, 't')
            t = P.gen(0)

            dims = ((1 + t**10) * (1 + t**12) * (1 + t**11))
            dims = dims / ((1 - t**4) * (1 - t**6) * (1 - t**8) *
                           (1 - t**12)).add_bigoh(self.__weight + 1)

            return dims[self.__weight]
        if N == 3:
            ## By Dern, Paramodular forms of degree 2 and level 3, Corollary 5.6
            P = PowerSeriesRing(ZZ, 't')
            t = P.gen(0)

            dims = ((1 + t**12) * (1 + t**8 + t**9 + t**10 + t**11 + t**19))
            dims = dims / ((1 - t**4) * (1 - t**6)**2 *
                           (1 - t**12)).add_bigoh(self.__weight + 1)

            return dims[self.__weight]
        if N == 5:
            ## By Marschner, Paramodular forms of degree 2 with particular emphasis on level t = 5,
            ## Corollary 7.3.4. PhD thesis electronically available via the library of
            ## RWTH University, Aachen, Germany
            P = PowerSeriesRing(ZZ, 't')
            t = P.gen(0)

            dims =   t**30 + t**24 + t**23 + 2*t**22 + t**21 + 2*t**20 + t**19 + 2*t**18 \
                   + 2*t**16 + 2*t**14 + 2*t**12 + t**11 + 2*t**10 + t**9 + 2*t**8 + t**7 + t**6 + 1
            dims = dims / ((1 - t**4) * (1 - t**5) * (1 - t**6) *
                           (1 - t**12)).add_bigoh(self.__weight + 1)

            return dims[self.__weight]

        if self.__weight == 2:
            ## There are only cuspforms, since there is no elliptic modular form
            ## of weight 2.
            if N < 277:
                ## Poor, Yuen - Paramodular cusp forms tells us that all forms are
                ## Gritsenko lifts
                return JacobiFormD1NN_Gamma(self.__level, 2)._rank(QQ)

            raise NotImplementedError
        elif self.__weight == 4:
            ## This is the formula cited by Poor and Yuen in Paramodular cusp forms
            cuspidal_dim = Integer((N**2 - 143) / Integer(576) +
                                   N / Integer(8) + kronecker_symbol(-1, N) *
                                   (N - 12) / Integer(96) +
                                   kronecker_symbol(2, N) / Integer(8) +
                                   kronecker_symbol(3, N) / Integer(12) +
                                   kronecker_symbol(-3, N) * N / Integer(36))
        else:
            ## This is the formula given by Ibukiyama in
            ## Relations of dimension of automorphic forms of Sp(2,R) and its compact twist Sp(2),
            ## Theorem 4
            p = N
            k = self.__weight

            ## This is the reversed Ibukiyama symbol [.., .., ..; ..]
            def ibukiyama_symbol(modulus, *args):
                return args[k % modulus]

            ## if p == 2 this formula is wrong. If the weight is even it differs by
            ## -3/16 from the true dimension and if the weight is odd it differs by
            ## -1/16 from the true dimension.
            H1 = (p**2 + 1) * (2 * k - 2) * (2 * k - 3) * (
                2 * k - 4) / Integer(2**9 * 3**3 * 5)
            H2 = (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**8 * 3**2) \
                 + ( (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**7 * 3)
                     if p !=2 else
                     (-1)**k * (2 * k - 2) * (2 * k - 4) / Integer(2**9) )
            H3 = (ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) /
                  Integer(2**4 * 3) if p != 3 else 5 *
                  ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) /
                  Integer(2**5 * 3))
            H4 = (ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) /
                  Integer(2**2 * 3**3) if p != 2 else 5 *
                  ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) /
                  Integer(2**2 * 3**3))
            H5 = ibukiyama_symbol(6, -1, -k + 1, -k + 2, 1, k - 1,
                                  k - 2) / Integer(2**2 * 3**2)
            if p % 4 == 1:
                H6 = 5 * (2 * k - 3) * (p + 1) / Integer(
                    2**7 * 3) + (-1)**k * (p + 1) / Integer(2**7)
            elif p % 4 == 3:
                H6 = (2 * k - 3) * (p - 1) / Integer(
                    2**7) + 5 * (-1)**k * (p - 1) / Integer(2**7 * 3)
            else:
                H6 = 3 * (2 * k - 3) / Integer(2**7) + 7 * (-1)**k / Integer(
                    2**7 * 3)
            if p % 3 == 1:
                H7 =   (2 * k - 3) * (p + 1) / Integer(2 * 3**3) \
                     + (p + 1) * ibukiyama_symbol(3, 0, -1, 1) / Integer(2**2 * 3**3)
            elif p % 3 == 2:
                H7 =   (2 * k - 3) * (p - 1) / Integer(2**2 * 3**3) \
                     + (p - 1) * ibukiyama_symbol(3, 0, -1, 1) / Integer(2 * 3**3)
            else:
                H7 =   5 * (2 * k - 3) / Integer(2**2 * 3**3) \
                     + ibukiyama_symbol(3, 0, -1, 1) / Integer(3**3)
            H8 = ibukiyama_symbol(12, 1, 0, 0, -1, -1, -1, -1, 0, 0, 1, 1,
                                  1) / Integer(2 * 3)
            H9 = (2 * ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(3**2)
                  if p != 2 else ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) /
                  Integer(2 * 3**2))
            H10 = (1 + kronecker_symbol(5, p)) * ibukiyama_symbol(
                5, 1, 0, 0, -1, 0) / Integer(5)
            H11 = (1 + kronecker_symbol(2, p)) * ibukiyama_symbol(
                4, 1, 0, 0, -1) / Integer(2**3)
            if p % 12 == 1:
                H12 = ibukiyama_symbol(3, 0, 1, -1) / Integer(2 * 3)
            elif p % 12 == 11:
                H12 = (-1)**k / Integer(2 * 3)
            elif p == 2 or p == 3:
                H12 = (-1)**k / Integer(2**2 * 3)
            else:
                H12 = 0

            I1 = ibukiyama_symbol(6, 0, 1, 1, 0, -1, -1) / Integer(6)
            I2 = ibukiyama_symbol(3, -2, 1, 1) / Integer(2 * 3**2)
            if p == 3:
                I3 = ibukiyama_symbol(3, -2, 1, 1) / Integer(3**2)
            elif p % 3 == 1:
                I3 = 2 * ibukiyama_symbol(3, -1, 1, 0) / Integer(3**2)
            else:
                I3 = 2 * ibukiyama_symbol(3, -1, 0, 1) / Integer(3**2)
            I4 = ibukiyama_symbol(4, -1, 1, 1, -1) / Integer(2**2)
            I5 = (-1)**k / Integer(2**3)
            I6 = (-1)**k * (2 - kronecker_symbol(-1, p)) / Integer(2**4)
            I7 = -(-1)**k * (2 * k - 3) / Integer(2**3 * 3)
            I8 = -p * (2 * k - 3) / Integer(2**4 * 3**2)
            I9 = -1 / Integer(2**3 * 3)
            I10 = (p + 1) / Integer(2**3 * 3)
            I11 = -(1 + kronecker_symbol(-1, p)) / Integer(8)
            I12 = -(1 + kronecker_symbol(-3, p)) / Integer(6)

            cuspidal_dim =   H1 + H2 + H3 + H4 + H5 + H6 + H7 + H8 + H9 + H10 + H11 + H12 \
                           + I1 + I2 + I3 + I4 + I5 + I6 + I7 + I8 + I9 + I10 + I11 + I12

        mfs = ModularForms(1, self.__weight)

        return cuspidal_dim + mfs.dimension() + mfs.cuspidal_subspace(
        ).dimension()
示例#42
0
文件: ell_wp.py 项目: robertwb/sage
def compute_wp_fast(k, A, B, m):
    r"""
    Computes the Weierstrass function of an elliptic curve defined by short Weierstrass model: `y^2 = x^3 + Ax + B`. It does this with as fast as polynomial of degree `m` can be multiplied together in the base ring, i.e. `O(M(n))` in the notation of [BMSS].

    Let `p` be the characteristic of the underlying field: Then we must have either `p=0`, or `p > m + 3`.

    INPUT:

     - ``k`` - the base field of the curve
     - ``A`` - and
     - ``B`` - as the coeffients of the short Weierstrass model `y^2 = x^3 +Ax +B`, and
     - ``m`` - the precision to which the function is computed to.

    OUTPUT:

    the Weierstrass `\wp` function as a Laurent series to precision `m`.

    ALGORITHM:

    This function uses the algorithm described in section 3.3 of
    [BMSS].

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_fast
        sage: compute_wp_fast(QQ, 1, 8, 7)
        z^-2 - 1/5*z^2 - 8/7*z^4 + 1/75*z^6 + O(z^7)

        sage: k = GF(37)
        sage: compute_wp_fast(k, k(1), k(8), 5)
        z^-2 + 22*z^2 + 20*z^4 + O(z^5)

    """
    R = PowerSeriesRing(k,'z',default_prec=m+5)
    z = R.gen()
    s = 2
    f1 = z.add_bigoh(m+3)
    n = 2*m + 4

    # solve the nonlinear differential equation
    while (s < n):
        f1pr = f1.derivative()
        next_s = 2*s - 1

        a = 2*f1pr
        b = -(6*B*(f1**5) + 4*A*(f1**3))
        c = B*(f1**6) + A*f1**4 + 1 - (f1pr**2)

        # we should really be computing only mod z^next_s here.
        # but we loose only a factor 2
        f2 = solve_linear_differential_system(a, b, c, 0)
        # sometimes we get to 0 quicker than s reaches n
        if f2 == 0:
            break
        f1 = f1 + f2
        s = next_s

    R = f1
    Q = R**2
    pe = 1/Q

    return pe
示例#43
0
class MeromorphicFunctions(Parent, CachedRepresentation):
    Element = MeromorphicFunctionsElement

    def __init__(self, K, additive=True, dlog=True):
        Parent.__init__(self)
        self._additive = additive
        self._dlog = dlog
        self._base_ring = K
        self._prec = K.precision_cap()
        psprec = self._prec + 1 if dlog else self._prec
        self._Ps = PowerSeriesRing(self._base_ring,
                                   names='t',
                                   default_prec=psprec)
        if self._additive:
            self._V = FreeModule(K, self._prec)
        t = self._Ps.gen()
        self._Ps_local_variable = lambda Q: 1 - t / Q
        self._unset_coercions_used()
        self.register_action(Scaling(ZZ, self))
        self.register_action(MatrixAction(MatrixSpace(K, 2, 2), self))

    def acting_matrix(self, g, dim=None):
        try:
            g = g.matrix()
        except AttributeError:
            pass
        return self.get_action_data(g)

    @cached_method
    def get_action_data(self, g, K=None):
        a, b, c, d = g.list()
        prec = self._prec
        if K is None:
            if hasattr(a, 'lift'):
                a, b, c, d = a.lift(), b.lift(), c.lift(), d.lift()
                p = g.parent().base_ring().prime()
                K = ZpCA(p, prec)
            else:
                K = g.parent().base_ring()
        Ps = PowerSeriesRing(K, 't', default_prec=prec)
        z = Ps.gen()
        zz = (d * z - b) / (-c * z + a)
        zz_ps0 = Ps(zz).add_bigoh(prec)
        if self._dlog:
            zz_ps = ((a * d - b * c) * (-c * z + a)**-2).add_bigoh(prec)
        else:
            zz_ps = Ps(1).add_bigoh(prec)
        if self.is_additive():
            M = Matrix(ZZ, prec, prec, 0)
            for j in range(prec):
                for i, aij in enumerate(zz_ps.list()):
                    M[i, j] = aij
                if j < prec - 1:  # Don't need the last multiplication
                    zz_ps = (zz_ps0 * zz_ps).add_bigoh(prec)
                else:
                    return M
        else:
            ans = [Ps(1), zz_ps]
            for _ in range(prec - 1):
                zz_ps = (zz_ps0 * zz_ps).add_bigoh(prec)
                ans.append(zz_ps)
            return ans

    def is_additive(self):
        return self._additive

    def base_ring(self):
        return self._base_ring

    def power_series_ring(self):
        return self._Ps

    def _element_constructor_(self, data):
        return self.element_class(self, data)

    def _repr_(self):
        return "Meromorphic %s Functions over %s" % (
            'Additive' if self._additive else 'Multiplicative',
            self._base_ring)
示例#44
0
文件: ell_wp.py 项目: shalec/sage
def compute_wp_fast(k, A, B, m):
    r"""
    Computes the Weierstrass function of an elliptic curve defined by short Weierstrass model: `y^2 = x^3 + Ax + B`. It does this with as fast as polynomial of degree `m` can be multiplied together in the base ring, i.e. `O(M(n))` in the notation of [BMSS].

    Let `p` be the characteristic of the underlying field: Then we must have either `p=0`, or `p > m + 3`.

    INPUT:

     - ``k`` - the base field of the curve
     - ``A`` - and
     - ``B`` - as the coeffients of the short Weierstrass model `y^2 = x^3 +Ax +B`, and
     - ``m`` - the precision to which the function is computed to.

    OUTPUT:

    the Weierstrass `\wp` function as a Laurent series to precision `m`.

    ALGORITHM:

    This function uses the algorithm described in section 3.3 of
    [BMSS].

    EXAMPLES::

        sage: from sage.schemes.elliptic_curves.ell_wp import compute_wp_fast
        sage: compute_wp_fast(QQ, 1, 8, 7)
        z^-2 - 1/5*z^2 - 8/7*z^4 + 1/75*z^6 + O(z^7)

        sage: k = GF(37)
        sage: compute_wp_fast(k, k(1), k(8), 5)
        z^-2 + 22*z^2 + 20*z^4 + O(z^5)

    """
    R = PowerSeriesRing(k, 'z', default_prec=m + 5)
    z = R.gen()
    s = 2
    f1 = z.add_bigoh(m + 3)
    n = 2 * m + 4

    # solve the nonlinear differential equation
    while (s < n):
        f1pr = f1.derivative()
        next_s = 2 * s - 1

        a = 2 * f1pr
        b = -(6 * B * (f1**5) + 4 * A * (f1**3))
        c = B * (f1**6) + A * f1**4 + 1 - (f1pr**2)

        # we should really be computing only mod z^next_s here.
        # but we loose only a factor 2
        f2 = solve_linear_differential_system(a, b, c, 0)
        # sometimes we get to 0 quicker than s reaches n
        if f2 == 0:
            break
        f1 = f1 + f2
        s = next_s

    R = f1
    Q = R**2
    pe = 1 / Q

    return pe
示例#45
0
def _dimension_Gamma_2( wt_range, j, group = 'Gamma(2)'):
    """
    Return the dict
    {(k-> partition ->  [ d(k), e(k), c(k)] for k in wt_range]},
    where d(k), e(k), c(k) are the dimensions
    of the $p$-canonical part of $M_{k,j}( \Gamma(2))$ and its subspaces of
    Non-cusp forms and Cusp forms.
    """

    partitions = [ u'6', u'51', u'42', u'411', u'33', u'321',
                   u'311', u'222', u'2211', u'21111', u'111111']

    if is_odd(j):
        dct = dict( (k,dict((h,[0,0,0]) for h in partitions)) for k in wt_range)
        for k in dct:
            dct[k]['All'] = [0,0,0]
        partitions.insert( 0,'All')
        return partitions, dct
        
    if j>=2 and  wt_range[0] < 4:
        raise NotImplementedError( 'Dimensions of \(M_{k,j}\) for \(k<4\) and even \(j\ge 2\) not implemented')

    query = { 'sym_power': str(j), 'group' : 'Gamma(2)', 'space': 'total'}
    db_total = fetch( query)
    assert db_total, '%s: Data not available' % query
    query['space'] = 'cusp'
    db_cusp = fetch( query)
    assert db_cusp, '%s: Data not available' % query
    
    P = PowerSeriesRing( IntegerRing(),  default_prec =wt_range[-1] + 1,  names = ('t',))
    t = P.gen()
    total = dict()
    cusp = dict()
    for p in partitions:
        total[p] = eval(db_total[p])
        cusp[p] = eval(db_cusp[p])
    # total = dict( ( p, eval(db_total[p])) for p in partitions)
    # cusp = dict( ( p, eval(db_cusp[p])) for p in partitions)
    
    if 'Gamma(2)' == group:
        dct = dict( (k, dict( (p, [total[p][k], total[p][k]-cusp[p][k], cusp[p][k]])
                              for p in partitions)) for k in wt_range)
        for k in dct:
            dct[k]['All'] = [sum( dct[k][p][j] for p in dct[k]) for j in range(3)]
            
        partitions.insert( 0,'All')
        headers = partitions

    elif 'Gamma1(2)' == group:
        ps = { '3': ['6', '42', '222'],
               '21': ['51', '42', '321'],
               '111': ['411', '33']}
        
        dct = dict( (k, dict( (p,[
                            sum( total[q][k] for q in ps[p]),
                            sum( total[q][k]-cusp[q][k] for q in ps[p]),
                            sum( cusp[q][k] for q in ps[p]),
                            ]) for p in ps)) for k in wt_range) 
        for k in dct:
            dct[k]['All'] = [sum( dct[k][p][j] for p in dct[k]) for j in range(3)]       

        headers = ps.keys()
        headers.sort( reverse = True)
        headers.insert( 0,'All')

    elif 'Gamma0(2)' == group:
        headers = ['Total', 'Non cusp', 'Cusp']
        ps = ['6', '42', '222']
        dct = dict( (k, { 'Total': sum( total[p][k] for p in ps),
                          'Non cusp': sum( total[p][k]-cusp[p][k] for p in ps),
                          'Cusp': sum( cusp[p][k] for p in ps)})
                    for k in wt_range)

    elif 'Sp4(Z)' == group:
        headers = ['Total', 'Non cusp', 'Cusp']
        p = '6'
        dct = dict( (k, { 'Total': total[p][k],
                          'Non cusp': total[p][k]-cusp[p][k],
                          'Cusp': cusp[p][k]})
                    for k in wt_range)
    else:
        raise NotImplemetedError( 'Dimension for %s not implemented' % group)

    return headers, dct
示例#46
0
    def _rank(self, K):
        """
        Return the dimension of the level N space of given weight. 
        """
        if not K is QQ:
            raise NotImplementedError

        if not self.__level.is_prime():
            raise NotImplementedError

        if self.__weight % 2 != 0:
            raise NotImplementedError("Only even weights available")

        N = self.__level

        if N == 1:
            ## By Igusa's theorem on the generators of the graded ring
            P = PowerSeriesRing(ZZ, "t")
            t = P.gen(0)

            dims = 1 / ((1 - t ** 4) * (1 - t ** 6) * (1 - t ** 10) * (1 - t ** 12)).add_bigoh(self.__weight + 1)

            return dims[self.__weight]
        if N == 2:
            ## As in Ibukiyama, Onodera - On the graded ring of modular forms of the Siegel
            ## paramodular group of level 2, Proposition 2
            P = PowerSeriesRing(ZZ, "t")
            t = P.gen(0)

            dims = (1 + t ** 10) * (1 + t ** 12) * (1 + t ** 11)
            dims = dims / ((1 - t ** 4) * (1 - t ** 6) * (1 - t ** 8) * (1 - t ** 12)).add_bigoh(self.__weight + 1)

            return dims[self.__weight]
        if N == 3:
            ## By Dern, Paramodular forms of degree 2 and level 3, Corollary 5.6
            P = PowerSeriesRing(ZZ, "t")
            t = P.gen(0)

            dims = (1 + t ** 12) * (1 + t ** 8 + t ** 9 + t ** 10 + t ** 11 + t ** 19)
            dims = dims / ((1 - t ** 4) * (1 - t ** 6) ** 2 * (1 - t ** 12)).add_bigoh(self.__weight + 1)

            return dims[self.__weight]
        if N == 5:
            ## By Marschner, Paramodular forms of degree 2 with particular emphasis on level t = 5,
            ## Corollary 7.3.4. PhD thesis electronically available via the library of
            ## RWTH University, Aachen, Germany
            P = PowerSeriesRing(ZZ, "t")
            t = P.gen(0)

            dims = (
                t ** 30
                + t ** 24
                + t ** 23
                + 2 * t ** 22
                + t ** 21
                + 2 * t ** 20
                + t ** 19
                + 2 * t ** 18
                + 2 * t ** 16
                + 2 * t ** 14
                + 2 * t ** 12
                + t ** 11
                + 2 * t ** 10
                + t ** 9
                + 2 * t ** 8
                + t ** 7
                + t ** 6
                + 1
            )
            dims = dims / ((1 - t ** 4) * (1 - t ** 5) * (1 - t ** 6) * (1 - t ** 12)).add_bigoh(self.__weight + 1)

            return dims[self.__weight]

        if self.__weight == 2:
            ## There are only cuspforms, since there is no elliptic modular form
            ## of weight 2.
            if N < 277:
                ## Poor, Yuen - Paramodular cusp forms tells us that all forms are
                ## Gritsenko lifts
                return JacobiFormD1NN_Gamma(self.__level, 2)._rank(QQ)

            raise NotImplementedError
        elif self.__weight == 4:
            ## This is the formula cited by Poor and Yuen in Paramodular cusp forms
            cuspidal_dim = Integer(
                (N ** 2 - 143) / Integer(576)
                + N / Integer(8)
                + kronecker_symbol(-1, N) * (N - 12) / Integer(96)
                + kronecker_symbol(2, N) / Integer(8)
                + kronecker_symbol(3, N) / Integer(12)
                + kronecker_symbol(-3, N) * N / Integer(36)
            )
        else:
            ## This is the formula given by Ibukiyama in
            ## Relations of dimension of automorphic forms of Sp(2,R) and its compact twist Sp(2),
            ## Theorem 4
            p = N
            k = self.__weight

            ## This is the reversed Ibukiyama symbol [.., .., ..; ..]
            def ibukiyama_symbol(modulus, *args):
                return args[k % modulus]

            ## if p == 2 this formula is wrong. If the weight is even it differs by
            ## -3/16 from the true dimension and if the weight is odd it differs by
            ## -1/16 from the true dimension.
            H1 = (p ** 2 + 1) * (2 * k - 2) * (2 * k - 3) * (2 * k - 4) / Integer(2 ** 9 * 3 ** 3 * 5)
            H2 = (-1) ** k * (2 * k - 2) * (2 * k - 4) / Integer(2 ** 8 * 3 ** 2) + (
                (-1) ** k * (2 * k - 2) * (2 * k - 4) / Integer(2 ** 7 * 3)
                if p != 2
                else (-1) ** k * (2 * k - 2) * (2 * k - 4) / Integer(2 ** 9)
            )
            H3 = (
                ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) / Integer(2 ** 4 * 3)
                if p != 3
                else 5 * ibukiyama_symbol(4, k - 2, -k + 1, -k + 2, k - 1) / Integer(2 ** 5 * 3)
            )
            H4 = (
                ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) / Integer(2 ** 2 * 3 ** 3)
                if p != 2
                else 5 * ibukiyama_symbol(3, 2 * k - 3, -k + 1, -k + 2) / Integer(2 ** 2 * 3 ** 3)
            )
            H5 = ibukiyama_symbol(6, -1, -k + 1, -k + 2, 1, k - 1, k - 2) / Integer(2 ** 2 * 3 ** 2)
            if p % 4 == 1:
                H6 = 5 * (2 * k - 3) * (p + 1) / Integer(2 ** 7 * 3) + (-1) ** k * (p + 1) / Integer(2 ** 7)
            elif p % 4 == 3:
                H6 = (2 * k - 3) * (p - 1) / Integer(2 ** 7) + 5 * (-1) ** k * (p - 1) / Integer(2 ** 7 * 3)
            else:
                H6 = 3 * (2 * k - 3) / Integer(2 ** 7) + 7 * (-1) ** k / Integer(2 ** 7 * 3)
            if p % 3 == 1:
                H7 = (2 * k - 3) * (p + 1) / Integer(2 * 3 ** 3) + (p + 1) * ibukiyama_symbol(3, 0, -1, 1) / Integer(
                    2 ** 2 * 3 ** 3
                )
            elif p % 3 == 2:
                H7 = (2 * k - 3) * (p - 1) / Integer(2 ** 2 * 3 ** 3) + (p - 1) * ibukiyama_symbol(
                    3, 0, -1, 1
                ) / Integer(2 * 3 ** 3)
            else:
                H7 = 5 * (2 * k - 3) / Integer(2 ** 2 * 3 ** 3) + ibukiyama_symbol(3, 0, -1, 1) / Integer(3 ** 3)
            H8 = ibukiyama_symbol(12, 1, 0, 0, -1, -1, -1, -1, 0, 0, 1, 1, 1) / Integer(2 * 3)
            H9 = (
                2 * ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(3 ** 2)
                if p != 2
                else ibukiyama_symbol(6, 1, 0, 0, -1, 0, 0) / Integer(2 * 3 ** 2)
            )
            H10 = (1 + kronecker_symbol(5, p)) * ibukiyama_symbol(5, 1, 0, 0, -1, 0) / Integer(5)
            H11 = (1 + kronecker_symbol(2, p)) * ibukiyama_symbol(4, 1, 0, 0, -1) / Integer(2 ** 3)
            if p % 12 == 1:
                H12 = ibukiyama_symbol(3, 0, 1, -1) / Integer(2 * 3)
            elif p % 12 == 11:
                H12 = (-1) ** k / Integer(2 * 3)
            elif p == 2 or p == 3:
                H12 = (-1) ** k / Integer(2 ** 2 * 3)
            else:
                H12 = 0

            I1 = ibukiyama_symbol(6, 0, 1, 1, 0, -1, -1) / Integer(6)
            I2 = ibukiyama_symbol(3, -2, 1, 1) / Integer(2 * 3 ** 2)
            if p == 3:
                I3 = ibukiyama_symbol(3, -2, 1, 1) / Integer(3 ** 2)
            elif p % 3 == 1:
                I3 = 2 * ibukiyama_symbol(3, -1, 1, 0) / Integer(3 ** 2)
            else:
                I3 = 2 * ibukiyama_symbol(3, -1, 0, 1) / Integer(3 ** 2)
            I4 = ibukiyama_symbol(4, -1, 1, 1, -1) / Integer(2 ** 2)
            I5 = (-1) ** k / Integer(2 ** 3)
            I6 = (-1) ** k * (2 - kronecker_symbol(-1, p)) / Integer(2 ** 4)
            I7 = -(-1) ** k * (2 * k - 3) / Integer(2 ** 3 * 3)
            I8 = -p * (2 * k - 3) / Integer(2 ** 4 * 3 ** 2)
            I9 = -1 / Integer(2 ** 3 * 3)
            I10 = (p + 1) / Integer(2 ** 3 * 3)
            I11 = -(1 + kronecker_symbol(-1, p)) / Integer(8)
            I12 = -(1 + kronecker_symbol(-3, p)) / Integer(6)

            cuspidal_dim = (
                H1
                + H2
                + H3
                + H4
                + H5
                + H6
                + H7
                + H8
                + H9
                + H10
                + H11
                + H12
                + I1
                + I2
                + I3
                + I4
                + I5
                + I6
                + I7
                + I8
                + I9
                + I10
                + I11
                + I12
            )

        mfs = ModularForms(1, self.__weight)

        return cuspidal_dim + mfs.dimension() + mfs.cuspidal_subspace().dimension()
示例#47
0
class MPowerSeriesRing_generic(PowerSeriesRing_generic, Nonexact):
    r"""
    A multivariate power series ring.  This class is implemented as a
    single variable power series ring in the variable ``T`` over a
    multivariable polynomial ring in the specified generators.  Each
    generator ``g`` of the multivariable polynomial ring (called the
    "foreground ring") is mapped to ``g*T`` in the single variable power series
    ring (called the "background ring").  The background power series ring
    is used to do arithmetic and track total-degree precision.  The
    foreground polynomial ring is used to display elements.

    For usage and examples, see above, and :meth:`PowerSeriesRing`.
    """
    ### methods from PowerSeriesRing_generic that we *don't* override:
    #
    # variable_names_recursive : works just fine
    #
    # __contains__ : works just fine
    #
    # base_extend : works just fine
    #
    # is_exact : works just fine
    #
    # random_element : works just fine
    #
    # is_field : works just fine
    #
    # is_finite : works just fine
    #
    # __setitem__ : works just fine
    #
    #
    #### notes
    #
    # sparse setting may not be implemented completely
    Element = MPowerSeries

    def __init__(self,
                 base_ring,
                 num_gens,
                 name_list,
                 order='negdeglex',
                 default_prec=10,
                 sparse=False):
        """
        Initializes a multivariate power series ring.  See PowerSeriesRing
        for complete documentation.

        INPUT

            - ``base_ring`` - a commutative ring

            - ``num_gens`` - number of generators

            - ``name_list`` - List of indeterminate names or a single name.
                If a single name is given, indeterminates will be this name
                followed by a number from 0 to num_gens - 1.  If a list is
                given, these will be the indeterminate names and the length
                of the list must be equal to num_gens.

            - ``order`` - ordering of variables; default is
              negative degree lexicographic

            - ``default_prec`` - The default total-degree precision for
              elements.  The default value of default_prec is 10.

            - ``sparse`` - whether or not power series are sparse

        EXAMPLES::

            sage: R.<t,u,v> = PowerSeriesRing(QQ)
            sage: g = 1 + v + 3*u*t^2 - 2*v^2*t^2
            sage: g = g.add_bigoh(5); g
            1 + v + 3*t^2*u - 2*t^2*v^2 + O(t, u, v)^5
            sage: g in R
            True

        TESTS:

        By :trac:`14084`, the multi-variate power series ring belongs to the
        category of integral domains, if the base ring does::

            sage: P = ZZ[['x','y']]
            sage: P.category()
            Category of integral domains
            sage: TestSuite(P).run()

        Otherwise, it belongs to the category of commutative rings::

            sage: P = Integers(15)[['x','y']]
            sage: P.category()
            Category of commutative rings
            sage: TestSuite(P).run()

        """
        order = TermOrder(order, num_gens)
        self._term_order = order
        if not base_ring.is_commutative():
            raise TypeError("Base ring must be a commutative ring.")
        n = int(num_gens)
        if n < 0:
            raise ValueError(
                "Multivariate Polynomial Rings must have more than 0 variables."
            )
        self._ngens = n
        self._has_singular = False  #cannot convert to Singular by default
        # Multivariate power series rings inherit from power series rings. But
        # apparently we can not call their initialisation. Instead, initialise
        # CommutativeRing and Nonexact:
        CommutativeRing.__init__(self,
                                 base_ring,
                                 name_list,
                                 category=_IntegralDomains if base_ring
                                 in _IntegralDomains else _CommutativeRings)
        Nonexact.__init__(self, default_prec)

        # underlying polynomial ring in which to represent elements
        self._poly_ring_ = PolynomialRing(base_ring,
                                          self.variable_names(),
                                          sparse=sparse,
                                          order=order)
        # because sometimes PowerSeriesRing_generic calls self.__poly_ring
        self._PowerSeriesRing_generic__poly_ring = self._poly_ring()

        # background univariate power series ring
        self._bg_power_series_ring = PowerSeriesRing(self._poly_ring_,
                                                     'Tbg',
                                                     sparse=sparse,
                                                     default_prec=default_prec)
        self._bg_indeterminate = self._bg_power_series_ring.gen()

        self._is_sparse = sparse
        self._params = (base_ring, num_gens, name_list, order, default_prec,
                        sparse)
        self._populate_coercion_lists_()

    def _repr_(self):
        """
        Prints out a multivariate power series ring.

        EXAMPLES::

            sage: R.<x,y> = PowerSeriesRing(GF(17))
            sage: R  #indirect doctest
            Multivariate Power Series Ring in x, y over Finite Field of size 17
            sage: R.rename('my multivariate power series ring')
            sage: R
            my multivariate power series ring
        """
        if self.ngens() == 0:
            generators_rep = "no variables"
        else:
            generators_rep = ", ".join(self.variable_names())

        s = "Multivariate Power Series Ring in %s over %s" % (generators_rep,
                                                              self.base_ring())
        if self.is_sparse():
            s = 'Sparse ' + s
        return s

    def _latex_(self):
        """
        Returns latex representation of power series ring

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'v'); M
            Multivariate Power Series Ring in v0, v1, v2, v3 over Rational Field
            sage: M._latex_()
            '\\Bold{Q}[[v_{0}, v_{1}, v_{2}, v_{3}]]'
        """
        generators_latex = ", ".join(self.latex_variable_names())
        return "%s[[%s]]" % (latex.latex(self.base_ring()), generators_latex)

    def is_integral_domain(self, proof=False):
        """
        Return True if the base ring is an integral domain; otherwise
        return False.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'v'); M
            Multivariate Power Series Ring in v0, v1, v2, v3 over Rational Field
            sage: M.is_integral_domain()
            True
        """
        return self.base_ring().is_integral_domain()

    def is_noetherian(self, proof=False):
        """
        Power series over a Noetherian ring are Noetherian.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'v'); M
            Multivariate Power Series Ring in v0, v1, v2, v3 over Rational Field
            sage: M.is_noetherian()
            True

            sage: W = PowerSeriesRing(InfinitePolynomialRing(ZZ,'a'),2,'x,y')
            sage: W.is_noetherian()
            False
        """
        return self.base_ring().is_noetherian()

    def term_order(self):
        """
        Print term ordering of self.  Term orderings are implemented by the
        TermOrder class.

        EXAMPLES::

            sage: M.<x,y,z> = PowerSeriesRing(ZZ,3);
            sage: M.term_order()
            Negative degree lexicographic term order
            sage: m = y*z^12 - y^6*z^8 - x^7*y^5*z^2 + x*y^2*z + M.O(15); m
            x*y^2*z + y*z^12 - x^7*y^5*z^2 - y^6*z^8 + O(x, y, z)^15

            sage: N = PowerSeriesRing(ZZ,3,'x,y,z', order="deglex");
            sage: N.term_order()
            Degree lexicographic term order
            sage: N(m)
            -x^7*y^5*z^2 - y^6*z^8 + y*z^12 + x*y^2*z + O(x, y, z)^15
        """
        return self._term_order

    def characteristic(self):
        """
        Return characteristic of base ring, which is characteristic of self.

        EXAMPLES::

            sage: H = PowerSeriesRing(GF(65537),4,'f'); H
            Multivariate Power Series Ring in f0, f1, f2, f3 over
            Finite Field of size 65537
            sage: H.characteristic()
            65537
        """
        return self.base_ring().characteristic()

    def construction(self):
        """
        Returns a functor F and base ring R such that F(R) == self.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'f'); M
            Multivariate Power Series Ring in f0, f1, f2, f3 over Rational Field

            sage: (c,R) = M.construction(); (c,R)
            (Completion[('f0', 'f1', 'f2', 'f3')],
            Multivariate Polynomial Ring in f0, f1, f2, f3 over Rational Field)
            sage: c
            Completion[('f0', 'f1', 'f2', 'f3')]
            sage: c(R)
            Multivariate Power Series Ring in f0, f1, f2, f3 over Rational Field
            sage: c(R) == M
            True
        """
        from sage.categories.pushout import CompletionFunctor
        return (CompletionFunctor(self._names,
                                  self.default_prec()), self._poly_ring())

    def change_ring(self, R):
        """
        Returns the power series ring over R in the same variable as self.
        This function ignores the question of whether the base ring of self
        is or can extend to the base ring of R; for the latter, use
        base_extend.

        EXAMPLES::

            sage: R.<t,u,v> = PowerSeriesRing(QQ); R
            Multivariate Power Series Ring in t, u, v over Rational Field
            sage: R.base_extend(RR)
            Multivariate Power Series Ring in t, u, v over Real Field with
            53 bits of precision
            sage: R.change_ring(IntegerModRing(10))
            Multivariate Power Series Ring in t, u, v over Ring of integers
            modulo 10
            sage: R.base_extend(IntegerModRing(10))
            Traceback (most recent call last):
            ...
            TypeError: no base extension defined


            sage: S = PowerSeriesRing(GF(65537),2,'x,y'); S
            Multivariate Power Series Ring in x, y over Finite Field of size
            65537
            sage: S.change_ring(GF(5))
            Multivariate Power Series Ring in x, y over Finite Field of size 5
        """
        return PowerSeriesRing(R,
                               names=self.variable_names(),
                               default_prec=self.default_prec())

    def remove_var(self, *var):
        """
        Remove given variable or sequence of variables from self.

        EXAMPLES::

            sage: A.<s,t,u> = PowerSeriesRing(ZZ)
            sage: A.remove_var(t)
            Multivariate Power Series Ring in s, u over Integer Ring
            sage: A.remove_var(s,t)
            Power Series Ring in u over Integer Ring


            sage: M = PowerSeriesRing(GF(5),5,'t'); M
            Multivariate Power Series Ring in t0, t1, t2, t3, t4 over
            Finite Field of size 5
            sage: M.remove_var(M.gens()[3])
            Multivariate Power Series Ring in t0, t1, t2, t4 over Finite
            Field of size 5

        Removing all variables results in the base ring::

            sage: M.remove_var(*M.gens())
            Finite Field of size 5

        """
        vars = list(self.variable_names())
        for v in var:
            vars.remove(str(v))
        if len(vars) == 0:
            return self.base_ring()
        return PowerSeriesRing(self.base_ring(), names=vars)

    ## this is defined in PowerSeriesRing_generic
    # def __call__(self, f, prec=infinity):
    #     """
    #     Coerce object to this multivariate power series ring.
    #     """
    #     return

    def _coerce_impl(self, f):
        """
        Return the canonical coercion of ``f`` into this multivariate power
        series ring, if one is defined, or raise a TypeError.

        The rings that canonically coerce to this multivariate power series
        ring are:

            - this ring itself

            - a polynomial or power series ring in the same variables or a
              subset of these variables (possibly empty), over any base
              ring that canonically coerces into the base ring of this ring

        EXAMPLES::

            sage: R.<t,u,v> = PowerSeriesRing(QQ); R
            Multivariate Power Series Ring in t, u, v over Rational Field
            sage: S1.<t,v> = PolynomialRing(ZZ); S1
            Multivariate Polynomial Ring in t, v over Integer Ring
            sage: f1 = -t*v + 2*v^2 + v; f1
            -t*v + 2*v^2 + v
            sage: R(f1)
            v - t*v + 2*v^2
            sage: S2.<u,v> = PowerSeriesRing(ZZ); S2
            Multivariate Power Series Ring in u, v over Integer Ring
            sage: f2 = -2*v^2 + 5*u*v^2 + S2.O(6); f2
            -2*v^2 + 5*u*v^2 + O(u, v)^6
            sage: R(f2)
            -2*v^2 + 5*u*v^2 + O(t, u, v)^6

            sage: R2 = R.change_ring(GF(2))
            sage: R2(f1)
            v + t*v
            sage: R2(f2)
            u*v^2 + O(t, u, v)^6

        TESTS::

            sage: R.<t,u,v> = PowerSeriesRing(QQ)
            sage: S1.<t,v> = PolynomialRing(ZZ)
            sage: f1 = S1.random_element()
            sage: g1 = R._coerce_impl(f1)
            sage: f1.parent() == R
            False
            sage: g1.parent() == R
            True

        """

        P = f.parent()
        if is_MPolynomialRing(P) or is_MPowerSeriesRing(P) \
               or is_PolynomialRing(P) or is_PowerSeriesRing(P):
            if set(P.variable_names()).issubset(set(self.variable_names())):
                if self.has_coerce_map_from(P.base_ring()):
                    return self(f)
        else:
            return self._coerce_try(f, [self.base_ring()])

    def _is_valid_homomorphism_(self, codomain, im_gens):
        """
        Replacement for method of PowerSeriesRing_generic.

        To be valid, a homomorphism must send generators to elements of
        positive valuation or to nilpotent elements.

        Note that the method is_nilpotent doesn't (as of sage 4.4) seem to
        be defined for obvious examples (matrices, quotients of polynomial
        rings).

        EXAMPLES::

            sage: R.<a,b,c> = PowerSeriesRing(Zmod(8)); R
            Multivariate Power Series Ring in a, b, c over Ring of integers
            modulo 8
            sage: M = PowerSeriesRing(ZZ,3,'x,y,z');
            sage: M._is_valid_homomorphism_(R,[a,c,b])
            True

            sage: M._is_valid_homomorphism_(R,[0,c,b])
            True

        2 is nilpotent in `ZZ/8`, but 3 is not::

            sage: M._is_valid_homomorphism_(R,[2,c,b])
            True
            sage: M._is_valid_homomorphism_(R,[3,c,b])
            False

        Over `ZZ`, 2 is not nilpotent::

            sage: S = R.change_ring(ZZ); S
            Multivariate Power Series Ring in a, b, c over Integer Ring
            sage: M._is_valid_homomorphism_(S,[a,c,b])
            True
            sage: M._is_valid_homomorphism_(S,[0,c,b])
            True
            sage: M._is_valid_homomorphism_(S,[2,c,b])
            False

            sage: g = [S.random_element(10)*v for v in S.gens()]
            sage: M._is_valid_homomorphism_(S,g)
            True
        """
        try:
            im_gens = [codomain(v) for v in im_gens]
        except TypeError:
            raise TypeError(
                "The given generator images do not coerce to codomain.")

        if len(im_gens) is not self.ngens():
            raise ValueError("You must specify the image of each generator.")
        if all(v == 0 for v in im_gens):
            return True
        if is_MPowerSeriesRing(codomain) or is_PowerSeriesRing(codomain):
            try:
                B = all(v.valuation() > 0 or v.is_nilpotent() for v in im_gens)
            except NotImplementedError:
                B = all(v.valuation() > 0 for v in im_gens)
            return B
        if is_CommutativeRing(codomain):
            return all(v.is_nilpotent() for v in im_gens)

    def _coerce_map_from_(self, P):
        """
        The rings that canonically coerce to this multivariate power series
        ring are:

            - this ring itself

            - a polynomial or power series ring in the same variables or a
              subset of these variables (possibly empty), over any base
              ring that canonically coerces into this ring

            - any ring that coerces into the foreground polynomial ring of this ring

        EXAMPLES::

            sage: A = GF(17)[['x','y']]
            sage: A.has_coerce_map_from(ZZ)
            True
            sage: A.has_coerce_map_from(ZZ['x'])
            True
            sage: A.has_coerce_map_from(ZZ['y','x'])
            True
            sage: A.has_coerce_map_from(ZZ[['x']])
            True
            sage: A.has_coerce_map_from(ZZ[['y','x']])
            True
            sage: A.has_coerce_map_from(ZZ['x','z'])
            False
            sage: A.has_coerce_map_from(GF(3)['x','y'])
            False
            sage: A.has_coerce_map_from(Frac(ZZ['y','x']))
            False

        TESTS::

            sage: M = PowerSeriesRing(ZZ,3,'x,y,z');
            sage: M._coerce_map_from_(M)
            True
            sage: M._coerce_map_from_(M.remove_var(x))
            True
            sage: M._coerce_map_from_(PowerSeriesRing(ZZ,x))
            True
            sage: M._coerce_map_from_(PolynomialRing(ZZ,'x,z'))
            True
            sage: M._coerce_map_from_(PolynomialRing(ZZ,0,''))
            True
            sage: M._coerce_map_from_(ZZ)
            True

            sage: M._coerce_map_from_(Zmod(13))
            False
            sage: M._coerce_map_from_(PolynomialRing(ZZ,2,'x,t'))
            False
            sage: M._coerce_map_from_(PolynomialRing(Zmod(11),2,'x,y'))
            False

            sage: P = PolynomialRing(ZZ,3,'z')
            sage: H = PowerSeriesRing(P,4,'f'); H
            Multivariate Power Series Ring in f0, f1, f2, f3 over Multivariate Polynomial Ring in z0, z1, z2 over Integer Ring
            sage: H._coerce_map_from_(P)
            True
            sage: H._coerce_map_from_(P.remove_var(P.gen(1)))
            True
            sage: H._coerce_map_from_(PolynomialRing(ZZ,'z2,f0'))
            True

        """
        if is_MPolynomialRing(P) or is_MPowerSeriesRing(P) \
                   or is_PolynomialRing(P) or is_PowerSeriesRing(P):
            if set(P.variable_names()).issubset(set(self.variable_names())):
                if self.has_coerce_map_from(P.base_ring()):
                    return True

        return self._poly_ring().has_coerce_map_from(P)

    def _element_constructor_(self, f, prec=None):
        """
        TESTS::

            sage: M = PowerSeriesRing(ZZ,5,'t');
            sage: t = M.gens();
            sage: m = -2*t[0]*t[3]^6*t[4] - 12*t[0]^2*t[3]*t[4]^6 + t[1]*t[2]*t[3]^4*t[4]^3 + M.O(10)
            sage: M._element_constructor_(m)
            -2*t0*t3^6*t4 - 12*t0^2*t3*t4^6 + t1*t2*t3^4*t4^3 +
            O(t0, t1, t2, t3, t4)^10
            sage: R = PolynomialRing(ZZ,5,'t')
            sage: t = R.gens()
            sage: p = -4*t[0]*t[4] + t[1]^2 + t[1]*t[2] - 6*t[2]*t[4] - t[3]*t[4]
            sage: M._element_constructor_(p)
            -4*t0*t4 + t1^2 + t1*t2 - 6*t2*t4 - t3*t4
            sage: p.parent()
            Multivariate Polynomial Ring in t0, t1, t2, t3, t4 over Integer Ring
            sage: M._element_constructor_(p).parent()
            Multivariate Power Series Ring in t0, t1, t2, t3, t4 over
            Integer Ring
        """
        if prec is None:
            try:
                prec = f.prec()
            except AttributeError:
                prec = infinity
        return self.element_class(parent=self, x=f, prec=prec)

    def __cmp__(self, other):
        """
        Compare this multivariate power series ring to something else.

        Power series rings are considered equal if the base ring, variable
        names, and default truncation precision are the same.  Note that we
        don't compare term-ordering.

        First the base rings are compared, then the variable names, then
        the default precision.

        EXAMPLES::

            sage: R.<t,u> = PowerSeriesRing(ZZ)
            sage: S.<t,u> = PowerSeriesRing(ZZ)
            sage: R is S
            True
            sage: R == S
            True
            sage: S.<t,u> = PowerSeriesRing(ZZ, default_prec=30)
            sage: R == S
            False
            sage: PowerSeriesRing(QQ,3,'t') == PowerSeriesRing(ZZ,3,'t')
            False
            sage: PowerSeriesRing(QQ,5,'t') == 5
            False
        """
        if not isinstance(other, MPowerSeriesRing_generic):
            return -1
        c = cmp(self.base_ring(), other.base_ring())
        if c: return c
        c = cmp(self.variable_names(), other.variable_names())
        if c: return c
        c = cmp(self.default_prec(), other.default_prec())
        if c: return c
        return 0

    def laurent_series_ring(self):
        """
        Laruent series not yet implemented for multivariate power series rings

        TESTS::

            sage: M = PowerSeriesRing(ZZ,3,'x,y,z');
            sage: M.laurent_series_ring()
            Traceback (most recent call last):
            ...
            NotImplementedError: Laurent series not implemented for
            multivariate power series.
        """
        raise NotImplementedError(
            "Laurent series not implemented for multivariate power series.")

    def _poly_ring(self, x=None):
        """
        Return the underlying polynomial ring used to represent elements of
        this power series ring.  If given an input x, returns x coerced
        into this polynomial ring.

        EXAMPLES::

            sage: R.<t,u> = PowerSeriesRing(QQ)
            sage: R._poly_ring()
            Multivariate Polynomial Ring in t, u over Rational Field
            sage: R._poly_ring(2).parent()
            Multivariate Polynomial Ring in t, u over Rational Field
        """
        if x is None:
            return self._poly_ring_
        else:
            return self._poly_ring_(x)

    def _mpoly_ring(self, x=None):
        """
        Same as _poly_ring

        TESTS::

            sage: R.<t,u> = PowerSeriesRing(QQ)
            sage: R._mpoly_ring()
            Multivariate Polynomial Ring in t, u over Rational Field
            sage: R._mpoly_ring(2).parent()
            Multivariate Polynomial Ring in t, u over Rational Field
        """
        return self._poly_ring(x)

    def _bg_ps_ring(self, x=None):
        """
        Return the background univariate power series ring.  If given an
        input x, returns x coerced into this power series ring.

        EXAMPLES::

            sage: R.<t,u> = PowerSeriesRing(QQ)
            sage: R._bg_ps_ring()
            Power Series Ring in Tbg over Multivariate Polynomial Ring in
            t, u over Rational Field
            sage: R._bg_ps_ring(4).parent() == R
            False

        """
        if x is None:
            return self._bg_power_series_ring
        else:
            return self._bg_power_series_ring(x)

    def is_sparse(self):
        """
        Is self sparse?

        EXAMPLES::

            sage: M = PowerSeriesRing(ZZ,3,'s,t,u'); M
            Multivariate Power Series Ring in s, t, u over Integer Ring
            sage: M.is_sparse()
            False
            sage: N = PowerSeriesRing(ZZ,3,'s,t,u',sparse=True); N
            Sparse Multivariate Power Series Ring in s, t, u over Integer Ring
            sage: N.is_sparse()
            True
        """
        return self._is_sparse

    def is_dense(self):
        """
        Is self dense? (opposite of sparse)

        EXAMPLES::

            sage: M = PowerSeriesRing(ZZ,3,'s,t,u'); M
            Multivariate Power Series Ring in s, t, u over Integer Ring
            sage: M.is_dense()
            True
            sage: N = PowerSeriesRing(ZZ,3,'s,t,u',sparse=True); N
            Sparse Multivariate Power Series Ring in s, t, u over Integer Ring
            sage: N.is_dense()
            False
        """
        return not self.is_sparse()

    def gen(self, n=0):
        """
        Return the nth generator of self.

        EXAMPLES::

            sage: M = PowerSeriesRing(ZZ,10,'v');
            sage: M.gen(6)
            v6
        """
        if n < 0 or n >= self._ngens:
            raise ValueError("Generator not defined.")
        #return self(self._poly_ring().gens()[int(n)])
        return self.element_class(parent=self,
                                  x=self._poly_ring().gens()[int(n)],
                                  is_gen=True)

    def ngens(self):
        """
        Return number of generators of self.

        EXAMPLES::

            sage: M = PowerSeriesRing(ZZ,10,'v');
            sage: M.ngens()
            10
        """
        return self._ngens

    def prec_ideal(self):
        """
        Return the ideal which determines precision; this is the ideal
        generated by all of the generators of our background polynomial
        ring.

        EXAMPLES::

            sage: A.<s,t,u> = PowerSeriesRing(ZZ)
            sage: A.prec_ideal()
            Ideal (s, t, u) of Multivariate Polynomial Ring in s, t, u over
            Integer Ring
        """
        return self._poly_ring().ideal(self._poly_ring().gens())

    def bigoh(self, prec):
        """
        Return big oh with precision ``prec``.  The function ``O`` does the same thing.

        EXAMPLES::

            sage: T.<a,b> = PowerSeriesRing(ZZ,2); T
            Multivariate Power Series Ring in a, b over Integer Ring
            sage: T.bigoh(10)
            0 + O(a, b)^10
            sage: T.O(10)
            0 + O(a, b)^10
        """
        return self(0).O(prec)

    def O(self, prec):
        """
        Return big oh with precision ``prec``.  This function is an alias for ``bigoh``.

        EXAMPLES::

            sage: T.<a,b> = PowerSeriesRing(ZZ,2); T
            Multivariate Power Series Ring in a, b over Integer Ring
            sage: T.O(10)
            0 + O(a, b)^10
            sage: T.bigoh(10)
            0 + O(a, b)^10
        """
        return self.bigoh(prec)

    def _send_to_bg(self, f):
        """
        Send an element of the foreground polynomial ring to the background
        power series ring.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'f')
            sage: f = M._poly_ring().gens()
            sage: bg = M._send_to_bg((f[0] + f[2] + 2)**2); bg
            4 + (4*f0 + 4*f2)*Tbg + (f0^2 + 2*f0*f2 + f2^2)*Tbg^2

            sage: M._send_to_bg(bg)
            Traceback (most recent call last):
            ...
            TypeError: Cannot coerce input to polynomial ring.
        """
        try:
            f = self._poly_ring(f)
        except TypeError:
            raise TypeError("Cannot coerce input to polynomial ring.")
        fg_to_bg_dict = dict((v, v * self._bg_ps_ring().gen())
                             for v in self._poly_ring().gens())
        return self._bg_ps_ring(f.subs(fg_to_bg_dict))

    def _send_to_fg(self, f):
        """
        Send an element of the background univariate power series ring to
        the foreground multivariate polynomial ring.

        EXAMPLES::

            sage: M = PowerSeriesRing(QQ,4,'f')
            sage: f = M._poly_ring().gens()
            sage: bg = M._send_to_bg((f[0] + f[2] + 2)**2); bg
            4 + (4*f0 + 4*f2)*Tbg + (f0^2 + 2*f0*f2 + f2^2)*Tbg^2
            sage: bg.parent()
            Power Series Ring in Tbg over Multivariate Polynomial Ring in f0, f1,
            f2, f3 over Rational Field
            sage: fg = M._send_to_fg(bg); fg
            4 + 4*f0 + 4*f2 + f0^2 + 2*f0*f2 + f2^2
            sage: fg.parent()
            Multivariate Polynomial Ring in f0, f1, f2, f3 over Rational Field
            sage: fg = M._send_to_fg(bg.add_bigoh(3)); fg
            4 + 4*f0 + 4*f2 + f0^2 + 2*f0*f2 + f2^2
            sage: fg = M._send_to_fg(bg.add_bigoh(2)); fg
            4 + 4*f0 + 4*f2
        """
        return self._poly_ring(f.polynomial().subs({self._bg_indeterminate:
                                                    1}))
示例#48
0
class BianchiDistributions(Module, UniqueRepresentation):
    r"""
    This class represents the overconvergent approximation modules used to
    describe p-adic overconvergent Bianchi modular symbols.

    INPUT:

     - ``p`` - integer
        Prime with which we work

     - ``depth`` - integer (Default: None)
        Precision to which we work; work with moments x^iy^j for
        i,j up to the depth

     - ``act_on_left`` - boolean, (Default: False) 
        Encodes whether Sigma_0(p)^2 is acting on the left or right.

     - ``adjuster`` - Sigma0ActionAdjuster class (Default: _default_adjuster())
        If using a different convention for matrix actions, tell the code where a,b,c,d w
        should be mapped to.


    AUTHORS:

    - Marc Masdeu (2018-08-14)
    - Chris Williams (2018-08-16)
    """
    def __init__(self, p, depth, act_on_left=False, adjuster=None):
        self._dimension = 0  ## Hack!! Dimension was being called before it was intialised
        self._Rmod = ZpCA(p, depth - 1)  ## create Zp
        Module.__init__(self, base=self._Rmod)
        self.Element = BianchiDistributionElement
        self._R = ZZ
        self._p = p
        self._depth = depth
        self._pN = self._p**(depth - 1)
        self._cache_powers = dict()
        self._unset_coercions_used()

        ## Initialise monoid Sigma_0(p) + action; use Pollack-Stevens modular symbol code
        ## our_adjuster() is set above to allow different conventions
        if adjuster is None:
            adjuster = _default_adjuster()

        self._adjuster = adjuster

        ## Power series ring for representing distributions as strings
        self._repr_R = PowerSeriesRing(self._R,
                                       num_gens=2,
                                       default_prec=self._depth,
                                       names='X,Y')

        self._Sigma0Squared = Sigma0Squared(self._p, self._Rmod, adjuster)
        self._act = Sigma0SquaredAction(self._Sigma0Squared,
                                        self,
                                        act_on_left=act_on_left)
        self.register_action(self._act)
        self._populate_coercion_lists_()

        ## Initialise dictionaries of indices to translate between pairs and index for moments
        self._index = dict()
        self._ij = []
        m = 0

        ## Populate dictionary/array giving index of the basis element corr. to tuple (i,j), 0 <= i,j <= depth = n
        ## These things are ordered by degree of y, then degree of x: [1, x, x^2, ..., y, xy, ... ]
        for j in range(depth):
            for i in range(depth):
                self._ij.append((i, j))
                self._index[(i, j)] = m
                m += 1

        self._dimension = m  ## Number of moments we store

        ## Power series ring Zp[[x,y]]. We have to work with monomials up to x^depth * y^depth, so need prec = 2*depth
        self._PowerSeries_x = PowerSeriesRing(self._Rmod,
                                              default_prec=self._depth,
                                              names='x')
        self._PowerSeries_x_ZZ = PowerSeriesRing(ZZ,
                                                 default_prec=self._depth,
                                                 names='x')
        self._PowerSeries = PowerSeriesRing(self._PowerSeries_x,
                                            default_prec=self._depth,
                                            names='y')
        self._PowerSeries_ZZ = PowerSeriesRing(self._PowerSeries_x_ZZ,
                                               default_prec=self._depth,
                                               names='y')

    def index(self, ij):
        r"""
        Function to return index of a tuple (i,j).

        Input:
           - ij (tuple) : pair (i,j)
        Returns:
            Place in ordered basis corresponding to x^iy^j.
        """
        return self._index[tuple(ij)]

    def ij_from_pos(self, n):
        r"""
        From position in the ordered basis, returns corr. tuple (n,i)

        Input:
                - n (int) : position in basis.
        Returns:
                pair (i,j) s.t. the nth basis vector is x^iy^j
        """
        return self._ij[n]

    def monomial_from_index(self, n, R=None):
        """
        Takes index and returns the corresponding monomial in the basis
        """
        X, Y = self._repr_R.gens()
        if isinstance(n, tuple):
            (i, j) = n
        else:
            i, j = self._ij[n]
        return X**i * Y**j

    def basis_vector(self, ij):
        """
        Returns the (i,j)th basis vector (in the dual basis), which takes
        the monomial x^iy^j to 1 and every other monomial to 0.

        EXAMPLES::

            sage: from darmonpoints.ocbianchi import BianchiDistributions
            sage: D = BianchiDistributions(11,4)
            sage: D.basis_vector((2,3))
            X^2*Y^3
            sage: D.basis_vector(5)
            X*Y
        """
        moments = vector(ZZ, [0 for i in range(self._dimension)])
        if isinstance(ij, tuple):
            index = self.index(ij)
            moments[index] = 1
        else:
            moments[ij] = 1
        return self(moments)

    def analytic_functions(self):
        r"""
        Returns underlying power series of rigid analytic functions, that is,
        the space on which a distribution should take values.
        """
        return self._PowerSeries

    def analytic_vars(self):
        r"""
        Returns x,y, the variables of the underlying ring of analytic functions.
        """
        x = self.analytic_functions()(self._PowerSeries_x.gen())
        y = self.analytic_functions().gen()
        return x, y

    def Sigma0Squared(self):
        r"""
        Returns underlying monoid Sigma_0(p)^2.
        """
        return self._Sigma0Squared

    def Sigma0(self):
        r"""
        Returns underlying monoid Sigma_0(p)^2.
        """
        return self._Sigma0Squared

    def approx_module(self, M=None):
        if M is None:
            M = self.dimension()
        return MatrixSpace(self._R, M, 1)

    def clear_cache(self):
        del self._cache_powers
        self._cache_powers = dict()

    def is_overconvergent(self):
        return True

    def _an_element_(self):
        r"""
        """
        return BianchiDistributionElement(self,
                                          Matrix(self._R, self._dimension, 1,
                                                 range(1,
                                                       self._dimension + 1)),
                                          check=False)

    def _coerce_map_from_(self, S):
        # Nothing coherces here, except BianchiDistributionElement
        return False

    def _element_constructor_(self, x, check=True, normalize=False):
        #Code how to coherce x into the space
        #Admissible values of x?
        return BianchiDistributionElement(self, x)

    def acting_matrix(self, g, M):
        G = g.parent()
        qrep = G.quaternion_to_matrix(g)
        qrep_bar = qrep.apply_map(lambda x: x.trace() - x)
        first, second = qrep.apply_map(G._F_to_local), qrep_bar.apply_map(
            G._F_to_local)
        return self._get_powers(self.Sigma0Squared()(first, second))

    def _get_powers(self, g, emb=None):
        r"""
        Auxiliary function to compute the Sigma_0(p)^2 action on moments.

        The action sends a monomial x^i to (gx)^i, where gx = (b+dx)/(a+cx). The 
        action on two-variable functions is simply the product of two copies of the
        one variable action.

        Input:
                - g : Sigma0SquaredElement object (in the relevant Sigma_0(p)^2 group)

        Returns:
                matrix of (g_x,g_y) acting on distributions in the basis given by monomials

        EXAMPLES::

            sage: from darmonpoints.ocbianchi import BianchiDistributions
            sage: D = BianchiDistributions(11,2)
            sage: h = D.Sigma0Squared()([1,1,0,1],[1,1,0,1])
            sage: D._get_powers(h)
            [1 0 0 0]
            [1 1 0 0]
            [1 0 1 0]
            [1 1 1 1]
            sage: h = D.Sigma0Squared()([2,3,11,1],[12,1,22,1])
            sage: D._get_powers(h)
            [1 0 0 0]
            [7 6 0 0]
            [1 0 1 0]
            [7 6 7 6]
        """
        ## We want to ultimately compute actions on distributions. The matrix describing the (left)
        ## action of g on distributions is the transpose of the action of adjoint(g) acting on the (left)
        ## of analytic functions, so we start by taking adjoints. Then put the matrix entries into lists

        ## NOTE: First apply the adjuster defined above; permutes a,b,c,d to allow for different conventions.
        abcdx = g.first_element()
        abcdy = g.second_element()

        ## Adjust for action: change of convention is encoded in our_adjuster class above
        adjuster = self._adjuster
        abcdx = adjuster(abcdx.matrix())
        abcdy = adjuster(abcdy.matrix())

        ## We want better keys; keys in Zp are not great. Store them instead in ZZ
        abcdxZZ = tuple(ZZ(t) for t in abcdx)
        abcdyZZ = tuple(ZZ(t) for t in abcdy)

        ## check to see if the action of (g,h) has already been computed and cached
        try:
            return self._cache_powers[(abcdxZZ, abcdyZZ)]
        except KeyError:
            pass

        ## Sanity check output
        verbose('The element [{},{}] has not been stored. Computing:'.format(
            abcdxZZ, abcdyZZ),
                level=2)

        R = self._PowerSeries  ## Zp[[x,y]
        y = R.gen()
        x = R.base_ring().gen()

        ## get values of a,b,c,d for x and y
        if emb is None:
            a, b, c, d = abcdx
            A, B, C, D = abcdy
        else:
            gg = emb(abcdy)
            a, b, c, d = gg[0].list()
            A, B, C, D = gg[1].list()

        ## Initialise terms
        num_x = b + d * x  ## b + az + O(11^depth)R
        denom_x = a + c * x  ## d + cz + O(11^depth)R
        num_y = B + D * x
        denom_y = A + C * x

        ## Ratios
        r = R.base_ring()(num_x / denom_x)  ## after action on x
        s = num_y / denom_y  ## after action on y

        r = r.change_ring(ZZ)
        s = s.change_ring(ZZ)

        RZ = self._PowerSeries_ZZ
        phi = s.parent().hom([RZ.gen()])
        ## Constant term
        const = r.parent()(1)
        spows = [const]
        for n in range(self._depth):
            spows.append(s * spows[-1])

        acted_monomials = {}
        for j in range(self._depth):
            acted_monomials[(0, j)] = phi(spows[j])
        rpow = 1
        for i in range(1, self._depth):
            rpow *= r
            rpow.add_bigoh(self._depth)
            for j in range(self._depth):
                acted_monomials[(i, j)] = rpow * phi(spows[j])

        matrix_rows = []
        for n in range(self._dimension):
            f = acted_monomials[tuple(self.ij_from_pos(n))]
            new_row = []
            for polx in f.padded_list(self._depth):
                new_row.extend(polx.padded_list(self._depth))
            matrix_rows.append(new_row)

        ## Build matrix . DO NOT TAKE TRANSPOSE, (built this in as a consequence of implementation)
        matrix_action = Matrix(ZZ, matrix_rows)
        #acted_monomials_list = Matrix(R.base_ring(),self._depth,self._depth,acted_monomials_list)#.apply_map(ZZ)
        self._cache_powers[(abcdxZZ, abcdyZZ)] = matrix_action
        return matrix_action

    def _repr_(self):
        r"""
        This returns the representation of self as a string.
        """
        return "Space of %s-adic Bianchi distributions with k=0 action and precision cap %s" % (
            self._p, self._dimension - 1)

    def prime(self):
        r"""
        Returns underlying prime.
        """
        return self._p

    def basis(self):
        r"""
        A basis of the module.

        Returns all monomials in x,y of degree (in each variable) up to the specified depth-1.

        """
        try:
            return self._basis
        except:
            pass
        self._basis = [
            BianchiDistributionElement(self,
                                       Matrix(self._R,
                                              self._dimension,
                                              1, {(jj, 0): 1},
                                              sparse=False),
                                       check=False)
            for jj in range(self._dimension)
        ]
        return self._basis

    def base_ring(self):
        r"""
        This function returns the base ring of the overconvergent element.
        """
        return self._Rmod

    def depth(self):
        r"""
        Returns the depth of the module. If the depth is d, then a basis for the approximation
        modules is x^iy^j with i,j in {0,...,d-1}.
        """
        return self._depth

    def dimension(self):
        r"""
        Returns the dimension (rank) of the module.
        """
        return self._dimension

    def precision_cap(self):
        r"""
        Returns the depth of the module. If the depth is d, then a basis for the approximation
        modules is x^iy^j with i,j in {0,...,d-1}.
        """
        return self._depth

    def is_exact(self):
        r"""
        All distributions are finite approximations. They are only exact as elements of
        D/Fil^{d,d}D, where d is the depth.
        """
        return False