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
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)
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))
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)
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])
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
def form_acting_matrix_on_dist(p,M,k,a,b,c,d): """forms a large M x M matrix say G such that if v is the vector of moments of a distribution mu, then v*G is the vector of moments of mu|[a,b;c,d]""" # print("Checking...") # print(a,b,c,d) # print(p) assert (a%p != 0) and (c%p == 0), "acting by bad matrix" R=PowerSeriesRing(QQ,'y',default_prec=M) y=R.gen() scale=(b+d*y)/(a+c*y) t=((a+c*y)**k).truncate(M) A = [] for i in range(0,M): temp1=t.list(); d=len(temp1) for j in range(d,M): temp1 = temp1 + [0] #while len(temp1)>M: # temp1.pop() A = A + [temp1] t=(t*scale).truncate(M) q=p**M B=Matrix(QQ,A).transpose() for r in range(0,M): for c in range(0,M): #B[r,c]=B[r,c]%(p**(M-c)) B[r,c]=B[r,c]%(q) return B
def 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)
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))
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],)
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
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],)
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)
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")
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],)
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))
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 __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 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
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_()
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}))
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}))
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)
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)