Beispiel #1
0
def __dimension_Sp6Z(wt):
    """
    Return the dimensions of subspaces of Siegel modular forms on $Sp(6,Z)$.

    OUTPUT
    ("Total", "Miyawaki-Type-1", "Miyawaki-Type-2 (conjectured)", "Interesting")
    Remember, Miywaki type 2 is ONLY CONJECTURED!!
    """
    if not is_even(wt):
        return (0, 0, 0, 0)
    R = PowerSeriesRing(IntegerRing(), default_prec=wt + 1, names=('x',))
    (x,) = R._first_ngens(1)
    S = PowerSeriesRing(IntegerRing(), default_prec=max(2 * wt - 1,1), names=('y',))
    (y,) = S._first_ngens(1)
    H_all = 1 / ((1 - x ** 4) * (1 - x ** 12) ** 2 * (1 - x ** 14) * (1 - x ** 18) *
                (1 - x ** 20) * (1 - x ** 30)) * (
                    1 + x ** 6 + x ** 10 + x ** 12 + 3 * x ** 16 + 2 * x ** 18 + 2 * x ** 20 +
                    5 * x ** 22 + 4 * x ** 24 + 5 * x ** 26 + 7 * x ** 28 + 6 * x ** 30 + 9 * x ** 32 +
                    10 * x ** 34 + 10 * x ** 36 + 12 * x ** 38 + 14 * x ** 40 + 15 * x ** 42 + 16 * x ** 44 +
                    18 * x ** 46 + 18 * x ** 48 + 19 * x ** 50 + 21 * x ** 52 + 19 * x ** 54 + 21 * x ** 56 +
                    21 * x ** 58 + 19 * x ** 60 + 21 * x ** 62 + 19 * x ** 64 + 18 * x ** 66 + 18 * x ** 68 +
                    16 * x ** 70 + 15 * x ** 72 + 14 * x ** 74 + 12 * x ** 76 + 10 * x ** 78 + 10 * x ** 80 +
                    9 * x ** 82 + 6 * x ** 84 + 7 * x ** 86 + 5 * x ** 88 + 4 * x ** 90 + 5 * x ** 92 +
                    2 * x ** 94 + 2 * x ** 96 + 3 * x ** 98 + x ** 102 + x ** 104 + x ** 108 + x ** 114)

    H_noncusp = 1 / (1 - x ** 4) / (1 - x ** 6) / (1 - x ** 10) / (1 - x ** 12)
    H_E = y ** 12 / (1 - y ** 4) / (1 - y ** 6)
    H_Miyawaki1 = H_E[wt] * H_E[2 * wt - 4]
    H_Miyawaki2 = H_E[wt - 2] * H_E[2 * wt - 2]
    a, b, c, d = H_all[wt], H_noncusp[wt], H_Miyawaki1, H_Miyawaki2
    return (a, c, d, a - b - c - d)
    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 _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 
Beispiel #4
0
def _dimension_Gamma0_4_half(k):
    """
    Return the dimensions of subspaces of Siegel modular forms$Gamma0(4)$
    of half integral weight  k - 1/2.

    INPUT
        The realweight is k-1/2

    OUTPUT
        ('Total', 'Non cusp', 'Cusp')

    REMARK
        Note that formula from Hayashida's and Ibukiyama's paper has formula
        that coefficient of x^w is for weight (w+1/2). So here w=k-1.
    """
    R = PowerSeriesRing(IntegerRing(), default_prec=k, names=("x",))
    (x,) = R._first_ngens(1)
    H_all = 1 / (1 - x) / (1 - x ** 2) ** 2 / (1 - x ** 3)
    H_cusp = (
        (2 * x ** 5 + x ** 7 + x ** 9 - 2 * x ** 11 + 4 * x ** 6 - x ** 8 + x ** 10 - 3 * x ** 12 + x ** 14)
        / (1 - x ** 2) ** 2
        / (1 - x ** 6)
    )
    a, c = H_all[k - 1], H_cusp[k - 1]
    return (a, a - c, c)
Beispiel #5
0
def test_tensprod_121_chi():
    C121=[1,2,-1,2,1,-2,2,0,-2,2,0,-2,-4,4,-1,-4,2,-4,0,2,-2,0,\
    -1,0,-4,-8,5,4,0,-2,7,-8,0,4,2,-4,3,0,4,0,8,-4,6,0,-2,-2,\
    8,4,-3,-8,-2,-8,-6,10,0,0,0,0,5,-2,-12,14,-4,-8,-4,0,-7,4,\
    1,4,-3,0,-4,6,4,0,0,8,10,-4,1,16,6,-4,2,12,0,0,15,-4,-8,\
    -2,-7,16,0,8,-7,-6,0,-8,-2,-4,-16,0,-2,-12,-18,10,-10,0,-3,\
    -8,9,0,-1,0,8,10,4,0,0,-24,-8,14,-9,-8,-8,0,-6,-8,18,0,0,\
    -14,5,0,-7,2,-10,4,-8,-6,0,8,0,-8,3,6,10,8,-2,0,-4,0,7,8,\
    -7,20,6,-8,-2,2,4,16,0,12,12,0,3,4,0,12,6,0,-8,0,-5,30,\
    -15,-4,7,-16,12,0,3,-14,0,16,10,0,17,8,-4,-14,4,-6,2,0,0,0]
    chi=[1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,\
    1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,1,\
    -1,1,1,1,-1,-1,-1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1,\
    1,1,1,-1,-1,-1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1,1,\
    1,1,-1,-1,-1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1,1,1,\
    1,-1,-1,-1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1,1,1,1,\
    -1,-1,-1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1,1,1,1,-1,\
    -1,-1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1,1,1,1,-1,-1,\
    -1,1,-1,0,1,-1,1,1,1,-1,-1,-1,1,-1,0,1,-1]
    ANS=[1,-2,-1,2,1,2,-2,0,-2,-2,1,-2,4,4,-1,-4,-2,4,0,2,2,-2,\
    -1,0,-4,-8,5,-4,0,2,7,8,-1,4,-2,-4,3,0,-4,0,-8,-4,-6,2,-2,\
    2,8,4,-3,8,2,8,-6,-10,1,0,0,0,5,-2,12,-14,4,-8,4,2,-7,-4,\
    1,4,-3,0,4,-6,4,0,-2,8,-10,-4,1,16,-6,4,-2,12,0,0,15,4,-8,\
    -2,-7,-16,0,-8,-7,6,-2,-8,2,-4,-16,0,2,12,18,10,10,-2,-3,8,\
    9,0,-1,0,-8,-10,4,0,1,-24,8,14,-9,-8,8,0,6,-8,-18,-2,0,14,\
    5,0,-7,-2,10,-4,-8,6,4,8,0,-8,3,6,-10,-8,2,0,4,4,7,-8,-7,\
    20,6,8,2,-2,4,-16,-1,12,-12,0,3,4,0,-12,-6,0,8,-4,-5,-30,\
    -15,-4,7,16,-12,0,3,14,-2,16,-10,0,17,8,4,14,-4,-6,-2,4,0,0]
    R = PowerSeriesRing(ZZ, "T")
    T = R.gens()[0]
    assert ANS==tensor_get_an_deg1(C121,chi,[[11,1-T]])
    assert ANS==tensor_get_an(C121,chi,2,1,[[11,1-T,1-T]])
    assert get_euler_factor(ANS,2)==(1+2*T+2*T**2+O(T**8))
    assert get_euler_factor(ANS,3)==(1+T+3*T**2+O(T**5))
    assert get_euler_factor(ANS,5)==(1-T+5*T**2+O(T**4))
Beispiel #6
0
    def series(self, n, prec):
        r"""
        Returns the `n`-th approximation to the `p`-adic `L`-series
        associated to self, as a power series in `T` (corresponding to
        `\gamma-1` with `\gamma= 1 + p` as a generator of `1+p\ZZ_p`).

        EXAMPLES::
        
            sage: from sage.modular.pollack_stevens.space import ps_modsym_from_elliptic_curve
            sage: E = EllipticCurve('57a')
            sage: p = 5
            sage: prec = 4
            sage: phi = ps_modsym_from_elliptic_curve(E)
            sage: phi_stabilized = phi.p_stabilize(p,M = prec+3)
            sage: Phi = phi_stabilized.lift(p,prec,None,algorithm='stevens',eigensymbol=True)
            sage: L = pAdicLseries(Phi)
            sage: L.series(3,4)
            O(5^3) + (3*5 + 5^2 + O(5^3))*T + (5 + O(5^2))*T^2
            
            sage: L1 = E.padic_lseries(5)
            sage: L1.series(4)
            O(5^6) + (3*5 + 5^2 + O(5^3))*T + (5 + 4*5^2 + O(5^3))*T^2 + (4*5^2 + O(5^3))*T^3 + (2*5 + 4*5^2 + O(5^3))*T^4 + O(T^5)
        
        """
        p = self.prime()
        M = self.symb().precision_absolute()
        K = pAdicField(p, M)
        R = PowerSeriesRing(K, names = 'T')
        T = R.gens()[0]
        R.set_default_prec(prec)
        return sum(self[i] * T**i for i in range(n))
    def hecke_polynomial_in_T_variable(self, q, var='x', basis=None, verbose=True):
        r"""
        The function hecke_polynomial returns a polynomial whose coefficients
        are power series in the variable `w`, which represents an element in the
        disc of radius `1/p`. This function instead uses the more standard
        variable `T`, which represents an element in the disc of radius `1`.

        EXAMPLES::

            sage: MM = FamiliesOfOMS(11, 0, sign=-1, p=3, prec_cap=[4, 4], base_coeffs=ZpCA(3, 8))
            sage: HP = MM.hecke_polynomial_in_T_variable(3, verbose=False); HP
            (1 + O(3^8))*x^2 + (2 + 2*3 + 3^2 + O(3^4) + (2 + 2*3 + O(3^3))*T + O(3^2)*T^2 + (1 + O(3))*T^3 + O(T^4))*x + 1 + 2*3 + 3^2 + O(3^4) + O(3^3)*T + (2 + 3 + O(3^2))*T^2 + (1 + O(3))*T^3 + O(T^4)
        """
        HPw = self.hecke_polynomial(q, var, basis, verbose)
        from sage.rings.power_series_ring import PowerSeriesRing
        from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
        v_prec = self.precision_cap()[1]
        RT = PowerSeriesRing(self.base_ring().base_ring(), 'T', default_prec=v_prec)
        R = PolynomialRing(RT, var)
        poly_coeffs = []
        for c in HPw.padded_list():
            prec = c.prec()
            cL = c.padded_list()
            length = len(cL)
            cL = [cL[i] >> i for i in range(length)]
            j = 0
            while j < length:
                if cL[j].precision_absolute() <= 0:
                    break
                j += 1
            poly_coeffs.append(RT(cL, j))
        poly_coeffs[-1] = RT.one()
        return R(poly_coeffs)
Beispiel #8
0
def test_tensprod_11a_17a():
    C11=[1,-2,-1,2,1,2,-2,0,-2,-2,1,-2,4,4,-1,-4,-2,4,0,2,2,-2,\
    -1,0,-4,-8,5,-4,0,2,7,8,-1,4,-2,-4,3,0,-4,0,-8,-4,-6,2,-2,\
    2,8,4,-3,8,2,8,-6,-10,1,0,0,0,5,-2,12,-14,4,-8,4,2,-7,-4,\
    1,4,-3,0,4,-6,4,0,-2,8,-10,-4,1,16,-6,4,-2,12,0,0,15,4,-8,\
    -2,-7,-16,0,-8,-7,6,-2,-8,2,-4,-16,0,2,12,18,10,10,-2,-3,8,\
    9,0,-1,0,-8,-10,4,0,1,-24,8,14,-9,-8,8,0,6,-8,-18,-2,0,14,\
    5,0,-7,-2,10,-4,-8,6,4,8,0,-8,3,6,-10,-8,2,0,4,4,7,-8,-7,\
    20,6,8,2,-2,4,-16,-1,12,-12,0,3,4,0,-12,-6,0,8,-4,-5,-30,\
    -15,-4,7,16,-12,0,3,14,-2,16,-10,0,17,8,4,14,-4,-6,-2,4,0,0]
    C17=[1,-1,0,-1,-2,0,4,3,-3,2,0,0,-2,-4,0,-1,1,3,-4,2,0,0,4,\
    0,-1,2,0,-4,6,0,4,-5,0,-1,-8,3,-2,4,0,-6,-6,0,4,0,6,-4,0,\
    0,9,1,0,2,6,0,0,12,0,-6,-12,0,-10,-4,-12,7,4,0,4,-1,0,8,\
    -4,-9,-6,2,0,4,0,0,12,2,9,6,-4,0,-2,-4,0,0,10,-6,-8,-4,0,\
    0,8,0,2,-9,0,1,-10,0,8,-6,0,-6,8,0,6,0,0,-4,-14,0,-8,-6,\
    6,12,4,0,-11,10,0,-4,12,12,8,3,0,-4,16,0,-16,-4,0,3,-6,0,\
    -8,8,0,4,0,3,-12,6,0,2,-10,0,-16,-12,-3,0,-8,0,-2,-12,0,10,\
    16,-9,24,6,0,4,-4,0,-9,2,12,-4,22,0,-4,0,0,-10,12,-6,-2,8,\
    0,12,4,0,0,0,0,-8,-16,0,2,-2,0,-9,-18,0,-20,-3]
    ANS=[1,2,0,2,-2,0,-8,8,15,-4,0,0,-8,-16,0,12,-2,30,0,-4,0,0,\
    -4,0,29,-16,0,-16,0,0,28,-8,0,-4,16,30,-6,0,0,-16,48,0,-24,\
    0,-30,-8,0,0,22,58,0,-16,-36,0,0,-64,0,0,-60,0,-120,56,-120,\
    -8,16,0,-28,-4,0,32,12,120,-24,-12,0,0,0,0,-120,-24,144,96,\
    24,0,4,-48,0,0,150,-60,64,-8,0,0,0,0,-14,44,0,58,-20,0,-128,\
    -64,0,-72,144,0,60,0,0,-96,-126,0,8,0,-120,-120,16,0,-11,-240,\
    0,56,-158,-240,64,-32,0,32,-288,0,0,-56,0,-16,42,0,-80,32,0,\
    24,0,180,0,-48,0,-12,100,0,-32,0,-30,0,-56,0,14,-240,0,16,32,\
    288,96,96,0,48,48,0,142,8,0,-48,-132,0,-232,0,0,300,-180,-60,\
    -14,128,0,-32,12,0,0,0,0,0,-272,0,8,-28,0,44,36,0,0,232]
    R = PowerSeriesRing(ZZ, "T")
    T = R.gens()[0]
    B11=[11,1-T,1+11*T**2]
    B17=[17,1+2*T+17*T**2,1-T]
    assert ANS==tensor_get_an_no_deg1(C11,C17,2,2,[B11,B17])
Beispiel #9
0
def _dimension_Sp4Z( wt_range):
    """
    Return the dimensions of subspaces of Siegel modular forms on $Sp(4,Z)$.

    OUTPUT
        ("Total", "Eisenstein", "Klingen", "Maass", "Interesting")
    """
    headers = ['Total', 'Eisenstein', 'Klingen', 'Maass', 'Interesting']

    R = PowerSeriesRing( IntegerRing(), default_prec = wt_range[-1] + 1, names = ('x',))
    (x,) = R._first_ngens(1)
    H_all = 1 / (1 - x ** 4) / (1 - x ** 6) / (1 - x ** 10) / (1 - x ** 12)
    H_Kl = x ** 12 / (1 - x ** 4) / (1 - x ** 6)
    H_MS = (x ** 10 + x ** 12) / (1 - x ** 4) / (1 - x ** 6)

    dct = dict( (k,
                 { 'Total': H_all[k], 
                   'Eisenstein': 1 if k >= 4 else 0,
                   'Klingen': H_Kl[k],
                   'Maass': H_MS[k],
                   'Interesting': H_all[k]-(1 if k >= 4 else 0)-H_Kl[k]-H_MS[k]
                   }
                 if is_even(k) else
                 { 'Total': H_all[k-35], 
                   'Eisenstein': 0,
                   'Klingen': 0,
                   'Maass': 0,
                   'Interesting': H_all[k-35]
                   }
                 ) for k in wt_range)

    return headers, dct
Beispiel #10
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
Beispiel #11
0
    def series(self, prec=5):
        r"""
        Return the ``prec``-th approximation to the `p`-adic `L`-series
        associated to self, as a power series in `T` (corresponding to
        `\gamma-1` with `\gamma` the chosen generator of `1+p\ZZ_p`).

        INPUT:

        - ``prec`` -- (default 5) is the precision of the power series

        EXAMPLES::

            sage: E = EllipticCurve('14a2')
            sage: p = 3
            sage: prec = 6
            sage: L = E.padic_lseries(p,implementation="pollackstevens",precision=prec) # long time
            sage: L.series(4)          # long time
            2*3 + 3^4 + 3^5 + O(3^6) + (2*3 + 3^2 + O(3^4))*T + (2*3 + O(3^2))*T^2 + (3 + O(3^2))*T^3 + O(T^4)

            sage: E = EllipticCurve("15a3")
            sage: L = E.padic_lseries(5,implementation="pollackstevens",precision=15)  # long time
            sage: L.series(3)            # long time
            O(5^15) + (2 + 4*5^2 + 3*5^3 + 5^5 + 2*5^6 + 3*5^7 + 3*5^8 + 2*5^9 + 2*5^10 + 3*5^11 + 5^12 + O(5^13))*T + (4*5 + 4*5^3 + 3*5^4 + 4*5^5 + 3*5^6 + 2*5^7 + 5^8 + 4*5^9 + 3*5^10 + O(5^11))*T^2 + O(T^3)

            sage: E = EllipticCurve("79a1")
            sage: L = E.padic_lseries(2,implementation="pollackstevens",precision=10) # not tested
            sage: L.series(4)  # not tested
            O(2^9) + (2^3 + O(2^4))*T + O(2^0)*T^2 + (O(2^-3))*T^3 + O(T^4)
        """
        p = self.prime()
        M = self.symbol().precision_relative()
        K = pAdicField(p, M)
        R = PowerSeriesRing(K, names="T")
        T = R.gens()[0]
        return R([self[i] for i in range(prec)]).add_bigoh(prec)
Beispiel #12
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()))
    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 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))
Beispiel #16
0
def _dimension_Gamma0_3(wt):
    """
    Return the dimensions of subspaces of Siegel modular forms on $Gamma0(3)$.

    OUTPUT
        ( "Total")

    REMARK
        Only total dimension implemented.
    """
    R = PowerSeriesRing( IntegerRing(), default_prec = wt + 1, names=('x',))
    (x,) = R._first_ngens(1)
    H_all = (1 + 2 * x ** 4 + x ** 6 + x ** 15 * (1 + 2 * x ** 2 + x ** 6)) / (1 - x ** 2) / (1 - x ** 4) / (1 - x ** 6) ** 2
    return ( H_all[wt],)
Beispiel #17
0
def list_to_euler_factor(L,prec):
    """
    takes a list [a_p, a_p^2,...
    and returns the euler factor
    """
    if isinstance(L[0], int):
        K = QQ
    else:
        K = L[0].parent()
    R = PowerSeriesRing(K, "T")
    T = R.gens()[0]
    f =  1/ R([1]+L)
    f = f.add_bigoh(prec+1)
    return f
Beispiel #18
0
def _dimension_Gamma0_4(wt):
    """
    Return the dimensions of subspaces of Siegel modular forms on $Gamma0(4)$.

    OUTPUT
        ( "Total",)

    REMARK
        Not completely implemented
    """
    R = PowerSeriesRing(IntegerRing(), default_prec=wt + 1, names=("x",))
    (x,) = R._first_ngens(1)
    H_all = (1 + x ** 4)(1 + x ** 11) / (1 - x ** 2) ** 3 / (1 - x ** 6)
    return (H_all[wt],)
Beispiel #19
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 _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 _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) 
Beispiel #22
0
def _dimension_Gamma0_4_psi_4(wt):
    """
    Return the dimensions of subspaces of Siegel modular forms
    on $Gamma_0(4)$
    with character $\psi_4$.

    OUTPUT
        ( "Total")

    REMARK
        The formula for odd weights is unknown or not obvious from the paper.
    """
    R = PowerSeriesRing(IntegerRing(), default_prec=wt + 1, names=("x",))
    (x,) = R._first_ngens(1)
    H_all_even = (x ** 12 + x ** 14) / (1 - x ** 2) ** 3 / (1 - x ** 6)
    if is_even(wt):
        return (H_all_even[wt],)
    else:
        raise NotImplementedError("Dimensions of $M_{k}(\Gamma_0(4), \psi_4)$ for odd $k$ not implemented")
Beispiel #23
0
def _dimension_Gamma0_3_psi_3(wt):
    """
    Return the dimensions of the space of Siegel modular forms
    on $Gamma_0(3)$ with character $\psi_3$.

    OUTPUT
        ( "Total")

    REMARK
        Not completely implemented
    """
    R = PowerSeriesRing(IntegerRing(), default_prec=wt + 1, names=("x",))
    (x,) = R._first_ngens(1)
    B = 1 / (1 - x ** 1) / (1 - x ** 3) / (1 - x ** 4) / (1 - x ** 3)
    H_all_odd = B
    H_all_even = B * x ** 14
    if is_even(wt):
        return (H_all_even[wt],)
    else:
        return (H_all_odd[wt],)
Beispiel #24
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
    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) )
    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))
Beispiel #27
0
    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
        """
        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
        ParentWithGens.__init__(self, base_ring, name_list)
        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()

        ## use the following in PowerSeriesRing_generic.__call__
        self._PowerSeriesRing_generic__power_series_class = MPowerSeries

        self._is_sparse = sparse
        self._params = (base_ring, num_gens, name_list, order, default_prec,
                        sparse)
        self._populate_coercion_lists_()
Beispiel #28
0
    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
        """
        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
        ParentWithGens.__init__(self, base_ring, name_list)
        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()

        ## use the following in PowerSeriesRing_generic.__call__
        self._PowerSeriesRing_generic__power_series_class = MPowerSeries

        self._is_sparse = sparse
        self._params = (base_ring, num_gens, name_list,
                         order, default_prec, sparse)
        self._populate_coercion_lists_()
Beispiel #29
0
        def q_dimension(self, q=None, prec=None, use_product=False):
            r"""
            Return the `q`-dimension of ``self``.

            Let `B(\lambda)` denote a highest weight crystal. Recall that
            the degree of the `\mu`-weight space of `B(\lambda)` (under
            the principal gradation) is equal to
            `\langle \rho^{\vee}, \lambda - \mu \rangle` where
            `\langle \rho^{\vee}, \alpha_i \rangle = 1` for all `i \in I`
            (in particular, take `\rho^{\vee} = \sum_{i \in I} h_i`).

            The `q`-dimension of a highest weight crystal `B(\lambda)` is
            defined as

            .. MATH::

                \dim_q B(\lambda) := \sum_{j \geq 0} \dim(B_j) q^j,

            where `B_j` denotes the degree `j` portion of `B(\lambda)`. This
            can be expressed as the product

            .. MATH::

                \dim_q B(\lambda) = \prod_{\alpha^{\vee} \in \Delta_+^{\vee}}
                \left( \frac{1 - q^{\langle \lambda + \rho, \alpha^{\vee}
                \rangle}}{1 - q^{\langle \rho, \alpha^{\vee} \rangle}}
                \right)^{\mathrm{mult}\, \alpha},

            where `\Delta_+^{\vee}` denotes the set of positive coroots.
            Taking the limit as `q \to 1` gives the dimension of `B(\lambda)`.
            For more information, see [Kac]_ Section 10.10.

            INPUT:

            - ``q`` -- the (generic) parameter `q`

            - ``prec`` -- (default: ``None``) The precision of the power
              series ring to use if the crystal is not known to be finite
              (i.e. the number of terms returned).
              If ``None``, then the result is returned as a lazy power series.

            - ``use_product`` -- (default: ``False``) if we have a finite
              crystal and ``True``, use the product formula

            EXAMPLES::

                sage: C = crystals.Tableaux(['A',2], shape=[2,1])
                sage: qdim = C.q_dimension(); qdim
                q^4 + 2*q^3 + 2*q^2 + 2*q + 1
                sage: qdim(1)
                8
                sage: len(C) == qdim(1)
                True
                sage: C.q_dimension(use_product=True) == qdim
                True
                sage: C.q_dimension(prec=20)
                q^4 + 2*q^3 + 2*q^2 + 2*q + 1
                sage: C.q_dimension(prec=2)
                2*q + 1

                sage: R.<t> = QQ[]
                sage: C.q_dimension(q=t^2)
                t^8 + 2*t^6 + 2*t^4 + 2*t^2 + 1

                sage: C = crystals.Tableaux(['A',2], shape=[5,2])
                sage: C.q_dimension()
                q^10 + 2*q^9 + 4*q^8 + 5*q^7 + 6*q^6 + 6*q^5
                 + 6*q^4 + 5*q^3 + 4*q^2 + 2*q + 1

                sage: C = crystals.Tableaux(['B',2], shape=[2,1])
                sage: qdim = C.q_dimension(); qdim
                q^10 + 2*q^9 + 3*q^8 + 4*q^7 + 5*q^6 + 5*q^5
                 + 5*q^4 + 4*q^3 + 3*q^2 + 2*q + 1
                sage: qdim == C.q_dimension(use_product=True)
                True

                sage: C = crystals.Tableaux(['D',4], shape=[2,1])
                sage: C.q_dimension()
                q^16 + 2*q^15 + 4*q^14 + 7*q^13 + 10*q^12 + 13*q^11
                 + 16*q^10 + 18*q^9 + 18*q^8 + 18*q^7 + 16*q^6 + 13*q^5
                 + 10*q^4 + 7*q^3 + 4*q^2 + 2*q + 1

            We check with a finite tensor product::

                sage: TP = crystals.TensorProduct(C, C)
                sage: TP.cardinality()
                25600
                sage: qdim = TP.q_dimension(use_product=True); qdim # long time
                q^32 + 2*q^31 + 8*q^30 + 15*q^29 + 34*q^28 + 63*q^27 + 110*q^26
                 + 175*q^25 + 276*q^24 + 389*q^23 + 550*q^22 + 725*q^21
                 + 930*q^20 + 1131*q^19 + 1362*q^18 + 1548*q^17 + 1736*q^16
                 + 1858*q^15 + 1947*q^14 + 1944*q^13 + 1918*q^12 + 1777*q^11
                 + 1628*q^10 + 1407*q^9 + 1186*q^8 + 928*q^7 + 720*q^6
                 + 498*q^5 + 342*q^4 + 201*q^3 + 117*q^2 + 48*q + 26
                sage: qdim(1) # long time
                25600
                sage: TP.q_dimension() == qdim # long time
                True

            The `q`-dimensions of infinite crystals are returned
            as formal power series::

                sage: C = crystals.LSPaths(['A',2,1], [1,0,0])
                sage: C.q_dimension(prec=5)
                1 + q + 2*q^2 + 2*q^3 + 4*q^4 + O(q^5)
                sage: C.q_dimension(prec=10)
                1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6
                 + 9*q^7 + 13*q^8 + 16*q^9 + O(q^10)
                sage: qdim = C.q_dimension(); qdim
                1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6
                 + 9*q^7 + 13*q^8 + 16*q^9 + 22*q^10 + O(x^11)
                sage: qdim.compute_coefficients(15)
                sage: qdim
                1 + q + 2*q^2 + 2*q^3 + 4*q^4 + 5*q^5 + 7*q^6
                 + 9*q^7 + 13*q^8 + 16*q^9 + 22*q^10 + 27*q^11
                 + 36*q^12 + 44*q^13 + 57*q^14 + 70*q^15 + O(x^16)

            REFERENCES:

            .. [Kac] Victor G. Kac. *Infinite-dimensional Lie Algebras*.
               Third edition. Cambridge University Press, Cambridge, 1990.
            """
            from sage.rings.all import ZZ
            WLR = self.weight_lattice_realization()
            I = self.index_set()
            mg = self.highest_weight_vectors()
            max_deg = float('inf') if prec is None else prec - 1

            def iter_by_deg(gens):
                next = set(gens)
                deg = -1
                while next and deg < max_deg:
                    deg += 1
                    yield len(next)
                    todo = next
                    next = set([])
                    while todo:
                        x = todo.pop()
                        for i in I:
                            y = x.f(i)
                            if y is not None:
                                next.add(y)
                # def iter_by_deg

            from sage.categories.finite_crystals import FiniteCrystals
            if self in FiniteCrystals():
                if q is None:
                    from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
                    q = PolynomialRing(ZZ, 'q').gen(0)

                if use_product:
                    # Since we are in the classical case, all roots occur with multiplicity 1
                    pos_coroots = [
                        x.associated_coroot() for x in WLR.positive_roots()
                    ]
                    rho = WLR.rho()
                    P = q.parent()
                    ret = P.zero()
                    for v in self.highest_weight_vectors():
                        hw = v.weight()
                        ret += P.prod((1 - q**(rho + hw).scalar(ac)) /
                                      (1 - q**rho.scalar(ac))
                                      for ac in pos_coroots)
                    # We do a cast since the result would otherwise live in the fraction field
                    return P(ret)

            elif prec is None:
                # If we're here, we may not be a finite crystal.
                # In fact, we're probably infinite.
                from sage.combinat.species.series import LazyPowerSeriesRing
                if q is None:
                    P = LazyPowerSeriesRing(ZZ, names='q')
                else:
                    P = q.parent()
                if not isinstance(P, LazyPowerSeriesRing):
                    raise TypeError(
                        "the parent of q must be a lazy power series ring")
                ret = P(iter_by_deg(mg))
                ret.compute_coefficients(10)
                return ret

            from sage.rings.power_series_ring import PowerSeriesRing, PowerSeriesRing_generic
            if q is None:
                q = PowerSeriesRing(ZZ, 'q', default_prec=prec).gen(0)
            P = q.parent()
            ret = P.sum(c * q**deg for deg, c in enumerate(iter_by_deg(mg)))
            if ret.degree() == max_deg and isinstance(P,
                                                      PowerSeriesRing_generic):
                ret = P(ret, prec)
            return ret
Beispiel #30
0
    def _wronskian_adjoint(self, weight_parity=0, p=None):
        r"""
        The matrix `W^\# \pmod{p}`, mentioned on page 142 of Nils Skoruppa's thesis.
        This matrix is represented by a list of lists of q-expansions.
        
        The q-expansion is shifted by `-(m + 1) (2*m + 1) / 24` in the case of even weights, and it is
        shifted by `-(m - 1) (2*m - 3) / 24` otherwise. This is compensated by the missing q-powers
        returned by _wronskian_invdeterminant.
        
        INPUT:
        
        - `p` -- A prime or ``None``.
        
        - ``weight_parity`` -- An integer (default: `0`).
        """
        try:
            if weight_parity % 2 == 0:
                wronskian_adjoint = self.__wronskian_adjoint_even
            else:
                wronskian_adjoint = self.__wronskian_adjoint_ood

            if p is None:
                return wronskian_adjoint
            else:
                P = PowerSeriesRing(GF(p), 'q')

                return [map(P, row) for row in wronskian_adjoint]

        except AttributeError:
            qexp_prec = self._qexp_precision()

            if p is None:
                PS = self.integral_power_series_ring()
            else:
                PS = PowerSeriesRing(GF(p), 'q')
            m = self.jacobi_index()

            twom = 2 * m
            frmsq = twom**2

            thetas = dict(
                ((i, j), dict()) for i in xrange(m + 1) for j in xrange(m + 1))

            ## We want to calculate \hat \theta_{j,l} = sum_r (2 m r + j)**2l q**(m r**2 + j r)
            ## in the case of even weight, and \hat \theta_{j,l} = sum_r (2 m r + j)**(2l + 1) q**(m r**2 + j r),
            ## otherwise.
            for r in xrange(isqrt((qexp_prec - 1 + m) // m) + 2):
                for j in (xrange(m + 1) if weight_parity %
                          2 == 0 else range(1, m)):
                    fact_p = (twom * r + j)**2
                    fact_m = (twom * r - j)**2
                    if weight_parity % 2 == 0:
                        coeff_p = 2
                        coeff_m = 2
                    else:
                        coeff_p = 2 * (twom * r + j)
                        coeff_m = -2 * (twom * r - j)

                    for l in (xrange(m + 1) if weight_parity %
                              2 == 0 else range(1, m)):
                        thetas[(j, l)][m * r**2 + r * j] = coeff_p
                        thetas[(j, l)][m * r**2 - r * j] = coeff_m
                        coeff_p = coeff_p * fact_p
                        coeff_m = coeff_m * fact_m
            if weight_parity % 2 == 0:
                thetas[(0, 0)][0] = 1

            thetas = dict((k, PS(th).add_bigoh(qexp_prec))
                          for (k, th) in thetas.iteritems())

            W = matrix(PS, m + 1 if weight_parity % 2 == 0 else (m - 1), [
                thetas[(j, l)] for j in (xrange(m + 1) if weight_parity %
                                         2 == 0 else range(1, m))
                for l in (xrange(m + 1) if weight_parity %
                          2 == 0 else range(1, m))
            ])

            ## Since the adjoint of matrices with entries in a general ring
            ## is extremely slow for matrices of small size, we hard code the
            ## the cases `m = 2` and `m = 3`.  The expressions are obtained by
            ## computing the adjoint of a matrix with entries `w_{i,j}` in a
            ## polynomial algebra.
            if W.nrows() == 1:
                Wadj = matrix(PS, [[1]])
            elif W.nrows() == 2:
                Wadj = matrix(PS, [[W[1, 1], -W[0, 1]], [-W[1, 0], W[0, 0]]])

            elif W.nrows() == 3 and qexp_prec > 10**5:
                adj00 = W[1, 1] * W[2, 2] - W[2, 1] * W[1, 2]
                adj01 = -W[1, 0] * W[2, 2] + W[2, 0] * W[1, 2]
                adj02 = W[1, 0] * W[2, 1] - W[2, 0] * W[1, 1]
                adj10 = -W[0, 1] * W[2, 2] + W[2, 1] * W[0, 2]
                adj11 = W[0, 0] * W[2, 2] - W[2, 0] * W[0, 2]
                adj12 = -W[0, 0] * W[2, 1] + W[2, 0] * W[0, 1]
                adj20 = W[0, 1] * W[1, 2] - W[1, 1] * W[0, 2]
                adj21 = -W[0, 0] * W[1, 2] + W[1, 0] * W[0, 2]
                adj22 = W[0, 0] * W[1, 1] - W[1, 0] * W[0, 1]

                Wadj = matrix(PS,
                              [[adj00, adj01, adj02], [adj10, adj11, adj12],
                               [adj20, adj21, adj22]])

            elif W.nrows() == 4 and qexp_prec > 10**5:
                adj00 = -W[0, 2] * W[1, 1] * W[2, 0] + W[0, 1] * W[1, 2] * W[
                    2, 0] + W[0, 2] * W[1, 0] * W[2, 1] - W[0, 0] * W[
                        1, 2] * W[2, 1] - W[0, 1] * W[1, 0] * W[2, 2] + W[
                            0, 0] * W[1, 1] * W[2, 2]
                adj01 = -W[0, 3] * W[1, 1] * W[2, 0] + W[0, 1] * W[1, 3] * W[
                    2, 0] + W[0, 3] * W[1, 0] * W[2, 1] - W[0, 0] * W[
                        1, 3] * W[2, 1] - W[0, 1] * W[1, 0] * W[2, 3] + W[
                            0, 0] * W[1, 1] * W[2, 3]
                adj02 = -W[0, 3] * W[1, 2] * W[2, 0] + W[0, 2] * W[1, 3] * W[
                    2, 0] + W[0, 3] * W[1, 0] * W[2, 2] - W[0, 0] * W[
                        1, 3] * W[2, 2] - W[0, 2] * W[1, 0] * W[2, 3] + W[
                            0, 0] * W[1, 2] * W[2, 3]
                adj03 = -W[0, 3] * W[1, 2] * W[2, 1] + W[0, 2] * W[1, 3] * W[
                    2, 1] + W[0, 3] * W[1, 1] * W[2, 2] - W[0, 1] * W[
                        1, 3] * W[2, 2] - W[0, 2] * W[1, 1] * W[2, 3] + W[
                            0, 1] * W[1, 2] * W[2, 3]

                adj10 = -W[0, 2] * W[1, 1] * W[3, 0] + W[0, 1] * W[1, 2] * W[
                    3, 0] + W[0, 2] * W[1, 0] * W[3, 1] - W[0, 0] * W[
                        1, 2] * W[3, 1] - W[0, 1] * W[1, 0] * W[3, 2] + W[
                            0, 0] * W[1, 1] * W[3, 2]
                adj11 = -W[0, 3] * W[1, 1] * W[3, 0] + W[0, 1] * W[1, 3] * W[
                    3, 0] + W[0, 3] * W[1, 0] * W[3, 1] - W[0, 0] * W[
                        1, 3] * W[3, 1] - W[0, 1] * W[1, 0] * W[3, 3] + W[
                            0, 0] * W[1, 1] * W[3, 3]
                adj12 = -W[0, 3] * W[1, 2] * W[3, 0] + W[0, 2] * W[1, 3] * W[
                    3, 0] + W[0, 3] * W[1, 0] * W[3, 2] - W[0, 0] * W[
                        1, 3] * W[3, 2] - W[0, 2] * W[1, 0] * W[3, 3] + W[
                            0, 0] * W[1, 2] * W[3, 3]
                adj13 = -W[0, 3] * W[1, 2] * W[3, 1] + W[0, 2] * W[1, 3] * W[
                    3, 1] + W[0, 3] * W[1, 1] * W[3, 2] - W[0, 1] * W[
                        1, 3] * W[3, 2] - W[0, 2] * W[1, 1] * W[3, 3] + W[
                            0, 1] * W[1, 2] * W[3, 3]

                adj20 = -W[0, 2] * W[2, 1] * W[3, 0] + W[0, 1] * W[2, 2] * W[
                    3, 0] + W[0, 2] * W[2, 0] * W[3, 1] - W[0, 0] * W[
                        2, 2] * W[3, 1] - W[0, 1] * W[2, 0] * W[3, 2] + W[
                            0, 0] * W[2, 1] * W[3, 2]
                adj21 = -W[0, 3] * W[2, 1] * W[3, 0] + W[0, 1] * W[2, 3] * W[
                    3, 0] + W[0, 3] * W[2, 0] * W[3, 1] - W[0, 0] * W[
                        2, 3] * W[3, 1] - W[0, 1] * W[2, 0] * W[3, 3] + W[
                            0, 0] * W[2, 1] * W[3, 3]
                adj22 = -W[0, 3] * W[2, 2] * W[3, 0] + W[0, 2] * W[2, 3] * W[
                    3, 0] + W[0, 3] * W[2, 0] * W[3, 2] - W[0, 0] * W[
                        2, 3] * W[3, 2] - W[0, 2] * W[2, 0] * W[3, 3] + W[
                            0, 0] * W[2, 2] * W[3, 3]
                adj23 = -W[0, 3] * W[2, 2] * W[3, 1] + W[0, 2] * W[2, 3] * W[
                    3, 1] + W[0, 3] * W[2, 1] * W[3, 2] - W[0, 1] * W[
                        2, 3] * W[3, 2] - W[0, 2] * W[2, 1] * W[3, 3] + W[
                            0, 1] * W[2, 2] * W[3, 3]

                adj30 = -W[1, 2] * W[2, 1] * W[3, 0] + W[1, 1] * W[2, 2] * W[
                    3, 0] + W[1, 2] * W[2, 0] * W[3, 1] - W[1, 0] * W[
                        2, 2] * W[3, 1] - W[1, 1] * W[2, 0] * W[3, 2] + W[
                            1, 0] * W[2, 1] * W[3, 2]
                adj31 = -W[1, 3] * W[2, 1] * W[3, 0] + W[1, 1] * W[2, 3] * W[
                    3, 0] + W[1, 3] * W[2, 0] * W[3, 1] - W[1, 0] * W[
                        2, 3] * W[3, 1] - W[1, 1] * W[2, 0] * W[3, 3] + W[
                            1, 0] * W[2, 1] * W[3, 3]
                adj32 = -W[1, 3] * W[2, 2] * W[3, 0] + W[1, 2] * W[2, 3] * W[
                    3, 0] + W[1, 3] * W[2, 0] * W[3, 2] - W[1, 0] * W[
                        2, 3] * W[3, 2] - W[1, 2] * W[2, 0] * W[3, 3] + W[
                            1, 0] * W[2, 2] * W[3, 3]
                adj33 = -W[1, 3] * W[2, 2] * W[3, 1] + W[1, 2] * W[2, 3] * W[
                    3, 1] + W[1, 3] * W[2, 1] * W[3, 2] - W[1, 1] * W[
                        2, 3] * W[3, 2] - W[1, 2] * W[2, 1] * W[3, 3] + W[
                            1, 1] * W[2, 2] * W[3, 3]

                Wadj = matrix(PS, [[adj00, adj01, adj02, adj03],
                                   [adj10, adj11, adj12, adj13],
                                   [adj20, adj21, adj22, adj23],
                                   [adj30, adj31, adj32, adj33]])
            else:
                Wadj = W.adjoint()

            if weight_parity % 2 == 0:
                wronskian_adjoint = [[Wadj[i, r] for i in xrange(m + 1)]
                                     for r in xrange(m + 1)]
            else:
                wronskian_adjoint = [[Wadj[i, r] for i in xrange(m - 1)]
                                     for r in xrange(m - 1)]

            if p is None:
                if weight_parity % 2 == 0:
                    self.__wronskian_adjoint_even = wronskian_adjoint
                else:
                    self.__wronskian_adjoint_odd = wronskian_adjoint

            return wronskian_adjoint
    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_()
Beispiel #32
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
    #
    # __call__ : works just fine
    #
    # __contains__ : works just fine
    #
    # base_extend : works just fine
    #
    # is_exact : works just fine
    #
    # random_element : works just fine
    #
    # is_atomic_repr : 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
    #

    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
        """
        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
        ParentWithGens.__init__(self, base_ring, name_list)
        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()

        ## use the following in PowerSeriesRing_generic.__call__
        self._PowerSeriesRing_generic__power_series_class = MPowerSeries

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

    def __reduce__(self):
        """
        EXAMPLES::
        
            sage: R.<t,u,v> = PowerSeriesRing(ZZ)
            sage: S = loads(dumps(R)); S
            Multivariate Power Series Ring in t, u, v over Integer Ring
            sage: type(S)
            <class 'sage.rings.multi_power_series_ring.MPowerSeriesRing_generic'>
            sage: R.<x,y,z> = PowerSeriesRing(QQ, default_prec=10, sparse=True)
            sage: S = loads(dumps(R)); S
            Sparse Multivariate Power Series Ring in x, y, z over Rational Field
            sage: type(S)
            <class 'sage.rings.multi_power_series_ring.MPowerSeriesRing_generic'>
        """
        return unpickle_multi_power_series_ring_v0, self._params

    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


        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):
        """
        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
        """
        try:
            prec = f.prec()
        except AttributeError:
            prec = infinity
        return MPowerSeries(self, f, 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 MPowerSeries(self,
                            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.subs({self._bg_indeterminate: 1}))
    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_()
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}))
Beispiel #35
0
    def guess(self, sequence, algorithm='sage'):
        """
        Return the minimal CFiniteSequence that generates the sequence.

        Assume the first value has index 0.

        INPUT:

        - ``sequence`` -- list of integers
        - ``algorithm`` -- string
            - 'sage' - the default is to use Sage's matrix kernel function
            - 'pari' - use Pari's implementation of LLL
            - 'bm' - use Sage's Berlekamp-Massey algorithm

        OUTPUT:

        - a CFiniteSequence, or 0 if none could be found

        With the default kernel method, trailing zeroes are chopped
        off before a guessing attempt. This may reduce the data
        below the accepted length of six values.

        EXAMPLES::

            sage: C.<x> = CFiniteSequences(QQ)
            sage: C.guess([1,2,4,8,16,32])
            C-finite sequence, generated by 1/(-2*x + 1)
            sage: r = C.guess([1,2,3,4,5])
            Traceback (most recent call last):
            ...
            ValueError: Sequence too short for guessing.

        With Berlekamp-Massey, if an odd number of values is given, the last one is dropped.
        So with an odd number of values the result may not generate the last value::

            sage: r = C.guess([1,2,4,8,9], algorithm='bm'); r
            C-finite sequence, generated by 1/(-2*x + 1)
            sage: r[0:5]
            [1, 2, 4, 8, 16]
        """
        S = self.polynomial_ring()
        if algorithm == 'bm':
            from sage.matrix.berlekamp_massey import berlekamp_massey
            if len(sequence) < 2:
                raise ValueError('Sequence too short for guessing.')
            R = PowerSeriesRing(QQ, 'x')
            if len(sequence) % 2 == 1:
                sequence = sequence[:-1]
            l = len(sequence) - 1
            denominator = S(berlekamp_massey(sequence).list()[::-1])
            numerator = R(S(sequence) * denominator, prec=l).truncate()

            return CFiniteSequence(numerator / denominator)
        elif algorithm == 'pari':
            global _gp
            if len(sequence) < 6:
                raise ValueError('Sequence too short for guessing.')
            if _gp is None:
                _gp = Gp()
                _gp("ggf(v)=local(l,m,p,q,B);l=length(v);B=floor(l/2);\
                if(B<3,return(0));m=matrix(B,B,x,y,v[x-y+B+1]);\
                q=qflll(m,4)[1];if(length(q)==0,return(0));\
                p=sum(k=1,B,x^(k-1)*q[k,1]);\
                q=Pol(Pol(vector(l,n,v[l-n+1]))*p+O(x^(B+1)));\
                if(polcoeff(p,0)<0,q=-q;p=-p);q=q/p;p=Ser(q+O(x^(l+1)));\
                for(m=1,l,if(polcoeff(p,m-1)!=v[m],return(0)));q")
            _gp.set('gf', sequence)
            _gp("gf=ggf(gf)")
            num = S(sage_eval(_gp.eval("Vec(numerator(gf))"))[::-1])
            den = S(sage_eval(_gp.eval("Vec(denominator(gf))"))[::-1])
            if num == 0:
                return 0
            else:
                return CFiniteSequence(num / den)
        else:
            from sage.matrix.constructor import matrix
            from sage.functions.other import floor, ceil
            from numpy import trim_zeros
            l = len(sequence)
            while l > 0 and sequence[l-1] == 0:
                l -= 1
            sequence = sequence[:l]
            if l == 0:
                return 0
            if l < 6:
                raise ValueError('Sequence too short for guessing.')

            hl = ceil(ZZ(l)/2)
            A = matrix([sequence[k:k+hl] for k in range(hl)])
            K = A.kernel()
            if K.dimension() == 0:
                return 0
            R = PolynomialRing(QQ, 'x')
            den = R(trim_zeros(K.basis()[-1].list()[::-1]))
            if den == 1:
                return 0
            offset = next((i for i, x in enumerate(sequence) if x!=0), None)
            S = PowerSeriesRing(QQ, 'x', default_prec=l-offset)
            num = S(R(sequence)*den).add_bigoh(floor(ZZ(l)/2+1)).truncate()
            if num == 0 or sequence != S(num/den).list():
                return 0
            else:
                return CFiniteSequence(num / den)
Beispiel #36
0
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
def theta_by_cholesky(self, q_prec):
    r"""
    Uses the real Cholesky decomposition to compute (the `q`-expansion of) the
    theta function of the quadratic form as a power series in `q` with terms
    correct up to the power `q^{\text{q\_prec}}`. (So its error is `O(q^
    {\text{q\_prec} + 1})`.)

    REFERENCE: 
        From Cohen's "A Course in Computational Algebraic Number Theory" book,
        p 102.

    EXAMPLES::
    
        ## Check the sum of 4 squares form against Jacobi's formula
        sage: Q = DiagonalQuadraticForm(ZZ, [1,1,1,1])
        sage: Theta = Q.theta_by_cholesky(10)
        sage: Theta
        1 + 8*q + 24*q^2 + 32*q^3 + 24*q^4 + 48*q^5 + 96*q^6 + 64*q^7 + 24*q^8 + 104*q^9 + 144*q^10
        sage: Expected =  [1] + [8*sum([d for d in divisors(n) if d%4 != 0])  for n in range(1,11)]
        sage: Expected
        [1, 8, 24, 32, 24, 48, 96, 64, 24, 104, 144]
        sage: Theta.list() == Expected
        True
        
    ::

        ## Check the form x^2 + 3y^2 + 5z^2 + 7w^2 represents everything except 2 and 22.
        sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7])
        sage: Theta = Q.theta_by_cholesky(50)
        sage: Theta_list = Theta.list()
        sage: [m  for m in range(len(Theta_list))  if Theta_list[m] == 0]
        [2, 22]

    """
    ## RAISE AN ERROR -- This routine is deprecated!
    #raise NotImplementedError, "This routine is deprecated.  Try theta_series(), which uses theta_by_pari()."

    n = self.dim()
    theta = [0 for i in range(q_prec + 1)]
    PS = PowerSeriesRing(ZZ, 'q')

    bit_prec = 53  ## TO DO: Set this precision to reflect the appropriate roundoff
    Cholesky = self.cholesky_decomposition(
        bit_prec
    )  ## error estimate, to be confident through our desired q-precision.
    Q = Cholesky  ##  <----  REDUNDANT!!!
    R = RealField(bit_prec)
    half = R(0.5)

    ## 1. Initialize
    i = n - 1
    T = [R(0) for j in range(n)]
    U = [R(0) for j in range(n)]
    T[i] = R(q_prec)
    U[i] = 0
    L = [0 for j in range(n)]
    x = [0 for j in range(n)]

    ## 2. Compute bounds
    #Z = sqrt(T[i] / Q[i,i])      ## IMPORTANT NOTE: sqrt preserves the precision of the real number it's given... which is not so good... =|
    #L[i] = floor(Z - U[i])       ## Note: This is a Sage Integer
    #x[i] = ceil(-Z - U[i]) - 1   ## Note: This is a Sage Integer too

    done_flag = False
    from_step4_flag = False
    from_step3_flag = True  ## We start by pretending this, since then we get to run through 2 and 3a once. =)

    #double Q_val_double;
    #unsigned long Q_val;                 // WARNING: Still need a good way of checking overflow for this value...

    ## Big loop which runs through all vectors
    while (done_flag == False):

        ## Loop through until we get to i=1 (so we defined a vector x)
        while from_step3_flag or from_step4_flag:  ## IMPORTANT WARNING:  This replaces a do...while loop, so it may have to be adjusted!

            ## Go to directly to step 3 if we're coming from step 4, otherwise perform step 2.
            if from_step4_flag:
                from_step4_flag = False
            else:
                ## 2. Compute bounds
                from_step3_flag = False
                Z = sqrt(T[i] / Q[i, i])
                L[i] = floor(Z - U[i])
                x[i] = ceil(-Z - U[i]) - 1

            ## 3a. Main loop

            ## DIAGNOSTIC
            #print
            #print "  L = ", L
            #print "  x = ", x

            x[i] += 1
            while (x[i] > L[i]):

                ## DIAGNOSTIC
                #print "  x = ", x

                i += 1
                x[i] += 1

            ## 3b. Main loop
            if (i > 0):
                from_step3_flag = True

                ## DIAGNOSTIC
                #print " i = " + str(i)
                #print " T[i] = " + str(T[i])
                #print " Q[i,i] = " + str(Q[i,i])
                #print " x[i] = " + str(x[i])
                #print " U[i] = " + str(U[i])
                #print " x[i] + U[i] = " + str(x[i] + U[i])
                #print " T[i-1] = " + str(T[i-1])

                T[i - 1] = T[i] - Q[i, i] * (x[i] + U[i]) * (x[i] + U[i])

                # DIAGNOSTIC
                #print " T[i-1] = " + str(T[i-1])
                #print

                i += -1
                U[i] = 0
                for j in range(i + 1, n):
                    U[i] += Q[i, j] * x[j]

        ## 4. Solution found (This happens when i=0)
        from_step4_flag = True
        Q_val_double = q_prec - T[0] + Q[0, 0] * (x[0] + U[0]) * (x[0] + U[0])
        Q_val = floor(
            Q_val_double + half
        )  ## Note: This rounds the value up, since the "round" function returns a float, but floor returns integer.

        ## DIAGNOSTIC
        #print " Q_val_double = ",  Q_val_double
        #print " Q_val = ",  Q_val
        #raise RuntimeError

        ## OPTIONAL SAFETY CHECK:
        eps = 0.000000001
        if (abs(Q_val_double - Q_val) > eps):
            raise RuntimeError, "Oh No!  We have a problem with the floating point precision... \n" \
                + " Q_val_double = " + str(Q_val_double) + "\n" \
                + " Q_val = " + str(Q_val) + "\n" \
                + " x = " + str(x) + "\n"

        ## DIAGNOSTIC
        #print " The float value is " + str(Q_val_double)
        #print " The associated long value is " + str(Q_val)
        #print

        if (Q_val <= q_prec):
            theta[Q_val] += 2

        ## 5. Check if x = 0, for exit condition. =)
        done_flag = True
        for j in range(n):
            if (x[j] != 0):
                done_flag = False

    ## Set the value: theta[0] = 1
    theta[0] = 1

    ## DIAGNOSTIC
    #print "Leaving ComputeTheta \n"

    ## Return the series, truncated to the desired q-precision
    return PS(theta)