def toPolynom(self,n): """ Return a couple compoosed by a polynomial ring and the polynomial equal to the truncated series of degree n-1 EXAMPLES:: sage: sage: L = u+v^2+3*w*z+w^2+u*v*z*w+u^2*v+w*z^4+v^6 sage: (PolRing,PolL) = L.toPolynom(3) sage: PolL v^2 + w^2 + 3*w*z + u sage: PolRing Multivariate Polynomial Ring in u, v, w, z over Rational Field """ if n>0: from sage.rings.polynomial.all import PolynomialRing BR=self.parent().base_ring() R = PolynomialRing(BR,self.parent()._names) v = R.gens() pol = R(0) for i in range(n): l = self.coefficient(i) for (e,t) in l: pol += e*reduce((lambda a,b:a*b),map((lambda a,b:a**b),v,t)) return R,pol else: raise ValueError, "n must be a nonnegative integer"
def __init__(self, PP, f, h=None, names=None, genus=None): x, y, z = PP.gens() df = f.degree() F1 = sum([f[i] * x**i * z**(df - i) for i in range(df + 1)]) if h is None: F = y**2 * z**(df - 2) - F1 else: dh = h.degree() deg = max(df, dh + 1) F0 = sum([h[i] * x**i * z**(dh - i) for i in range(dh + 1)]) F = y**2 * z**(deg - 2) + F0 * y * z**(deg - dh - 1) - F1 * z**(deg - df) plane_curve.ProjectivePlaneCurve.__init__(self, PP, F) R = PP.base_ring() if names is None: names = ("x", "y") else: names = normalize_names(2, names) self._names = names P1 = PolynomialRing(R, name=names[0]) P2 = PolynomialRing(P1, name=names[1]) self._PP = PP self._printing_ring = P2 self._hyperelliptic_polynomials = (f, h) self._genus = genus
def compute_bd(f, b, df, r, alpha): """Determine the next integral basis element form those already computed.""" # obtain the ring of Puiseux series in which the truncated series # live. these should already be such that the base ring is SR, the symbolic # ring. (below we will have to introduce symbolic indeterminants) R = f.parent() F = R.fraction_field() x,y = R.gens() # construct a list of indeterminants and a guess for the next integral # basis element. to make computations uniform in the univariate and # multivariate cases an additional generator of the underlying polynomial # ring is introduced. d = len(b) Q = PolynomialRing(QQbar, ['a%d'%n for n in range(d)] + ['dummy']) a = tuple(Q.gens()) b = tuple(b) P = PuiseuxSeriesRing(Q, str(x)) xx = P.gen() bd = F(y*b[-1]) # XXX HACK for l in range(len(r)): for k in range(len(r[l])): r[l][k] = r[l][k].change_ring(Q) # sufficiently singularize the current integral basis element guess at each # of the singular points of df for l in range(len(df)): k = df[l] # factor alphak = alpha[l] # point at which the truncated series are centered rk = r[l] # truncated puiseux series # singularize the current guess at the current point using each # truncated Puiseux seriesx sufficiently_singular = False while not sufficiently_singular: # from each puiseux series, rki, centered at alphak construct a # system of equations from the negative exponent terms appearing in # the expression A(x,rki)) equations = [] for rki in rk: # A = sum(a[j] * b[j](xx,rki) for j in range(d)) A = evaluate_A(a,b,xx,rki,d) A += bd(xx, rki) # implicit division by x-alphak, hence truncation to x^1 terms = A.truncate(1).coefficients() equations.extend(terms) # attempt to solve this linear system of equations. if a (unique) # solution exists then the integral basis element is not singular # enough at alphak sols = solve_coefficient_system(Q, equations, a) if not sols is None: bdm1 = sum(F(sols[i][0])*b[i] for i in range(d)) bd = F(bdm1 + bd)/ F(k) else: sufficiently_singular = True return bd
def compute_bd(f, b, df, r, alpha): """Determine the next integral basis element form those already computed.""" # obtain the ring of Puiseux series in which the truncated series # live. these should already be such that the base ring is SR, the symbolic # ring. (below we will have to introduce symbolic indeterminants) R = f.parent() F = R.fraction_field() x, y = R.gens() # construct a list of indeterminants and a guess for the next integral # basis element. to make computations uniform in the univariate and # multivariate cases an additional generator of the underlying polynomial # ring is introduced. d = len(b) Q = PolynomialRing(QQbar, ['a%d' % n for n in range(d)] + ['dummy']) a = tuple(Q.gens()) b = tuple(b) P = PuiseuxSeriesRing(Q, str(x)) xx = P.gen() bd = F(y * b[-1]) # XXX HACK for l in range(len(r)): for k in range(len(r[l])): r[l][k] = r[l][k].change_ring(Q) # sufficiently singularize the current integral basis element guess at each # of the singular points of df for l in range(len(df)): k = df[l] # factor alphak = alpha[l] # point at which the truncated series are centered rk = r[l] # truncated puiseux series # singularize the current guess at the current point using each # truncated Puiseux seriesx sufficiently_singular = False while not sufficiently_singular: # from each puiseux series, rki, centered at alphak construct a # system of equations from the negative exponent terms appearing in # the expression A(x,rki)) equations = [] for rki in rk: # A = sum(a[j] * b[j](xx,rki) for j in range(d)) A = evaluate_A(a, b, xx, rki, d) A += bd(xx, rki) # implicit division by x-alphak, hence truncation to x^1 terms = A.truncate(1).coefficients() equations.extend(terms) # attempt to solve this linear system of equations. if a (unique) # solution exists then the integral basis element is not singular # enough at alphak sols = solve_coefficient_system(Q, equations, a) if not sols is None: bdm1 = sum(F(sols[i][0]) * b[i] for i in range(d)) bd = F(bdm1 + bd) / F(k) else: sufficiently_singular = True return bd
def local_coordinates_at_infinity(self, prec=20, name='t'): """ For the genus `g` hyperelliptic curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x^g/y` is the local parameter at infinity INPUT: - ``prec`` -- desired precision of the local coordinates - ``name`` -- generator of the power series ring (default: ``t``) OUTPUT: `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x^g/y` is the local parameter at infinity EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-5*x^2+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + 5*t^4 - t^8 - 50*t^10 + O(t^12) sage: y t^-5 + 10*t - 2*t^5 - 75*t^7 + 50*t^11 + O(t^12) :: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^3-x+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + t^2 - t^4 - t^6 + 3*t^8 + O(t^12) sage: y t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12) AUTHOR: - Jennifer Balakrishnan (2007-12) """ g = self.genus() pol = self.hyperelliptic_polynomials()[0] K = LaurentSeriesRing(self.base_ring(), name, default_prec=prec + 2) t = K.gen() L = PolynomialRing(K, 'x') x = L.gen() i = 0 w = (x**g / t)**2 - pol wprime = w.derivative(x) if pol.degree() == 2 * g + 1: x = t**-2 else: x = t**-1 for i in range((RR(log(prec + 2) / log(2))).ceil()): x = x - w(x) / wprime(x) y = x**g / t return x + O(t**(prec + 2)), y + O(t**(prec + 2))
def _find_alpha(self, p, k, M=None, ap=None, new_base_ring=None, ordinary=True, check=True, find_extraprec=True): """ Finds `alpha`, a `U_p` eigenvalue, which is found as a root of the polynomial `x^2 - ap * x + p^(k+1)`. INPUT: - ``p`` -- prime - ``k`` -- Pollack-Stevens weight - ``M`` -- precision (default = None) of `Q_p` - ``ap`` -- Hecke eigenvalue at p (default = None) - ``new_base_ring`` -- field of definition of `alpha` (default = None) - ``ordinary`` -- True if the prime is ordinary (default = True) - ``check`` -- check to see if the prime is ordinary (default = True) - ``find_extraprec`` -- setting this to True finds extra precision (default = True) OUTPUT: - ``alpha`` -- `U_p` eigenvalue - ``new_base_ring`` -- field of definition of `alpha` with precision at least `newM` - ``newM`` -- new precision - ``eisenloss`` -- loss of precision - ``q`` -- a prime not equal to p which was used to find extra precision - ``aq`` -- the Hecke eigenvalue `aq` corresponding to `q` EXAMPLES:: sage: from sage.modular.pollack_stevens.space import ps_modsym_from_elliptic_curve sage: E = EllipticCurve('11a') sage: p = 5 sage: M = 10 sage: k = 0 sage: phi = ps_modsym_from_elliptic_curve(E) sage: phi._find_alpha(p,k,M) (1 + 4*5 + 3*5^2 + 2*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 3*5^7 + 2*5^8 + 3*5^9 + 3*5^10 + 3*5^12 + O(5^13), 5-adic Field with capped relative precision 13, 12, 1, None, None) """ if ap is None: ap = self.Tq_eigenvalue(p, check=check) if check and ap.valuation(p) > 0: raise ValueError("p is not ordinary") poly = PolynomialRing(ap.parent(), 'x')([p**(k+1), -ap, 1]) if new_base_ring is None: # These should actually be completions of disc.parent() if p == 2: # is this the right precision adjustment for p=2? new_base_ring = Qp(2, M+1) else: new_base_ring = Qp(p, M) set_padicbase = True else: set_padicbase = False try: verbose("finding alpha: rooting %s in %s"%(poly, new_base_ring)) (v0,e0),(v1,e1) = poly.roots(new_base_ring) except TypeError, ValueError: raise ValueError("new base ring must contain a root of x^2 - ap * x + p^(k+1)")
def local_coordinates_at_infinity(self, prec = 20, name = 't'): """ For the genus `g` hyperelliptic curve `y^2 = f(x)`, return `(x(t), y(t))` such that `(y(t))^2 = f(x(t))`, where `t = x^g/y` is the local parameter at infinity INPUT: - ``prec`` -- desired precision of the local coordinates - ``name`` -- generator of the power series ring (default: ``t``) OUTPUT: `(x(t),y(t))` such that `y(t)^2 = f(x(t))` and `t = x^g/y` is the local parameter at infinity EXAMPLES:: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^5-5*x^2+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + 5*t^4 - t^8 - 50*t^10 + O(t^12) sage: y t^-5 + 10*t - 2*t^5 - 75*t^7 + 50*t^11 + O(t^12) :: sage: R.<x> = QQ['x'] sage: H = HyperellipticCurve(x^3-x+1) sage: x,y = H.local_coordinates_at_infinity(10) sage: x t^-2 + t^2 - t^4 - t^6 + 3*t^8 + O(t^12) sage: y t^-3 + t - t^3 - t^5 + 3*t^7 - 10*t^11 + O(t^12) AUTHOR: - Jennifer Balakrishnan (2007-12) """ g = self.genus() pol = self.hyperelliptic_polynomials()[0] K = LaurentSeriesRing(self.base_ring(), name, default_prec=prec+2) t = K.gen() L = PolynomialRing(K,'x') x = L.gen() i = 0 w = (x**g/t)**2-pol wprime = w.derivative(x) x = t**-2 for i in range((RR(log(prec+2)/log(2))).ceil()): x = x - w(x)/wprime(x) y = x**g/t return x+O(t**(prec+2)) , y+O(t**(prec+2))
def specialize(self): """specializes to weight k -- i.e. projects to Sym^k""" k=self.weight if k==0: # R.<X,Y>=PolynomialRing(QQ,2) R = PolynomialRing(QQ,('X','Y')) X,Y = R.gens() P=0 for j in range(0,k+1): P=P+binomial(k,j)*((-1)**j)*self.moment(j)*(X**j)*(Y**(k-j)) return symk(k,P)
def map(self,psi): """psi is a map from the base_ring to Qp and this function applies psi to all polynomial coefficients and then lifts them to QQ""" #assert psi.domain()==self.base_ring k=self.weight # S.<X,Y>=PolynomialRing(QQ) S = PolynomialRing(QQ,('X','Y')) X,Y = S.gens() ans=0*X for j in range(k+1): ans=ans+psi(self.coef(j)).lift()*(X**j)*(Y**(k-j)) temp=copy(self) temp.poly=ans temp.base_ring=psi.codomain() return temp
def __init__(self,k,poly=None,base_ring=QQ): """A symk object is stored as a homogeneous polynomial of degree k Inputs: k -- weight poly -- homogeneous of weight k in X and Y base_ring -- ring containing the coefficients of poly""" #assert (poly == None) or (poly == 0) or (k==0) or (poly.is_homogeneous()), "must enter a homogeneous polynomial" #if (poly != None) and (poly !=0) and (k<>0): # assert poly.total_degree() == k, "the weight is incorrect" if poly != None: self.poly=poly else: # R.<X,Y>=PolynomialRing(base_ring,2) # self.poly=0*X R = PolynomialRing(base_ring,('X','Y')) self.poly=0*R.gens()[0] self.weight=k self.base_ring=base_ring
def _find_alpha(self, p, k, M=None, ap=None, new_base_ring=None, ordinary=True, check=True, find_extraprec=True): """ """ if ap is None: ap = self.Tq_eigenvalue(p, check=check) if check and ap.valuation(p) > 0: raise ValueError("p is not ordinary") poly = PolynomialRing(ap.parent(), 'x')([p**(k+1), -ap, 1]) if new_base_ring is None: # These should actually be completions of disc.parent() if p == 2: # is this the right precision adjustment for p=2? new_base_ring = Qp(2, M+1) else: new_base_ring = Qp(p, M) set_padicbase = True else: set_padicbase = False try: verbose("finding alpha: rooting %s in %s"%(poly, new_base_ring)) (v0,e0),(v1,e1) = poly.roots(new_base_ring) except TypeError, ValueError: raise ValueError("new base ring must contain a root of x^2 - ap * x + p^(k+1)")
def hyperelliptic_polynomials(self, K=None, var='x'): """ EXAMPLES:: sage: R.<x> = QQ[]; C = HyperellipticCurve(x^3 + x - 1, x^3/5); C Hyperelliptic Curve over Rational Field defined by y^2 + 1/5*x^3*y = x^3 + x - 1 sage: C.hyperelliptic_polynomials() (x^3 + x - 1, 1/5*x^3) """ if K is None: return self._hyperelliptic_polynomials else: f, h = self._hyperelliptic_polynomials P = PolynomialRing(K, var) return (P(f), P(h))
def cover_polynomial(self, K=None, var="x"): """ Return the polynomial defining the cyclic cover. EXAMPLES:: sage: ZZx.<x> = ZZ[]; CyclicCover(5, x^5 + x + 1).cover_polynomial() x^5 + x + 1 """ if K is None: return self._f else: P = PolynomialRing(K, var) return P(self._f)
def local_coordinates(self, pt, n): r""" Return local coordinates to precision n at the given point. Behaviour is flaky - some choices of `n` are worst that others. INPUT: - ``pt`` - an F-rational point on X which is not a point of ramification for the projection (x,y) - x. - ``n`` - the number of terms desired OUTPUT: x = x0 + t y = y0 + power series in t EXAMPLES:: sage: FF = FiniteField(5) sage: P2 = ProjectiveSpace(2, FF, names = ['x','y','z']) sage: x, y, z = P2.coordinate_ring().gens() sage: C = Curve(y^2*z^7-x^9-x*z^8) sage: pt = C([2,3,1]) sage: C.local_coordinates(pt,9) # todo: not implemented !!!! [2 + t, 3 + 3*t^2 + t^3 + 3*t^4 + 3*t^6 + 3*t^7 + t^8 + 2*t^9 + 3*t^11 + 3*t^12] """ f = self.defining_polynomial() R = f.parent() F = self.base_ring() p = F.characteristic() x0 = F(pt[0]) y0 = F(pt[1]) astr = ["a"+str(i) for i in range(1,2*n)] x,y = R.gens() R0 = PolynomialRing(F,2*n+2,names = [str(x),str(y),"t"]+astr) vars0 = R0.gens() t = vars0[2] yt = y0*t**0 + add([vars0[i]*t**(i-2) for i in range(3,2*n+2)]) xt = x0+t ft = f(xt,yt) S = singular S.eval('ring s = '+str(p)+','+str(R0.gens())+',lp;') S.eval('poly f = '+str(ft)) cmd = 'matrix c = coeffs ('+str(ft)+',t)' S.eval(cmd) N = int(S.eval('size(c)')) b = ["c["+str(i)+",1]," for i in range(2,N/2-4)] b = ''.join(b) b = b[:len(b)-1] #to cut off the trailing comma cmd = 'ideal I = '+b S.eval(cmd) c = S.eval('slimgb(I)') d = c.split("=") d = d[1:] d[len(d)-1] += "\n" e = [x[:x.index("\n")] for x in d] vals = [] for x in e: for y in vars0: if str(y) in x: if len(x.replace(str(y),"")) != 0: i = x.find("-") if i>0: vals.append([eval(x[1:i]),x[:i],F(eval(x[i+1:]))]) i = x.find("+") if i>0: vals.append([eval(x[1:i]),x[:i],-F(eval(x[i+1:]))]) else: vals.append([eval(str(y)[1:]),str(y),F(0)]) vals.sort() k = len(vals) v = [x0+t,y0+add([vals[i][2]*t**(i+1) for i in range(k)])] return v
def rational_points_iterator(self): r""" Return a generator object for the rational points on this curve. INPUT: - ``self`` -- a projective curve OUTPUT: A generator of all the rational points on the curve defined over its base field. EXAMPLE:: sage: F = GF(37) sage: P2.<X,Y,Z> = ProjectiveSpace(F,2) sage: C = Curve(X^7+Y*X*Z^5*55+Y^7*12) sage: len(list(C.rational_points_iterator())) 37 :: sage: F = GF(2) sage: P2.<X,Y,Z> = ProjectiveSpace(F,2) sage: C = Curve(X*Y*Z) sage: a = C.rational_points_iterator() sage: next(a) (1 : 0 : 0) sage: next(a) (0 : 1 : 0) sage: next(a) (1 : 1 : 0) sage: next(a) (0 : 0 : 1) sage: next(a) (1 : 0 : 1) sage: next(a) (0 : 1 : 1) sage: next(a) Traceback (most recent call last): ... StopIteration :: sage: F = GF(3^2,'a') sage: P2.<X,Y,Z> = ProjectiveSpace(F,2) sage: C = Curve(X^3+5*Y^2*Z-33*X*Y*X) sage: b = C.rational_points_iterator() sage: next(b) (0 : 1 : 0) sage: next(b) (0 : 0 : 1) sage: next(b) (2*a + 2 : a : 1) sage: next(b) (2 : a + 1 : 1) sage: next(b) (a + 1 : 2*a + 1 : 1) sage: next(b) (1 : 2 : 1) sage: next(b) (2*a + 2 : 2*a : 1) sage: next(b) (2 : 2*a + 2 : 1) sage: next(b) (a + 1 : a + 2 : 1) sage: next(b) (1 : 1 : 1) sage: next(b) Traceback (most recent call last): ... StopIteration """ g = self.defining_polynomial() K = g.parent().base_ring() from sage.rings.polynomial.all import PolynomialRing R = PolynomialRing(K,'X') X = R.gen() one = K.one() zero = K.zero() # the point with Z = 0 = Y try: t = self.point([one,zero,zero]) yield(t) except TypeError: pass # points with Z = 0, Y = 1 g10 = R(g(X,one,zero)) if g10.is_zero(): for x in K: yield(self.point([x,one,zero])) else: for x in g10.roots(multiplicities=False): yield(self.point([x,one,zero])) # points with Z = 1 for y in K: gy1 = R(g(X,y,one)) if gy1.is_zero(): for x in K: yield(self.point([x,y,one])) else: for x in gy1.roots(multiplicities=False): yield(self.point([x,y,one]))
def mnuk_conditions(g, b, generic_adjoint): """Determine the Mnuk conditions on the coefficients of :math:`P`. Determine the conditions on the coefficients `c` of `P` at the integral basis element `b` modulo the curve `g = g(u,v)`. See [Mnuk] for details. Parameters ---------- g : curve An algebraic curve. b : integral basis function An an element of the basis of the integral closure of the coordinate ring of `g`. See :func:`abelfunctions.integralbasis.integral_basis`. generic_adjoint : polynomial A generic adjoint polynomial as provided by :func:`differentials`. Only one instance is created for caching and performance purposes. Returns ------- conditions : list A list of expressions from which a system of equations is build to determine the differentials. """ # extract rings. the generic adjoint should be a member of R[*c][u,v] where # *c is a vector of the indeterminants. we will need to convert it to a # polynomial in R[u,v,*c] and then back (see below) R = g.parent() S = generic_adjoint.parent() B = S.base_ring() c = B.gens() T = QQbar[R.variable_names() + B.variable_names()] # compute b_num(x,y) * P(x,y) and reduce modulo the defining polynomial g. # we do this by casting the polynomial into the ring QQbar(x,*c)[y]. (the # coefficients of y in g need to be units) B = PolynomialRing(QQbar, [R.variable_names()[0]] + list(B.variable_names())) Q = B.fraction_field()[R.variable_names()[1]] u,v = map(Q,R.gens()) numer = b.numerator() denom = b.denominator() expr = numer(u,v) * generic_adjoint(u,v) modulus = g(u,v) r_reduced_mod_g = expr % modulus # now mod out by the denominator to get the remaining component, R(x,y). we # need to cast into the ring QQbar[y,*c][x] in order to do so. (note that # we don't need a base fraction field since the denominator is univariate # and therefore the leading coefficient is always a unit) u,v = map(T, R.gens()) r = r_reduced_mod_g(v).numerator() r_reduced_mod_denom = r.polynomial(u) % T(denom).polynomial(u) # finally, coerce the result to QQbar[*c][x,y] in order to obtain the # coefficients as linear combinations of the c_ij's. r = T(r_reduced_mod_denom(u)) # first need to coerce to "largest" ring, T u,v = map(S, R.gens()) c = map(S, c) args = [u, v] + c r = r(*args) conditions = r.coefficients() return conditions
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 @staticmethod def __classcall__(cls, base_ring, num_gens, name_list, order='negdeglex', default_prec=10, sparse=False): """ Preprocessing of arguments: The term order can be given as string or as a :class:`~sage.rings.polynomial.term_order.TermOrder` instance. TESTS:: sage: P1 = PowerSeriesRing(QQ, ['f0','f1','f2','f3'], order = TermOrder('degrevlex')) sage: P2 = PowerSeriesRing(QQ,4,'f', order='degrevlex') sage: P1 is P2 # indirect doctest True """ order = TermOrder(order, num_gens) return super(MPowerSeriesRing_generic, cls).__classcall__(cls, base_ring, num_gens, name_list, order, default_prec, sparse) 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 the power series are sparse. The underlying polynomial ring is always 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() """ 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(), 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'), prec=12], Multivariate Polynomial Ring in f0, f1, f2, f3 over Rational Field) sage: c Completion[('f0', 'f1', 'f2', 'f3'), prec=12] sage: c(R) Multivariate Power Series Ring in f0, f1, f2, f3 over Rational Field sage: c(R) == M True TESTS:: sage: M2 = PowerSeriesRing(QQ,4,'f', sparse=True) sage: M == M2 False sage: c,R = M2.construction() sage: c(R)==M2 True sage: M3 = PowerSeriesRing(QQ,4,'f', order='degrevlex') sage: M3 == M False sage: M3 == M2 False sage: c,R = M3.construction() sage: c(R)==M3 True """ from sage.categories.pushout import CompletionFunctor extras = {'order': self.term_order(), 'num_gens': self.ngens()} if self.is_sparse(): extras['sparse'] = True return (CompletionFunctor(self._names, self.default_prec(), extras=extras), 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, base_map=None): """ 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 You must either give a base map or there must be a coercion from the base ring to the codomain:: sage: T.<t> = ZZ[] sage: K.<i> = NumberField(t^2 + 1) sage: Q8.<z> = CyclotomicField(8) sage: X.<x> = PowerSeriesRing(Q8) sage: M.<a,b,c> = PowerSeriesRing(K) sage: M._is_valid_homomorphism_(X, [x,x,x+x^2]) # no coercion False sage: M._is_valid_homomorphism_(X, [x,x,x+x^2], base_map=K.hom([z^2])) 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 base_map is None and not codomain.has_coerce_map_from( self.base_ring()): return False if all(v == 0 for v in im_gens): return True from .laurent_series_ring import is_LaurentSeriesRing if is_MPowerSeriesRing(codomain) or is_PowerSeriesRing( codomain) or is_LaurentSeriesRing(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 try: return all(v.is_nilpotent() for v in im_gens) except NotImplementedError: pass return False 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 laurent_series_ring(self): """ Laurent 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.zero().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.") return self._bg_ps_ring(f.homogeneous_components()) 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_.sum(f.polynomial().coefficients())
def differentials_numerators(f): """Return the numerators of a basis of holomorphic differentials on a Riemann surface. Parameters ---------- f : plane algebraic curve Returns ------- differentials : list A list of :class:`Differential`s representing *a* basis of Abelian differentials of the first kind. """ # homogenize and compute total degree R = f.parent().change_ring(QQbar) x,y = R.gens() d = f.total_degree() # construct the generalized adjoint polynomial. we want to think of it as # an element of B[*c][x,y] where B is the base ring of f and *c are the # indeterminates cvars = ['c_%d_%d'%(i,j) for i in range(d-2) for j in range(d-2)] vars = list(R.variable_names()) + cvars C = PolynomialRing(QQbar, cvars) S = PolynomialRing(C, [x,y]) T = PolynomialRing(QQbar, vars) c = S.base_ring().gens() x,y = S(x),S(y) P = sum(c[j+(d-2)*i] * x**i * y**j for i in range(d-2) for j in range(d-2) if i+j <= d-3) # for each singular point [x:y:z] = [alpha:beta:gamma], map f onto the # "most convenient and appropriate" affine subspace, (u,v), and center at # u=0. determine the conditions on P singular_points = singularities(f) conditions = [] for singular_point, _ in singular_points: # recenter the curve and adjoint polynomial at the singular point: find # the affine plane u,v such that the singularity occurs at u=0 g = recenter_curve(f, singular_point) Ptilde = recenter_curve(P, singular_point) # compute the intergral basis at the recentered singular point # and determine the Mnuk conditions of the adjoint polynomial b = integral_basis(g) for bi in b: conditions_bi = mnuk_conditions(g, bi, Ptilde) conditions.extend(conditions_bi) # reduce the general adjoint modulo the ideal generated by the integral # basis conditions. the coefficients of the remaining c_ij's form the # numerators of a basis of abelian differentials of the first kind. # # additionally, we try to coerce the conditions to over QQ for speed. it's # questionable in this situation whether there is a noticible performance # gain but it does suppress the "slow toy implementation" warning. try: T = T.change_ring(QQ) ideal = T.ideal(conditions) basis = ideal.groebner_basis() except: pass ideal = T.ideal(conditions) basis = ideal.groebner_basis() P_reduced = P(T(x), T(y)) if basis != [0]: P_reduced = P_reduced.reduce(basis) U = R[S.base_ring().variable_names()] args = [U(x),U(y)] + [U(ci) for ci in c] Pc = P_reduced(*args) numerators = Pc.coefficients() return numerators
def mnuk_conditions(g, b, generic_adjoint): """Determine the Mnuk conditions on the coefficients of :math:`P`. Determine the conditions on the coefficients `c` of `P` at the integral basis element `b` modulo the curve `g = g(u,v)`. See [Mnuk] for details. Parameters ---------- g : curve An algebraic curve. b : integral basis function An an element of the basis of the integral closure of the coordinate ring of `g`. See :func:`abelfunctions.integralbasis.integral_basis`. generic_adjoint : polynomial A generic adjoint polynomial as provided by :func:`differentials`. Only one instance is created for caching and performance purposes. Returns ------- conditions : list A list of expressions from which a system of equations is build to determine the differentials. """ # extract rings. the generic adjoint should be a member of R[*c][u,v] where # *c is a vector of the indeterminants. we will need to convert it to a # polynomial in R[u,v,*c] and then back (see below) R = g.parent() S = generic_adjoint.parent() B = S.base_ring() c = B.gens() T = QQbar[R.variable_names() + B.variable_names()] # compute b_num(x,y) * P(x,y) and reduce modulo the defining polynomial g. # we do this by casting the polynomial into the ring QQbar(x,*c)[y]. (the # coefficients of y in g need to be units) B = PolynomialRing(QQbar, [R.variable_names()[0]] + list(B.variable_names())) Q = B.fraction_field()[R.variable_names()[1]] u,v = map(Q,R.gens()) numer = b.numerator() denom = b.denominator() expr = numer(u,v) * generic_adjoint(u,v) modulus = g(u,v) r_reduced_mod_g = expr % modulus # now mod out by the denominator to get the remaining component, R(x,y). we # need to cast into the ring QQbar[y,*c][x] in order to do so. (note that # we don't need a base fraction field since the denominator is univariate # and therefore the leading coefficient is always a unit) u,v = map(T, R.gens()) r = r_reduced_mod_g(v).numerator() r_reduced_mod_denom = r.polynomial(u) % T(denom).polynomial(u) # finally, coerce the result to QQbar[*c][x,y] in order to obtain the # coefficients as linear combinations of the c_ij's. r = r_reduced_mod_denom(u) # first need to coerce to "largest" ring, T u,v = map(S, R.gens()) c = map(S, c) args = [u,v] + c r = r(*args) conditions = r.coefficients() return conditions
def splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): """ Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print(e.degree_divisor) ....: print(e.degree_multiple) 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import verbose, cputime degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen().__pari__() verbose("Starting field: %s"%Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree//absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s"%[s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd(factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac//2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq//2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q/q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([4*a0*a2 - a1*a1 -a0*a3*a3, a1*a3 - 4*a0, -a2, 1]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq//3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s"%[s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]"%(degree_divisor,degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError("inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError("inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s"%(f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol*(denom**Mpol.poldegree())).subst("x", pari([0,1/denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s"%Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0])-1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod(Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL/Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s"%Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm//f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k*KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd(f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol/Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
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 splitting_field(poly, name, map=False, degree_multiple=None, abort_degree=None, simplify=True, simplify_all=False): r""" Compute the splitting field of a given polynomial, defined over a number field. INPUT: - ``poly`` -- a monic polynomial over a number field - ``name`` -- a variable name for the number field - ``map`` -- (default: ``False``) also return an embedding of ``poly`` into the resulting field. Note that computing this embedding might be expensive. - ``degree_multiple`` -- a multiple of the absolute degree of the splitting field. If ``degree_multiple`` equals the actual degree, this can enormously speed up the computation. - ``abort_degree`` -- abort by raising a :class:`SplittingFieldAbort` if it can be determined that the absolute degree of the splitting field is strictly larger than ``abort_degree``. - ``simplify`` -- (default: ``True``) during the algorithm, try to find a simpler defining polynomial for the intermediate number fields using PARI's ``polred()``. This usually speeds up the computation but can also considerably slow it down. Try and see what works best in the given situation. - ``simplify_all`` -- (default: ``False``) If ``True``, simplify intermediate fields and also the resulting number field. OUTPUT: If ``map`` is ``False``, the splitting field as an absolute number field. If ``map`` is ``True``, a tuple ``(K, phi)`` where ``phi`` is an embedding of the base field in ``K``. EXAMPLES:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = (x^3 + 2).splitting_field(); K Number Field in a with defining polynomial x^6 + 3*x^5 + 6*x^4 + 11*x^3 + 12*x^2 - 3*x + 1 sage: K.<a> = (x^3 - 3*x + 1).splitting_field(); K Number Field in a with defining polynomial x^3 - 3*x + 1 The ``simplify`` and ``simplify_all`` flags usually yield fields defined by polynomials with smaller coefficients. By default, ``simplify`` is True and ``simplify_all`` is False. :: sage: (x^4 - x + 1).splitting_field('a', simplify=False) Number Field in a with defining polynomial x^24 - 2780*x^22 + 2*x^21 + 3527512*x^20 - 2876*x^19 - 2701391985*x^18 + 945948*x^17 + 1390511639677*x^16 + 736757420*x^15 - 506816498313560*x^14 - 822702898220*x^13 + 134120588299548463*x^12 + 362240696528256*x^11 - 25964582366880639486*x^10 - 91743672243419990*x^9 + 3649429473447308439427*x^8 + 14310332927134072336*x^7 - 363192569823568746892571*x^6 - 1353403793640477725898*x^5 + 24293393281774560140427565*x^4 + 70673814899934142357628*x^3 - 980621447508959243128437933*x^2 - 1539841440617805445432660*x + 18065914012013502602456565991 sage: (x^4 - x + 1).splitting_field('a', simplify=True) Number Field in a with defining polynomial x^24 + 8*x^23 - 32*x^22 - 310*x^21 + 540*x^20 + 4688*x^19 - 6813*x^18 - 32380*x^17 + 49525*x^16 + 102460*x^15 - 129944*x^14 - 287884*x^13 + 372727*x^12 + 150624*x^11 - 110530*x^10 - 566926*x^9 + 1062759*x^8 - 779940*x^7 + 863493*x^6 - 1623578*x^5 + 1759513*x^4 - 955624*x^3 + 459975*x^2 - 141948*x + 53919 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Reducible polynomials also work:: sage: pol = (x^4 - 1)*(x^2 + 1/2)*(x^2 + 1/3) sage: pol.splitting_field('a', simplify_all=True) Number Field in a with defining polynomial x^8 - x^4 + 1 Relative situation:: sage: R.<x> = PolynomialRing(QQ) sage: K.<a> = NumberField(x^3 + 2) sage: S.<t> = PolynomialRing(K) sage: L.<b> = (t^2 - a).splitting_field() sage: L Number Field in b with defining polynomial t^6 + 2 With ``map=True``, we also get the embedding of the base field into the splitting field:: sage: L.<b>, phi = (t^2 - a).splitting_field(map=True) sage: phi Ring morphism: From: Number Field in a with defining polynomial x^3 + 2 To: Number Field in b with defining polynomial t^6 + 2 Defn: a |--> b^2 sage: (x^4 - x + 1).splitting_field('a', simplify_all=True, map=True)[1] Ring morphism: From: Rational Field To: Number Field in a with defining polynomial x^24 - 3*x^23 + 2*x^22 - x^20 + 4*x^19 + 32*x^18 - 35*x^17 - 92*x^16 + 49*x^15 + 163*x^14 - 15*x^13 - 194*x^12 - 15*x^11 + 163*x^10 + 49*x^9 - 92*x^8 - 35*x^7 + 32*x^6 + 4*x^5 - x^4 + 2*x^2 - 3*x + 1 Defn: 1 |--> 1 We can enable verbose messages:: sage: from sage.misc.verbose import set_verbose sage: set_verbose(2) sage: K.<a> = (x^3 - x + 1).splitting_field() verbose 1 (...: splitting_field.py, splitting_field) Starting field: y verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [(3, 0)] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(2, 2), (3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^2 + 23 verbose 1 (...: splitting_field.py, splitting_field) New field before simplifying: x^2 + 23 (time = ...) verbose 1 (...: splitting_field.py, splitting_field) New field: y^2 - y + 6 (time = ...) verbose 2 (...: splitting_field.py, splitting_field) Converted polynomials to new field (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to factor: [] verbose 2 (...: splitting_field.py, splitting_field) Done factoring (time = ...) verbose 1 (...: splitting_field.py, splitting_field) SplittingData to handle: [(3, 3)] verbose 1 (...: splitting_field.py, splitting_field) Bounds for absolute degree: [6, 6] verbose 2 (...: splitting_field.py, splitting_field) Handling polynomial x^3 - x + 1 verbose 1 (...: splitting_field.py, splitting_field) New field: y^6 + 3*y^5 + 19*y^4 + 35*y^3 + 127*y^2 + 73*y + 271 (time = ...) sage: set_verbose(0) Try all Galois groups in degree 4. We use a quadratic base field such that ``polgalois()`` cannot be used:: sage: R.<x> = PolynomialRing(QuadraticField(-11)) sage: C2C2pol = x^4 - 10*x^2 + 1 sage: C2C2pol.splitting_field('x') Number Field in x with defining polynomial x^8 + 24*x^6 + 608*x^4 + 9792*x^2 + 53824 sage: C4pol = x^4 + x^3 + x^2 + x + 1 sage: C4pol.splitting_field('x') Number Field in x with defining polynomial x^8 - x^7 - 2*x^6 + 5*x^5 + x^4 + 15*x^3 - 18*x^2 - 27*x + 81 sage: D8pol = x^4 - 2 sage: D8pol.splitting_field('x') Number Field in x with defining polynomial x^16 + 8*x^15 + 68*x^14 + 336*x^13 + 1514*x^12 + 5080*x^11 + 14912*x^10 + 35048*x^9 + 64959*x^8 + 93416*x^7 + 88216*x^6 + 41608*x^5 - 25586*x^4 - 60048*x^3 - 16628*x^2 + 12008*x + 34961 sage: A4pol = x^4 - 4*x^3 + 14*x^2 - 28*x + 21 sage: A4pol.splitting_field('x') Number Field in x with defining polynomial x^24 - 20*x^23 + 290*x^22 - 3048*x^21 + 26147*x^20 - 186132*x^19 + 1130626*x^18 - 5913784*x^17 + 26899345*x^16 - 106792132*x^15 + 371066538*x^14 - 1127792656*x^13 + 2991524876*x^12 - 6888328132*x^11 + 13655960064*x^10 - 23000783036*x^9 + 32244796382*x^8 - 36347834476*x^7 + 30850889884*x^6 - 16707053128*x^5 + 1896946429*x^4 + 4832907884*x^3 - 3038258802*x^2 - 200383596*x + 593179173 sage: S4pol = x^4 + x + 1 sage: S4pol.splitting_field('x') Number Field in x with defining polynomial x^48 ... Some bigger examples:: sage: R.<x> = PolynomialRing(QQ) sage: pol15 = chebyshev_T(31, x) - 1 # 2^30*(x-1)*minpoly(cos(2*pi/31))^2 sage: pol15.splitting_field('a') Number Field in a with defining polynomial x^15 - x^14 - 14*x^13 + 13*x^12 + 78*x^11 - 66*x^10 - 220*x^9 + 165*x^8 + 330*x^7 - 210*x^6 - 252*x^5 + 126*x^4 + 84*x^3 - 28*x^2 - 8*x + 1 sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: pol48.splitting_field('a') Number Field in a with defining polynomial x^48 ... If you somehow know the degree of the field in advance, you should add a ``degree_multiple`` argument. This can speed up the computation, in particular for polynomials of degree >= 12 or for relative extensions:: sage: pol15.splitting_field('a', degree_multiple=15) Number Field in a with defining polynomial x^15 + x^14 - 14*x^13 - 13*x^12 + 78*x^11 + 66*x^10 - 220*x^9 - 165*x^8 + 330*x^7 + 210*x^6 - 252*x^5 - 126*x^4 + 84*x^3 + 28*x^2 - 8*x - 1 A value for ``degree_multiple`` which isn't actually a multiple of the absolute degree of the splitting field can either result in a wrong answer or the following exception:: sage: pol48.splitting_field('a', degree_multiple=20) Traceback (most recent call last): ... ValueError: inconsistent degree_multiple in splitting_field() Compute the Galois closure as the splitting field of the defining polynomial:: sage: R.<x> = PolynomialRing(QQ) sage: pol48 = x^6 - 4*x^4 + 12*x^2 - 12 sage: K.<a> = NumberField(pol48) sage: L.<b> = pol48.change_ring(K).splitting_field() sage: L Number Field in b with defining polynomial x^48 ... Try all Galois groups over `\QQ` in degree 5 except for `S_5` (the latter is infeasible with the current implementation):: sage: C5pol = x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: C5pol.splitting_field('x') Number Field in x with defining polynomial x^5 + x^4 - 4*x^3 - 3*x^2 + 3*x + 1 sage: D10pol = x^5 - x^4 - 5*x^3 + 4*x^2 + 3*x - 1 sage: D10pol.splitting_field('x') Number Field in x with defining polynomial x^10 - 28*x^8 + 216*x^6 - 681*x^4 + 902*x^2 - 401 sage: AGL_1_5pol = x^5 - 2 sage: AGL_1_5pol.splitting_field('x') Number Field in x with defining polynomial x^20 + 10*x^19 + 55*x^18 + 210*x^17 + 595*x^16 + 1300*x^15 + 2250*x^14 + 3130*x^13 + 3585*x^12 + 3500*x^11 + 2965*x^10 + 2250*x^9 + 1625*x^8 + 1150*x^7 + 750*x^6 + 400*x^5 + 275*x^4 + 100*x^3 + 75*x^2 + 25 sage: A5pol = x^5 - x^4 + 2*x^2 - 2*x + 2 sage: A5pol.splitting_field('x') Number Field in x with defining polynomial x^60 ... We can use the ``abort_degree`` option if we don't want to compute fields of too large degree (this can be used to check whether the splitting field has small degree):: sage: (x^5+x+3).splitting_field('b', abort_degree=119) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field equals 120 sage: (x^10+x+3).splitting_field('b', abort_degree=60) # long time (10s on sage.math, 2014) Traceback (most recent call last): ... SplittingFieldAbort: degree of splitting field is a multiple of 180 Use the ``degree_divisor`` attribute to recover the divisor of the degree of the splitting field or ``degree_multiple`` to recover a multiple:: sage: from sage.rings.number_field.splitting_field import SplittingFieldAbort sage: try: # long time (4s on sage.math, 2014) ....: (x^8+x+1).splitting_field('b', abort_degree=60, simplify=False) ....: except SplittingFieldAbort as e: ....: print(e.degree_divisor) ....: print(e.degree_multiple) 120 1440 TESTS:: sage: from sage.rings.number_field.splitting_field import splitting_field sage: splitting_field(polygen(QQ), name='x', map=True, simplify_all=True) (Number Field in x with defining polynomial x, Ring morphism: From: Rational Field To: Number Field in x with defining polynomial x Defn: 1 |--> 1) """ from sage.misc.all import cputime from sage.misc.verbose import verbose degree_multiple = Integer(degree_multiple or 0) abort_degree = Integer(abort_degree or 0) # Kpol = PARI polynomial in y defining the extension found so far F = poly.base_ring() if is_RationalField(F): Kpol = pari("'y") else: Kpol = F.pari_polynomial("y") # Fgen = the generator of F as element of Q[y]/Kpol # (only needed if map=True) if map: Fgen = F.gen().__pari__() verbose("Starting field: %s" % Kpol) # L and Lred are lists of SplittingData. # L contains polynomials which are irreducible over K, # Lred contains polynomials which need to be factored. L = [] Lred = [SplittingData(poly._pari_with_name(), degree_multiple)] # Main loop, handle polynomials one by one while True: # Absolute degree of current field K absolute_degree = Integer(Kpol.poldegree()) # Compute minimum relative degree of splitting field rel_degree_divisor = Integer(1) for splitting in L: rel_degree_divisor = rel_degree_divisor.lcm(splitting.poldegree()) # Check for early aborts abort_rel_degree = abort_degree // absolute_degree if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort(absolute_degree * rel_degree_divisor, degree_multiple) # First, factor polynomials in Lred and store the result in L verbose("SplittingData to factor: %s" % [s._repr_tuple() for s in Lred]) t = cputime() for splitting in Lred: m = splitting.dm.gcd(degree_multiple).gcd( factorial(splitting.poldegree())) if m == 1: continue factors = Kpol.nffactor(splitting.pol)[0] for q in factors: d = q.poldegree() fac = factorial(d) # Multiple of the degree of the splitting field of q, # note that the degree equals fac iff the Galois group is S_n. mq = m.gcd(fac) if mq == 1: continue # Multiple of the degree of the splitting field of q # over the field defined by adding square root of the # discriminant. # If the Galois group is contained in A_n, then mq_alt is # also the degree multiple over the current field K. # Here, we have equality if the Galois group is A_n. mq_alt = mq.gcd(fac // 2) # If we are over Q, then use PARI's polgalois() to compute # these degrees exactly. if absolute_degree == 1: try: G = q.polgalois() except PariError: pass else: mq = Integer(G[0]) mq_alt = mq // 2 if (G[1] == -1) else mq # In degree 4, use the cubic resolvent to refine the # degree bounds. if d == 4 and mq >= 12: # mq equals 12 or 24 # Compute cubic resolvent a0, a1, a2, a3, a4 = (q / q.pollead()).Vecrev() assert a4 == 1 cubicpol = pari([ 4 * a0 * a2 - a1 * a1 - a0 * a3 * a3, a1 * a3 - 4 * a0, -a2, 1 ]).Polrev() cubicfactors = Kpol.nffactor(cubicpol)[0] if len(cubicfactors) == 1: # A4 or S4 # After adding a root of the cubic resolvent, # the degree of the extension defined by q # is a factor 3 smaller. L.append(SplittingData(cubicpol, 3)) rel_degree_divisor = rel_degree_divisor.lcm(3) mq = mq // 3 # 4 or 8 mq_alt = 4 elif len(cubicfactors) == 2: # C4 or D8 # The irreducible degree 2 factor is # equivalent to x^2 - q.poldisc(). discpol = cubicfactors[1] L.append(SplittingData(discpol, 2)) mq = mq_alt = 4 else: # C2 x C2 mq = mq_alt = 4 if mq > mq_alt >= 3: # Add quadratic resolvent x^2 - D to decrease # the degree multiple by a factor 2. discpol = pari([-q.poldisc(), 0, 1]).Polrev() discfactors = Kpol.nffactor(discpol)[0] if len(discfactors) == 1: # Discriminant is not a square L.append(SplittingData(discpol, 2)) rel_degree_divisor = rel_degree_divisor.lcm(2) mq = mq_alt L.append(SplittingData(q, mq)) rel_degree_divisor = rel_degree_divisor.lcm(q.poldegree()) if abort_rel_degree and rel_degree_divisor > abort_rel_degree: raise SplittingFieldAbort( absolute_degree * rel_degree_divisor, degree_multiple) verbose("Done factoring", t, level=2) if len(L) == 0: # Nothing left to do break # Recompute absolute degree multiple new_degree_multiple = absolute_degree for splitting in L: new_degree_multiple *= splitting.dm degree_multiple = new_degree_multiple.gcd(degree_multiple) # Absolute degree divisor degree_divisor = rel_degree_divisor * absolute_degree # Sort according to degree to handle low degrees first L.sort(key=lambda x: x.key()) verbose("SplittingData to handle: %s" % [s._repr_tuple() for s in L]) verbose("Bounds for absolute degree: [%s, %s]" % (degree_divisor, degree_multiple)) # Check consistency if degree_multiple % degree_divisor != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") for splitting in L: # The degree of the splitting field must be a multiple of # the degree of the polynomial. Only do this check for # SplittingData with minimal dm, because the higher dm are # defined as relative degree over the splitting field of # the polynomials with lesser dm. if splitting.dm > L[0].dm: break if splitting.dm % splitting.poldegree() != 0: raise ValueError( "inconsistent degree_multiple in splitting_field()") # Add a root of f = L[0] to construct the field N = K[x]/f(x) splitting = L[0] f = splitting.pol verbose("Handling polynomial %s" % (f.lift()), level=2) t = cputime() Npol, KtoN, k = Kpol.rnfequation(f, flag=1) # Make Npol monic integral primitive, store in Mpol # (after this, we don't need Npol anymore, only Mpol) Mdiv = pari(1) Mpol = Npol while True: denom = Integer(Mpol.pollead()) if denom == 1: break denom = pari(denom.factor().radical_value()) Mpol = (Mpol * (denom**Mpol.poldegree())).subst( "x", pari([0, 1 / denom]).Polrev("x")) Mpol /= Mpol.content() Mdiv *= denom # We are finished for sure if we hit the degree bound finished = (Mpol.poldegree() >= degree_multiple) if simplify_all or (simplify and not finished): # Find a simpler defining polynomial Lpol for Mpol verbose("New field before simplifying: %s" % Mpol, t) t = cputime() M = Mpol.polred(flag=3) n = len(M[0]) - 1 Lpol = M[1][n].change_variable_name("y") LtoM = M[0][n].change_variable_name("y").Mod( Mpol.change_variable_name("y")) MtoL = LtoM.modreverse() else: # Lpol = Mpol Lpol = Mpol.change_variable_name("y") MtoL = pari("'y") NtoL = MtoL / Mdiv KtoL = KtoN.lift().subst("x", NtoL).Mod(Lpol) Kpol = Lpol # New Kpol (for next iteration) verbose("New field: %s" % Kpol, t) if map: t = cputime() Fgen = Fgen.lift().subst("y", KtoL) verbose("Computed generator of F in K", t, level=2) if finished: break t = cputime() # Convert f and elements of L from K to L and store in L # (if the polynomial is certain to remain irreducible) or Lred. Lold = L[1:] L = [] Lred = [] # First add f divided by the linear factor we obtained, # mg is the new degree multiple. mg = splitting.dm // f.poldegree() if mg > 1: g = [c.subst("y", KtoL).Mod(Lpol) for c in f.Vecrev().lift()] g = pari(g).Polrev() g /= pari([k * KtoL - NtoL, 1]).Polrev() # divide linear factor Lred.append(SplittingData(g, mg)) for splitting in Lold: g = [c.subst("y", KtoL) for c in splitting.pol.Vecrev().lift()] g = pari(g).Polrev() mg = splitting.dm if Integer(g.poldegree()).gcd( f.poldegree()) == 1: # linearly disjoint fields L.append(SplittingData(g, mg)) else: Lred.append(SplittingData(g, mg)) verbose("Converted polynomials to new field", t, level=2) # Convert Kpol to Sage and construct the absolute number field Kpol = PolynomialRing(RationalField(), name=poly.variable_name())(Kpol / Kpol.pollead()) K = NumberField(Kpol, name) if map: return K, F.hom(Fgen, K) else: return K
def local_coordinates(self, pt, n): r""" Return local coordinates to precision n at the given point. Behaviour is flaky - some choices of `n` are worst that others. INPUT: - ``pt`` - an F-rational point on X which is not a point of ramification for the projection (x,y) - x. - ``n`` - the number of terms desired OUTPUT: x = x0 + t y = y0 + power series in t EXAMPLES:: sage: FF = FiniteField(5) sage: P2 = ProjectiveSpace(2, FF, names = ['x','y','z']) sage: x, y, z = P2.coordinate_ring().gens() sage: C = Curve(y^2*z^7-x^9-x*z^8) sage: pt = C([2,3,1]) sage: C.local_coordinates(pt,9) # todo: not implemented !!!! [2 + t, 3 + 3*t^2 + t^3 + 3*t^4 + 3*t^6 + 3*t^7 + t^8 + 2*t^9 + 3*t^11 + 3*t^12] """ f = self.defining_polynomial() R = f.parent() F = self.base_ring() p = F.characteristic() x0 = F(pt[0]) y0 = F(pt[1]) astr = ["a" + str(i) for i in range(1, 2 * n)] x, y = R.gens() R0 = PolynomialRing(F, 2 * n + 2, names=[str(x), str(y), "t"] + astr) vars0 = R0.gens() t = vars0[2] yt = y0 * t**0 + add( [vars0[i] * t**(i - 2) for i in range(3, 2 * n + 2)]) xt = x0 + t ft = f(xt, yt) S = singular S.eval('ring s = ' + str(p) + ',' + str(R0.gens()) + ',lp;') S.eval('poly f = ' + str(ft)) cmd = 'matrix c = coeffs (' + str(ft) + ',t)' S.eval(cmd) N = int(S.eval('size(c)')) b = ["c[" + str(i) + ",1]," for i in range(2, N / 2 - 4)] b = ''.join(b) b = b[:len(b) - 1] #to cut off the trailing comma cmd = 'ideal I = ' + b S.eval(cmd) c = S.eval('slimgb(I)') d = c.split("=") d = d[1:] d[len(d) - 1] += "\n" e = [x[:x.index("\n")] for x in d] vals = [] for x in e: for y in vars0: if str(y) in x: if len(x.replace(str(y), "")) != 0: i = x.find("-") if i > 0: vals.append( [eval(x[1:i]), x[:i], F(eval(x[i + 1:]))]) i = x.find("+") if i > 0: vals.append( [eval(x[1:i]), x[:i], -F(eval(x[i + 1:]))]) else: vals.append([eval(str(y)[1:]), str(y), F(0)]) vals.sort() k = len(vals) v = [x0 + t, y0 + add([vals[i][2] * t**(i + 1) for i in range(k)])] return v
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 rational_points_iterator(self): r""" Return a generator object for the rational points on this curve. INPUT: - ``self`` -- a projective curve OUTPUT: A generator of all the rational points on the curve defined over its base field. EXAMPLE:: sage: F = GF(37) sage: P2.<X,Y,Z> = ProjectiveSpace(F,2) sage: C = Curve(X^7+Y*X*Z^5*55+Y^7*12) sage: len(list(C.rational_points_iterator())) 37 :: sage: F = GF(2) sage: P2.<X,Y,Z> = ProjectiveSpace(F,2) sage: C = Curve(X*Y*Z) sage: a = C.rational_points_iterator() sage: a.next() (1 : 0 : 0) sage: a.next() (0 : 1 : 0) sage: a.next() (1 : 1 : 0) sage: a.next() (0 : 0 : 1) sage: a.next() (1 : 0 : 1) sage: a.next() (0 : 1 : 1) sage: a.next() Traceback (most recent call last): ... StopIteration :: sage: F = GF(3^2,'a') sage: P2.<X,Y,Z> = ProjectiveSpace(F,2) sage: C = Curve(X^3+5*Y^2*Z-33*X*Y*X) sage: b = C.rational_points_iterator() sage: b.next() (0 : 1 : 0) sage: b.next() (0 : 0 : 1) sage: b.next() (2*a + 2 : a : 1) sage: b.next() (2 : a + 1 : 1) sage: b.next() (a + 1 : 2*a + 1 : 1) sage: b.next() (1 : 2 : 1) sage: b.next() (2*a + 2 : 2*a : 1) sage: b.next() (2 : 2*a + 2 : 1) sage: b.next() (a + 1 : a + 2 : 1) sage: b.next() (1 : 1 : 1) sage: b.next() Traceback (most recent call last): ... StopIteration """ g = self.defining_polynomial() K = g.parent().base_ring() from sage.rings.polynomial.all import PolynomialRing R = PolynomialRing(K, 'X') X = R.gen() one = K.one_element() zero = K.zero_element() # the point with Z = 0 = Y try: t = self.point([one, zero, zero]) yield (t) except TypeError: pass # points with Z = 0, Y = 1 g10 = R(g(X, one, zero)) if g10.is_zero(): for x in K: yield (self.point([x, one, zero])) else: for x in g10.roots(multiplicities=False): yield (self.point([x, one, zero])) # points with Z = 1 for y in K: gy1 = R(g(X, y, one)) if gy1.is_zero(): for x in K: yield (self.point([x, y, one])) else: for x in gy1.roots(multiplicities=False): yield (self.point([x, y, one]))