def replace_p(P, listP, FractionField): """ Take a rational fraction P and a list of rational fractions and try to find the simplest product or quotient """ if P == 0: return P Q = FractionField(P) d = Q.numerator().degree() + Q.denominator().degree() case = 0 ind = -1 for i, PP in enumerate(listP): PP = FractionField(PP) if not (PP == P): QQ1 = P * PP dd1 = QQ1.numerator().degree() + QQ1.denominator().degree() if dd1 < d: Q = QQ1 d = Q.numerator().degree() + Q.denominator().degree() case = 1 ind = i QQ2 = PP / P dd2 = QQ2.numerator().degree() + QQ2.denominator().degree() if dd2 < d: Q = QQ2 d = Q.numerator().degree() + Q.denominator().degree() case = -1 ind = i return FractionField(Q), [case, ind]
def canonical_scheme(self, t=None): """ Return the canonical scheme. This is a scheme that contains this hypergeometric motive in its cohomology. EXAMPLES:: sage: from sage.modular.hypergeometric_motive import HypergeometricData as Hyp sage: H = Hyp(cyclotomic=([3],[4])) sage: H.gamma_list() [-1, 2, 3, -4] sage: H.canonical_scheme() Spectrum of Quotient of Multivariate Polynomial Ring in X0, X1, Y0, Y1 over Fraction Field of Univariate Polynomial Ring in t over Rational Field by the ideal (X0 + X1 - 1, Y0 + Y1 - 1, (-t)*X0^2*X1^3 + 27/64*Y0*Y1^4) sage: H = Hyp(gamma_list=[-2, 3, 4, -5]) sage: H.canonical_scheme() Spectrum of Quotient of Multivariate Polynomial Ring in X0, X1, Y0, Y1 over Fraction Field of Univariate Polynomial Ring in t over Rational Field by the ideal (X0 + X1 - 1, Y0 + Y1 - 1, (-t)*X0^3*X1^4 + 1728/3125*Y0^2*Y1^5) REFERENCES: [Kat1991]_, section 5.4 """ if t is None: t = FractionField(QQ['t']).gen() basering = t.parent() gamma_pos = [u for u in self.gamma_list() if u > 0] gamma_neg = [u for u in self.gamma_list() if u < 0] N_pos = len(gamma_pos) N_neg = len(gamma_neg) varX = ['X{}'.format(i) for i in range(N_pos)] varY = ['Y{}'.format(i) for i in range(N_neg)] ring = PolynomialRing(basering, varX + varY) gens = ring.gens() X = gens[:N_pos] Y = gens[N_pos:] eq0 = ring.sum(X) - 1 eq1 = ring.sum(Y) - 1 eq2_pos = ring.prod(X[i] ** gamma_pos[i] for i in range(N_pos)) eq2_neg = ring.prod(Y[j] ** -gamma_neg[j] for j in range(N_neg)) ideal = ring.ideal([eq0, eq1, self.M_value() * eq2_neg - t * eq2_pos]) return Spec(ring.quotient(ideal))
def compacte_liste(listP, FractionField): """ Take a list of rational fractions and return the an equivalent list with minimal degrees. """ check = matrix.zero(ZZ, len(listP)) Res = [P for P in listP] for i, P in enumerate(listP): P = FractionField(P) Q, [case, ind] = replace_p(P, listP, FractionField) check[i, i] = 1 if case != 0: check2 = check check2[i, ind] = case if check2.rank() < check.rank( ): #On vérifie qu'on n'oublie pas une équation! Q = P else: check = check2 if not (Q in Res): Res.remove(P) if Q.numerator().degree() + Q.denominator().degree() == 0: #(in case we had to proportional equations pass else: Res += [Q] return Res
def __init__(self, polynomial_ring, category): r""" Create the ring of CFiniteSequences over ``base_ring`` INPUT: - ``base_ring`` -- the base ring for the o.g.f (either ``QQ`` or ``ZZ``) - ``names`` -- an iterable of variables (should contain only one variable) - ``category`` -- the category of the ring (default: ``Fields()``) TESTS:: sage: C.<y> = CFiniteSequences(QQ); C The ring of C-Finite sequences in y over Rational Field sage: C.<x> = CFiniteSequences(QQ); C The ring of C-Finite sequences in x over Rational Field sage: C.<x> = CFiniteSequences(ZZ); C The ring of C-Finite sequences in x over Integer Ring sage: C.<x,y> = CFiniteSequences(ZZ) Traceback (most recent call last): ... NotImplementedError: Multidimensional o.g.f. not implemented. sage: C.<x> = CFiniteSequences(CC) Traceback (most recent call last): ... ValueError: O.g.f. base not rational. """ base_ring = polynomial_ring.base_ring() self._polynomial_ring = polynomial_ring self._fraction_field = FractionField(self._polynomial_ring) CommutativeRing.__init__(self, base_ring, self._polynomial_ring.gens(), category)
def local_height(self, v, prec=None): r""" Returns the maximum of the local height of the coordinates of this point. INPUT: - ``v`` -- a prime or prime ideal of the base ring. - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - a real number. EXAMPLES:: sage: P.<x,y,z>= ProjectiveSpace(QQ,2) sage: Q = P.point([4,4,1/150], False) sage: Q.local_height(5) 3.21887582486820 :: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: Q = P([4, 1, 30]) sage: Q.local_height(2) 0.693147180559945 """ K = FractionField(self.domain().base_ring()) if K not in _NumberFields: raise TypeError("must be over a number field or a number field order") return max([K(c).local_height(v, prec=prec) for c in self])
def find_monom_eqs(sys): """ Take a list of polynomials (seen as equations P=0 and returns the list A/B where P = A-B and A, B are monomials """ if sys == []: return sys P = sys[0] Ring = P.parent() # On vérifie le type for Q in sys[1:]: if not (Q.parent() == Ring): print "Problème de type" return [] Monom = [] Others = [] Field = FractionField(Ring) for P in sys: MON = P.monomials() if len(MON) == 2: Monom.append( Field(-P.monomial_coefficient(MON[0]) / P.monomial_coefficient(MON[1]) * MON[0] / MON[1])) else: Others.append(P) return Monom, Others, Field
def __hash__(self): """ Compute the hash value of this point. If the base ring has a fraction field, normalize the point in the fraction field and then hash so that equal points have equal hash values. If the base ring is not an integral domain, return the hash of the parent. OUTPUT: Integer. EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(ZZ, 1) sage: hash(P([1, 1])) 1300952125 # 32-bit 3713081631935493181 # 64-bit sage: hash(P.point([2, 2], False)) 1300952125 # 32-bit 3713081631935493181 # 64-bit :: sage: R.<x> = PolynomialRing(QQ) sage: K.<w> = NumberField(x^2 + 3) sage: O = K.maximal_order() sage: P.<x,y> = ProjectiveSpace(O, 1) sage: hash(P([1+w, 2])) -1562365407 # 32-bit 1251212645657227809 # 64-bit sage: hash(P([2, 1-w])) -1562365407 # 32-bit 1251212645657227809 # 64-bit TESTS:: sage: P.<x,y> = ProjectiveSpace(Zmod(10), 1) sage: Q.<x,y> = ProjectiveSpace(Zmod(10), 1) sage: hash(P([2, 5])) == hash(Q([2, 5])) True sage: hash(P([2, 5])) == hash(P([2,5])) True sage: hash(P([3, 7])) == hash(P([2,5])) True """ R = self.codomain().base_ring() #if there is a fraction field normalize the point so that #equal points have equal hash values if R in IntegralDomains(): P = self.change_ring(FractionField(R)) P.normalize_coordinates() return hash(tuple(P)) #if there is no good way to normalize return #a constant value return hash(self.codomain())
def __hash__(self): """ Computes the hash value of this point. OUTPUT: Integer. EXAMPLES:: sage: PP = ProductProjectiveSpaces(Zmod(6), [1, 1]) sage: hash(PP([5, 1, 2, 4])) 1266382469 # 32-bit -855399699883264379 # 64-bit :: sage: PP = ProductProjectiveSpaces(ZZ, [1, 2]) sage: hash(PP([1, 1, 2, 2, 2])) 805439612 # 32-bit 7267864846446758012 # 64-bit sage: hash(PP([1, 1, 1, 1, 1])) 805439612 # 32-bit 7267864846446758012 # 64-bit :: sage: PP = ProductProjectiveSpaces(QQ, [1, 1]) sage: hash(PP([1/7, 1, 2, 1])) 1139616004 # 32-bit -7585172175017137916 # 64-bit :: sage: PP = ProductProjectiveSpaces(GF(7), [1, 1, 1]) sage: hash(PP([4, 1, 5, 4, 6, 1])) 1796924635 # 32-bit -4539377540667874085 # 64-bit """ R = self.codomain().base_ring() # if there is a fraction field normalize the point so that # equal points have equal hash values if R in IntegralDomains(): P = self.change_ring(FractionField(R)) P.normalize_coordinates() return hash(tuple(P)) # if there is no good way to normalize return # a constant value return hash(self.codomain())
def local_height(self, v, prec=None): r""" Returns the maximum of the local height of the coefficients in any of the coordinate functions of this map. INPUT: - ``v`` -- a prime or prime ideal of the base ring. - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - a real number. EXAMPLES:: sage: T.<x,y,z,w,u> = ProductProjectiveSpaces([2, 1], QQ) sage: H = T.Hom(T) sage: f = H([4*x^2+3/100*y^2, 8/210*x*y, 1/10000*z^2, 20*w^2, 1/384*u*w]) sage: f.local_height(2) 4.85203026391962 :: sage: R.<z> = PolynomialRing(QQ) sage: K.<w> = NumberField(z^2-5) sage: P.<x,y,a,b> = ProductProjectiveSpaces([1, 1], K) sage: H = Hom(P,P) sage: f = H([2*x^2 + w/3*y^2, 1/w*y^2, a^2, 6*b^2 + 1/9*a*b]) sage: f.local_height(K.ideal(3)) 2.19722457733622 """ K = FractionField(self.domain().base_ring()) if K not in NumberFields(): raise TypeError( "must be over a number field or a number field order") H = 0 for i in range(self.domain().ambient_space().ngens()): C = self[i].coefficients() h = max(K(c).local_height(v, prec) for c in C) H = max(H, h) return H
def __hash__(self): """ Compute the hash value of this point. OUTPUT: Integer. EXAMPLES:: sage: PP = ProductProjectiveSpaces(Zmod(6), [1, 1]) sage: H = hash(PP([5, 1, 2, 4])) :: sage: PP = ProductProjectiveSpaces(ZZ, [1, 2]) sage: hash(PP([1, 1, 2, 2, 2])) == hash(PP([1, 1, 1, 1, 1])) True :: sage: PP = ProductProjectiveSpaces(QQ, [1, 1]) sage: hash(PP([1/7, 1, 2, 1])) == hash((1/7, 1, 2, 1)) True :: sage: PP = ProductProjectiveSpaces(GF(7), [1, 1, 1]) sage: hash(PP([4, 1, 5, 4, 6, 1])) == hash((4, 1, 5, 4, 6, 1)) False sage: hash(PP([4, 1, 5, 4, 6, 1])) == hash((4, 1, 3, 1, 6, 1)) True """ R = self.codomain().base_ring() # if there is a fraction field normalize the point so that # equal points have equal hash values if R in IntegralDomains(): P = self.change_ring(FractionField(R)) P.normalize_coordinates() return hash(tuple(P)) # if there is no good way to normalize return # a constant value return hash(self.codomain())
def local_height(self, v, prec=None): r""" Return the maximum of the local height of the coordinates of this point. This function computes the maximum of local height of each component point in the product. Local height of component point is computed using function for projective point. INPUT: - ``v`` -- a prime or prime ideal of the base ring. - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - a real number. EXAMPLES:: sage: PP = ProductProjectiveSpaces(QQ, [1, 1], 'x') sage: A = PP([11, 5, 10, 2]) sage: A.local_height(5) 1.60943791243410 :: sage: P = ProductProjectiveSpaces(QQ, [1,2], 'x') sage: Q = P([1, 4, 1/2, 2, 32]) sage: Q.local_height(2) 4.15888308335967 """ K = FractionField(self.domain().base_ring()) if K not in NumberFields(): raise TypeError( "must be over a number field or a number field order") n = self.codomain().ambient_space().num_components() return max(self[i].local_height(v, prec=prec) for i in range(n))
def local_height_arch(self, i, prec=None): r""" Returns the maximum of the local heights at the ``i``-th infinite place of this point. INPUT: - ``i`` -- an integer. - ``prec`` -- desired floating point precision (default: default RealField precision). OUTPUT: - a real number. EXAMPLES:: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: Q = P.point([4, 4, 1/150], False) sage: Q.local_height_arch(0) 1.38629436111989 :: sage: P.<x,y,z> = ProjectiveSpace(QuadraticField(5, 'w'), 2) sage: Q = P.point([4, 1, 30], False) sage: Q.local_height_arch(1) 3.401197381662155375413236691607 """ K = FractionField(self.domain().base_ring()) if K not in _NumberFields: raise TypeError( "must be over a number field or a number field order") if K == QQ: return max(K(c).local_height_arch(prec=prec) for c in self) else: return max(K(c).local_height_arch(i, prec=prec) for c in self)
def const_sys(self,n): """ self is a Defor manifold. Return the system (a DeforSystem) defining its Deformation Variety together with inequations. """ Eq=self.manifold.gluing_equations_pgl(N=n,equation_type='non_peripheral')#On récupère les equations d'aretes et de faces Mat=Eq.matrix;#c'est la matrice des exposants dans les equations variabl=Eq.explain_columns;#On récupère les variables ring=PolynomialRing(QQ,variabl);#On définit l'anneau des polynômes où on travaille field = FractionField(ring) ineqs = set([x for x in ring.gens()]+[x-1 for x in ring.gens()]); proj = {}; sys1 = [ construire_equation(r,ring) for r in Mat.rows() ]; sys2=[ring.gens()[3*i]*ring.gens()[3*i+1]*ring.gens()[3*i+2] +1 for i in range(len(variabl)/3)]; sys3=[ring.gens()[3*i]*(1-ring.gens()[3*i+2])-1 for i in range(len(variabl)/3)] +\ [ring.gens()[3*i+1]*(1-ring.gens()[3*i])-1 for i in range(len(variabl)/3)] +\ [ring.gens()[3*i+2]*(1-ring.gens()[3*i+1])-1 for i in range(len(variabl)/3)] eqs = sys1 + sys2 + sys3 return deforsys.DeforSystem(eqs,ineqs,proj,self.manifold,n)
def green_function(self, G, v, **kwds): r""" Evaluates the local Green's function with respect to the morphism ``G`` at the place ``v`` for ``self`` with ``N`` terms of the series or to within a given error bound. Must be over a number field or order of a number field. Note that this is the absolute local Green's function so is scaled by the degree of the base field. Use ``v=0`` for the archimedean place over `\QQ` or field embedding. Non-archimedean places are prime ideals for number fields or primes over `\QQ`. ALGORITHM: See Exercise 5.29 and Figure 5.6 of ``The Arithmetic of Dynamics Systems``, Joseph H. Silverman, Springer, GTM 241, 2007. INPUT: - ``G`` - a projective morphism whose local Green's function we are computing - ``v`` - non-negative integer. a place, use v=0 for the archimedean place kwds: - ``N`` - positive integer. number of terms of the series to use, default: 10 - ``prec`` - positive integer, float point or p-adic precision, default: 100 - ``error_bound`` - a positive real number OUTPUT: - a real number EXAMPLES:: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,x*y]); sage: Q=P(5,1) sage: f.green_function(Q,0,N=30) 1.6460930159932946233759277576 :: sage: P.<x,y>=ProjectiveSpace(QQ,1) sage: H=Hom(P,P) sage: f=H([x^2+y^2,x*y]); sage: Q=P(5,1) sage: Q.green_function(f,0,N=200,prec=200) 1.6460930160038721802875250367738355497198064992657997569827 :: sage: K.<w> = QuadraticField(3) sage: P.<x,y> = ProjectiveSpace(K,1) sage: H = Hom(P,P) sage: f = H([17*x^2+1/7*y^2,17*w*x*y]) sage: f.green_function(P.point([w,2],False), K.places()[1]) 1.7236334013785676107373093775 sage: print f.green_function(P([2,1]), K.ideal(7), N=7) 0.48647753726382832627633818586 sage: print f.green_function(P([w,1]), K.ideal(17), error_bound=0.001) -0.70761163353747779889947530309 .. TODO:: Implement general p-adic extensions so that the flip trick can be used for number fields. """ N = kwds.get('N', 10) #Get number of iterates (if entered) err = kwds.get('error_bound', None) #Get error bound (if entered) prec = kwds.get('prec', 100) #Get precision (if entered) R = RealField(prec) localht = R(0) BR = FractionField(self.codomain().base_ring()) GBR = G.change_ring(BR) #so the heights work if not BR in NumberFields(): raise NotImplementedError("Must be over a NumberField or a NumberField Order") #For QQ the 'flip-trick' works better over RR or Qp if isinstance(v, (NumberFieldFractionalIdeal, RingHomomorphism_im_gens)): K = BR elif is_prime(v): K = Qp(v, prec) elif v == 0: K = R v = BR.places(prec=prec)[0] else: raise ValueError("Invalid valuation (=%s) entered."%v) #Coerce all polynomials in F into polynomials with coefficients in K F = G.change_ring(K, False) d = F.degree() dim = F.codomain().ambient_space().dimension_relative() P = self.change_ring(K, False) if err is not None: err = R(err) if not err>0: raise ValueError("Error bound (=%s) must be positive."%err) if G.is_endomorphism() == False: raise NotImplementedError("Error bounds only for endomorphisms") #if doing error estimates, compute needed number of iterates D = (dim + 1) * (d - 1) + 1 #compute upper bound if isinstance(v, RingHomomorphism_im_gens): #archimedean vindex = BR.places(prec=prec).index(v) U = GBR.local_height_arch(vindex, prec=prec) + R(binomial(dim + d, d)).log() else: #non-archimedean U = GBR.local_height(v, prec=prec) #compute lower bound - from explicit polynomials of Nullstellensatz CR = GBR.codomain().ambient_space().coordinate_ring() #.lift() only works over fields I = CR.ideal(GBR.defining_polynomials()) maxh = 0 for k in range(dim + 1): CoeffPolys = (CR.gen(k) ** D).lift(I) Res = 1 h = 1 for poly in CoeffPolys: if poly != 0: for c in poly.coefficients(): Res = lcm(Res, c.denominator()) for poly in CoeffPolys: if poly != 0: if isinstance(v, RingHomomorphism_im_gens): #archimedean if BR == QQ: h = max([(Res*c).local_height_arch(prec=prec) for c in poly.coefficients()]) else: h = max([(Res*c).local_height_arch(vindex, prec=prec) for c in poly.coefficients()]) else: #non-archimedean h = max([c.local_height(v, prec=prec) for c in poly.coefficients()]) if h > maxh: maxh=h if isinstance(v, RingHomomorphism_im_gens): #archimedean L = R(Res / ((dim + 1) * binomial(dim + D - d, D - d) * maxh)).log().abs() else: #non-archimedean L = R(1 / maxh).log().abs() C = max([U, L]) if C != 0: N = R(C/(err)).log(d).abs().ceil() else: #we just need log||P||_v N=1 #START GREEN FUNCTION CALCULATION if isinstance(v, RingHomomorphism_im_gens): #embedding for archimedean local height for i in range(N+1): Pv = [ (v(t).abs()) for t in P ] m = -1 #compute the maximum absolute value of entries of a, and where it occurs for n in range(dim + 1): if Pv[n] > m: j = n m = Pv[n] # add to sum for the Green's function localht += ((1/R(d))**R(i)) * (R(m).log()) #get the next iterate if i < N: P.scale_by(1/P[j]) P = F(P, False) return (1/BR.absolute_degree()) * localht #else - prime or prime ideal for non-archimedean for i in range(N + 1): if BR == QQ: Pv = [ R(K(t).abs()) for t in P ] else: Pv = [ R(t.abs_non_arch(v)) for t in P ] m = -1 #compute the maximum absolute value of entries of a, and where it occurs for n in range(dim + 1): if Pv[n] > m: j = n m = Pv[n] # add to sum for the Green's function localht += ((1/R(d))**R(i)) * (R(m).log()) #get the next iterate if i < N: P.scale_by(1/P[j]) P = F(P, False) return (1/BR.absolute_degree()) * localht
def multiplier(self, P, n, check=True): r""" Returns the multiplier of the point ``P`` of period ``n`` by the map. The map must be an endomorphism. INPUT: - ``P`` - a point on domain of the map. - ``n`` - a positive integer, the period of ``P``. - ``check`` -- verify that ``P`` has period ``n``, Default:True. OUTPUT: - a square matrix of size ``self.codomain().dimension_relative()`` in the ``base_ring`` of the map. EXAMPLES:: sage: P.<x,y> = AffineSpace(QQ, 2) sage: H = End(P) sage: f = H([x^2, y^2]) sage: f.multiplier(P([1, 1]), 1) [2 0] [0 2] :: sage: P.<x,y,z> = AffineSpace(QQ, 3) sage: H = End(P) sage: f = H([x, y^2, z^2 - y]) sage: f.multiplier(P([1/2, 1, 0]), 2) [1 0 0] [0 4 0] [0 0 0] :: sage: P.<x> = AffineSpace(CC, 1) sage: H = End(P) sage: f = H([x^2 + 1/2]) sage: f.multiplier(P([0.5 + 0.5*I]), 1) [1.00000000000000 + 1.00000000000000*I] :: sage: R.<t> = PolynomialRing(CC, 1) sage: P.<x> = AffineSpace(R, 1) sage: H = End(P) sage: f = H([x^2 - t^2 + t]) sage: f.multiplier(P([-t + 1]), 1) [(-2.00000000000000)*t + 2.00000000000000] :: sage: P.<x,y> = AffineSpace(QQ, 2) sage: X = P.subscheme([x^2-y^2]) sage: H = End(X) sage: f = H([x^2, y^2]) sage: f.multiplier(X([1, 1]), 1) [2 0] [0 2] """ if not self.is_endomorphism(): raise TypeError("must be an endomorphism") if check: if self.nth_iterate(P, n) != P: raise ValueError("%s is not periodic of period %s" % (P, n)) if n < 1: raise ValueError("period must be a positive integer") N = self.domain().ambient_space().dimension_relative() l = identity_matrix(FractionField(self.codomain().base_ring()), N, N) Q = P J = self.jacobian() for i in range(0, n): R = self(Q) l = J(tuple(Q)) * l #chain rule matrix multiplication Q = R return l
def rational_diagonal_form(self, return_matrix=False): """ Returns a diagonal form equivalent to Q over the fraction field of its defining ring. If the return_matrix is True, then we return the transformation matrix performing the diagonalization as the second argument. INPUT: none OUTPUT: Q -- the diagonalized form of this quadratic form (optional) T -- matrix which diagonalizes Q (over it's fraction field) EXAMPLES:: sage: Q = QuadraticForm(ZZ, 2, [0,1,-1]) sage: Q Quadratic form in 2 variables over Integer Ring with coefficients: [ 0 1 ] [ * -1 ] sage: Q.rational_diagonal_form() Quadratic form in 2 variables over Rational Field with coefficients: [ -2 0 ] [ * 1/8 ] :: sage: Q = DiagonalQuadraticForm(ZZ, [1,3,5,7]) sage: Q.rational_diagonal_form(return_matrix=True) ( Quadratic form in 4 variables over Rational Field with coefficients: [ 1 0 0 0 ] [ * 3 0 0 ] [ * * 5 0 ] [ * * * 7 ] , <BLANKLINE> [1 0 0 0] [0 1 0 0] [0 0 1 0] [0 0 0 1] ) :: sage: Q1 = QuadraticForm(ZZ, 4, [1, 1, 0, 0, 1, 0, 0, 1, 0, 18]) sage: Q1 Quadratic form in 4 variables over Integer Ring with coefficients: [ 1 1 0 0 ] [ * 1 0 0 ] [ * * 1 0 ] [ * * * 18 ] sage: Q1.rational_diagonal_form(return_matrix=True) ( Quadratic form in 4 variables over Rational Field with coefficients: [ 1 0 0 0 ] [ * 3/4 0 0 ] [ * * 1 0 ] [ * * * 18 ] , <BLANKLINE> [ 1 -1/2 0 0] [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1] ) """ n = self.dim() Q = copy.deepcopy(self) Q.__init__(FractionField(self.base_ring()), self.dim(), self.coefficients()) MS = MatrixSpace(Q.base_ring(), n, n) T = MS(1) ## Clear the entries one row at a time. for i in range(n): ## Deal with rows where the diagonal entry is zero. if Q[i, i] == 0: ## Look for a non-zero entry and use it to make the diagonal non-zero (if it exists) for j in range(i + 1, n): if Q[i, j] != 0: temp = MS(1) if Q[i, j] + Q[j, j] == 0: temp[j, i] = -1 else: temp[j, i] = 1 ## Apply the transformation Q = Q(temp) T = T * temp break ## Create a matrix which deals with off-diagonal entries (all at once for each row) temp = MS(1) for j in range(i + 1, n): if Q[i, j] != 0: temp[i, j] = -Q[i, j] / ( Q[i, i] * 2 ) ## This should only occur when Q[i,i] != 0, which the above step guarantees. Q = Q(temp) T = T * temp ## Return the appropriate output if return_matrix: return Q, T else: return Q
def reynolds_operator(self, poly, chi=None): r""" Compute the Reynolds operator of this finite group `G`. This is the projection from a polynomial ring to the ring of relative invariants [Stu1993]_. If possible, the invariant is returned defined over the base field of the given polynomial ``poly``, otherwise, it is returned over the compositum of the fields involved in the computation. Only implemented for absolute fields. ALGORITHM: Let `K[x]` be a polynomial ring and `\chi` a linear character for `G`. Let .. MATH: K[x]^G_{\chi} = \{f \in K[x] | \pi f = \chi(\pi) f \forall \pi\in G\} be the ring of invariants of `G` relative to `\chi`. Then the Reynold's operator is a map `R` from `K[x]` into `K[x]^G_{\chi}` defined by .. MATH: f \mapsto \frac{1}{|G|} \sum_{ \pi \in G} \chi(\pi) f. INPUT: - ``poly`` -- a polynomial - ``chi`` -- (default: trivial character) a linear group character of this group OUTPUT: an invariant polynomial relative to `\chi` AUTHORS: Rebecca Lauren Miller and Ben Hutz EXAMPLES:: sage: S3 = MatrixGroup(SymmetricGroup(3)) sage: R.<x,y,z> = QQ[] sage: f = x*y*z^3 sage: S3.reynolds_operator(f) 1/3*x^3*y*z + 1/3*x*y^3*z + 1/3*x*y*z^3 :: sage: G = MatrixGroup(CyclicPermutationGroup(4)) sage: chi = G.character(G.character_table()[3]) sage: K.<v> = CyclotomicField(4) sage: R.<x,y,z,w> = K[] sage: G.reynolds_operator(x, chi) 1/4*x + (-1/4*v)*y - 1/4*z + (1/4*v)*w sage: chi = G.character(G.character_table()[2]) sage: R.<x,y,z,w> = QQ[] sage: G.reynolds_operator(x*y, chi) 1/4*x*y + (1/4*zeta4)*y*z + (-1/4*zeta4)*x*w - 1/4*z*w :: sage: K.<i> = CyclotomicField(4) sage: G = MatrixGroup(CyclicPermutationGroup(3)) sage: chi = G.character(G.character_table()[1]) sage: R.<x,y,z> = K[] sage: G.reynolds_operator(x*y^5, chi) 1/3*x*y^5 + (2/3*izeta3^3 + izeta3^2 + 8/3*izeta3 + 1)*x^5*z + (-2/3*izeta3^3 - izeta3^2 - 8/3*izeta3 - 4/3)*y*z^5 sage: R.<x,y,z> = QQbar[] sage: G.reynolds_operator(x*y^5, chi) 1/3*x*y^5 + (-0.1666666666666667? - 0.2886751345948129?*I)*x^5*z + (-0.1666666666666667? + 0.2886751345948129?*I)*y*z^5 :: sage: K.<i> = CyclotomicField(4) sage: Tetra = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0]) sage: chi = Tetra.character(Tetra.character_table()[4]) sage: L.<v> = QuadraticField(-3) sage: R.<x,y> = L[] sage: Tetra.reynolds_operator(x^4) 0 sage: Tetra.reynolds_operator(x^4, chi) 1/4*x^4 + (1/2*v)*x^2*y^2 + 1/4*y^4 sage: R.<x>=L[] sage: LL.<w> = L.extension(x^2+v) sage: R.<x,y> = LL[] sage: Tetra.reynolds_operator(x^4, chi) Traceback (most recent call last): ... NotImplementedError: only implemented for absolute fields :: sage: G = MatrixGroup(DihedralGroup(4)) sage: chi = G.character(G.character_table()[1]) sage: R.<x,y> = QQ[] sage: f = x^4 sage: G.reynolds_operator(f, chi) Traceback (most recent call last): ... TypeError: number of variables in polynomial must match size of matrices sage: R.<x,y,z,w> = QQ[] sage: f = x^3*y sage: G.reynolds_operator(f, chi) 1/8*x^3*y - 1/8*x*y^3 + 1/8*y^3*z - 1/8*y*z^3 - 1/8*x^3*w + 1/8*z^3*w + 1/8*x*w^3 - 1/8*z*w^3 Characteristic p>0 examples:: sage: G = MatrixGroup([[0,1,1,0]]) sage: R.<w,x> = GF(2)[] sage: G.reynolds_operator(x) Traceback (most recent call last): ... NotImplementedError: not implemented when characteristic divides group order :: sage: i = GF(7)(3) sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]]) sage: chi = G.character(G.character_table()[4]) sage: R.<w,x> = GF(7)[] sage: f = w^5*x + x^6 sage: G.reynolds_operator(f, chi) Traceback (most recent call last): ... NotImplementedError: nontrivial characters not implemented for characteristic > 0 sage: G.reynolds_operator(f) x^6 :: sage: K = GF(3^2,'t') sage: G = MatrixGroup([matrix(K,2,2, [0,K.gen(),1,0])]) sage: R.<x,y> = GF(3)[] sage: G.reynolds_operator(x^8) -x^8 - y^8 :: sage: K = GF(3^2,'t') sage: G = MatrixGroup([matrix(GF(3),2,2, [0,1,1,0])]) sage: R.<x,y> = K[] sage: f = -K.gen()*x sage: G.reynolds_operator(f) (t)*x + (t)*y """ if poly.parent().ngens() != self.degree(): raise TypeError( "number of variables in polynomial must match size of matrices" ) R = FractionField(poly.base_ring()) C = FractionField(self.base_ring()) if chi is None: #then this is the trivial character if R.characteristic() == 0: #non-modular case if C == QQbar or R == QQbar: L = QQbar elif not C.is_absolute() or not R.is_absolute(): raise NotImplementedError( "only implemented for absolute fields") else: #create the compositum if C.absolute_degree() == 1: L = R elif R.absolute_degree() == 1: L = C else: L = C.composite_fields(R)[0] elif not R.characteristic().divides(self.order()): if R.characteristic() != C.characteristic(): raise ValueError( "base fields must have same characteristic") else: if R.degree() >= C.degree(): L = R else: L = C else: raise NotImplementedError( "not implemented when characteristic divides group order") poly = poly.change_ring(L) poly_gens = vector(poly.parent().gens()) F = L.zero() for g in self: F += poly(*g.matrix() * vector(poly.parent().gens())) F /= self.order() return F #non-trivial character case K = chi.values()[0].parent() if R.characteristic() == 0: #extend base_ring to compositum if C == QQbar or K == QQbar or R == QQbar: L = QQbar elif not C.is_absolute() or not K.is_absolute( ) or not R.is_absolute(): raise NotImplementedError( "only implemented for absolute fields") else: fields = [] for M in [R, K, C]: if M.absolute_degree() != 1: fields.append(M) l = len(fields) if l == 0: # all are QQ L = R elif l == 1: #only one is an extension L = fields[0] elif l == 2: #only two are extensions L = fields[0].composite_fields(fields[1])[0] else: #all three are extensions L1 = fields[0].composite_fields(fields[1])[0] L = L1.composite_fields(fields[2])[0] else: raise NotImplementedError( "nontrivial characters not implemented for characteristic > 0") poly = poly.change_ring(L) poly_gens = vector(poly.parent().gens()) F = L.zero() for g in self: F += L(chi(g)) * poly(*g.matrix().change_ring(L) * poly_gens) F /= self.order() try: # attempt to move F to base_ring of polynomial F = F.change_ring(R) except (TypeError, ValueError): pass return F
def binary_quintic_coefficients_from_invariants(invariants, K=None, invariant_choice='default', scaling='none'): r""" Reconstruct a binary quintic from the values of its (Clebsch) invariants. INPUT: - ``invariants`` -- A list or tuple of values of the three or four invariants. The default option requires the Clebsch invariants `A`, `B`, `C` and `R` of the binary quintic. - ``K`` -- The field over which the quintic is defined. - ``invariant_choice`` -- The type of invariants provided. The accepted options are ``'clebsch'`` and ``'default'``, which are the same. No other options are implemented. - ``scaling`` -- How the coefficients should be scaled. The accepted values are ``'none'`` for no scaling, ``'normalized'`` to scale in such a way that the resulting coefficients are independent of the scaling of the input invariants and ``'coprime'`` which scales the input invariants by dividing them by their gcd. OUTPUT: A set of coefficients of a binary quintic, whose invariants are equal to the given ``invariants`` up to a scaling. EXAMPLES: First we check the general case, where the invariant `M` is non-zero:: sage: R.<x0, x1> = QQ[] sage: p = 3*x1^5 + 6*x1^4*x0 + 3*x1^3*x0^2 + 4*x1^2*x0^3 - 5*x1*x0^4 + 4*x0^5 sage: quintic = invariant_theory.binary_quintic(p, x0, x1) sage: invs = quintic.clebsch_invariants(as_tuple=True) sage: reconstructed = invariant_theory.binary_form_from_invariants(5, invs, variables=quintic.variables()) # indirect doctest sage: reconstructed Binary quintic with coefficients (9592267437341790539005557/244140625000000, 2149296928207625556323004064707/610351562500000000, 11149651890347700974453304786783/76293945312500000, 122650775751894638395648891202734239/47683715820312500000, 323996630945706528474286334593218447/11920928955078125000, 1504506503644608395841632538558481466127/14901161193847656250000) We can see that the invariants of the reconstructed form match the ones of the original form by scaling the invariants `B` and `C`:: sage: scale = invs[0]/reconstructed.A_invariant() sage: invs[1] == reconstructed.B_invariant()*scale^2 True sage: invs[2] == reconstructed.C_invariant()*scale^3 True If we compare the form obtained by this reconstruction to the one found by letting the covariants `\alpha` and `\beta` be the coordinates of the form, we find the forms are the same up to a power of the determinant of `\alpha` and `\beta`:: sage: alpha = quintic.alpha_covariant() sage: beta = quintic.beta_covariant() sage: g = matrix([[alpha(x0=1,x1=0),alpha(x0=0,x1=1)],[beta(x0=1,x1=0),beta(x0=0,x1=1)]])^-1 sage: transformed = tuple([g.determinant()^-5*x for x in quintic.transformed(g).coeffs()]) sage: transformed == reconstructed.coeffs() True This can also be seen by computing the `\alpha` covariant of the obtained form:: sage: reconstructed.alpha_covariant().coefficient(x1) 0 sage: reconstructed.alpha_covariant().coefficient(x0) != 0 True If the invariant `M` vanishes, then the coefficients are computed in a different way:: sage: [A,B,C] = [3,1,2] sage: M = 2*A*B - 3*C sage: M 0 sage: from sage.rings.invariants.reconstruction import binary_quintic_coefficients_from_invariants sage: reconstructed = binary_quintic_coefficients_from_invariants([A,B,C]) sage: reconstructed (-66741943359375/2097152, -125141143798828125/134217728, 0, 52793920040130615234375/34359738368, 19797720015048980712890625/1099511627776, -4454487003386020660400390625/17592186044416) sage: newform = sum([ reconstructed[i]*x0^i*x1^(5-i) for i in range(6) ]) sage: newquintic = invariant_theory.binary_quintic(newform, x0, x1) sage: scale = 3/newquintic.A_invariant() sage: [3, newquintic.B_invariant()*scale^2, newquintic.C_invariant()*scale^3] [3, 1, 2] Several special cases:: sage: quintic = invariant_theory.binary_quintic(x0^5 - x1^5, x0, x1) sage: invs = quintic.clebsch_invariants(as_tuple=True) sage: binary_quintic_coefficients_from_invariants(invs) (1, 0, 0, 0, 0, 1) sage: quintic = invariant_theory.binary_quintic(x0*x1*(x0^3-x1^3), x0, x1) sage: invs = quintic.clebsch_invariants(as_tuple=True) sage: binary_quintic_coefficients_from_invariants(invs) (0, 1, 0, 0, 1, 0) sage: quintic = invariant_theory.binary_quintic(x0^5 + 10*x0^3*x1^2 - 15*x0*x1^4, x0, x1) sage: invs = quintic.clebsch_invariants(as_tuple=True) sage: binary_quintic_coefficients_from_invariants(invs) (1, 0, 10, 0, -15, 0) sage: quintic = invariant_theory.binary_quintic(x0^2*(x0^3 + x1^3), x0, x1) sage: invs = quintic.clebsch_invariants(as_tuple=True) sage: binary_quintic_coefficients_from_invariants(invs) (1, 0, 0, 1, 0, 0) sage: quintic = invariant_theory.binary_quintic(x0*(x0^4 + x1^4), x0, x1) sage: invs = quintic.clebsch_invariants(as_tuple=True) sage: binary_quintic_coefficients_from_invariants(invs) (1, 0, 0, 0, 1, 0) For fields of characteristic 2, 3 or 5, there is no reconstruction implemented. This is part of :trac:`26786`.:: sage: binary_quintic_coefficients_from_invariants([3,1,2], K=GF(5)) Traceback (most recent call last): ... NotImplementedError: no reconstruction of binary quintics implemented for fields of characteristic 2, 3 or 5 TESTS:: sage: from sage.rings.invariants.reconstruction import binary_quintic_coefficients_from_invariants sage: binary_quintic_coefficients_from_invariants([1,2,3], scaling='unknown') Traceback (most recent call last): ... ValueError: unknown scaling option 'unknown' """ if invariant_choice not in ['default', 'clebsch']: raise ValueError('unknown choice of invariants {} for a binary quintic' .format(invariant_choice)) if scaling not in ['none', 'normalized', 'coprime']: raise ValueError("unknown scaling option '%s'" % scaling) if scaling == 'coprime': if len(invariants) == 3: invariants = _reduce_invariants(invariants, [1,2,3]) elif len(invariants) == 4: invariants = _reduce_invariants(invariants, [2,4,6,9]) A, B, C = invariants[0:3] if K is None: from sage.rings.fraction_field import FractionField K = FractionField(A.parent()) if K.characteristic() in [2, 3, 5]: raise NotImplementedError('no reconstruction of binary quintics ' 'implemented for fields of characteristic 2, 3 or 5') M = 2*A*B - 3*C N = K(2)**-1 * (A*C-B**2) R2 = -K(2)**-1 * (A*N**2-2*B*M*N+C*M**2) scale = [1,1,1,1,1,1] from sage.functions.all import binomial, sqrt if len(invariants) == 3: if R2.is_square(): R = sqrt(R2) else: # if R2 is not a square, we scale the invariants in a suitable way # so that the 'new' R2 is a square [A, B, C] = [R2*A, R2**2*B, R2**3*C] [M, N] = [R2**3*M, R2**4*N] R = R2**5 elif len(invariants) == 4: if invariants[3]**2 != R2: raise ValueError('provided invariants do not satisfy the syzygy ' 'for Clebsch invariants of a binary quintic') R = invariants[3] else: raise ValueError('incorrect number of invariants provided, this ' 'method requires 3 or 4 invariants') if M == 0: if N == 0: if A == 0: raise ValueError('no unique reconstruction possible for ' 'quintics with a treefold linear factor') else: if B == 0: return (1,0,0,0,0,1) else: return (0,1,0,0,1,0) else: # case corresponding to using alpha and gamma as coordinates if A == 0: return (1,0,0,0,1,0) else: if scaling == 'normalized': # scaling z by (R/A**3) scale = [ (-N)**-5*A**6*(R/A**3)**i for i in range(6) ] D = -N Delta = C a = [0] a.append((2*K(3)**-1*A**2-B)*N*B*K(2)**-1 - N**2*K(2)**-1) B0 = 2*K(3)**-1*A*R B1 = A*N*B*K(3)**-1 C0 = 2*K(3)**-1*R C1 = B*N else: # case corresponding to using alpha and beta as coordinates if R == 0: if A == 0: return (1,0,10,0,-15,0) elif scaling == 'normalized': # scaling x by A and z by sqrt(A) scale = [ (-M)**(-5)*sqrt(A)**(12+i) for i in range(6) ] else: if A == 0: if B == 0: return (1,0,0,1,0,0) elif scaling == 'normalized': # scaling y by R/B**2 scale = [ (-M)**(-3)*(R/B**2)**i for i in range(6) ] elif scaling == 'normalized': # scaling y by R/A**4 scale = [ (-M)**(-3)*(R/A**4)**i for i in range(6) ] D = -M Delta = A a = [0] a.append((2*K(3)**-1*A**2-B)*(N*A-M*B)*K(2)**-1 \ - M*(N*K(2)**-1-M*A*K(3)**-1)) B0 = R B1 = K(2)**-1*(N*A-M*B) C0 = 0 C1 = -M a[0] = (2*K(3)**-1*A**2-B)*R a.append(-D*B0 - K(2)**-1*Delta*a[0]) a.append(-D*B1 - K(2)**-1*Delta*a[1]) a.append(D**2*C0 + D*Delta*B0 + K(4)**-1*Delta**2*a[0]) a.append(D**2*C1 + D*Delta*B1 + K(4)**-1*Delta**2*a[1]) coeffs = tuple([K((-1)**i*binomial(5,i)*scale[5-i]*a[i]) for i in range(6)]) if scaling == 'coprime': from sage.arith.misc import gcd return tuple([coeffs[i]/gcd(coeffs) for i in range(6)]) else: return coeffs
def reynolds_operator(self, poly, chi=None): r""" Compute the Reynolds operator of this finite group `G`. This is the projection from a polynomial ring to the ring of relative invariants [Stu1993]_. If possible, the invariant is returned defined over the base field of the given polynomial ``poly``, otherwise, it is returned over the compositum of the fields involved in the computation. Only implemented for absolute fields. ALGORITHM: Let `K[x]` be a polynomial ring and `\chi` a linear character for `G`. Let .. MATH: K[x]^G_{\chi} = \{f \in K[x] | \pi f = \chi(\pi) f \forall \pi\in G\} be the ring of invariants of `G` relative to `\chi`. Then the Reynold's operator is a map `R` from `K[x]` into `K[x]^G_{\chi}` defined by .. MATH: f \mapsto \frac{1}{|G|} \sum_{ \pi \in G} \chi(\pi) f. INPUT: - ``poly`` -- a polynomial - ``chi`` -- (default: trivial character) a linear group character of this group OUTPUT: an invariant polynomial relative to `\chi` AUTHORS: Rebecca Lauren Miller and Ben Hutz EXAMPLES:: sage: S3 = MatrixGroup(SymmetricGroup(3)) sage: R.<x,y,z> = QQ[] sage: f = x*y*z^3 sage: S3.reynolds_operator(f) 1/3*x^3*y*z + 1/3*x*y^3*z + 1/3*x*y*z^3 :: sage: G = MatrixGroup(CyclicPermutationGroup(4)) sage: chi = G.character(G.character_table()[3]) sage: K.<v> = CyclotomicField(4) sage: R.<x,y,z,w> = K[] sage: G.reynolds_operator(x, chi) 1/4*x + (1/4*v)*y - 1/4*z + (-1/4*v)*w sage: chi = G.character(G.character_table()[2]) sage: R.<x,y,z,w> = QQ[] sage: G.reynolds_operator(x*y, chi) 1/4*x*y + (-1/4*zeta4)*y*z + (1/4*zeta4)*x*w - 1/4*z*w :: sage: K.<i> = CyclotomicField(4) sage: G = MatrixGroup(CyclicPermutationGroup(3)) sage: chi = G.character(G.character_table()[1]) sage: R.<x,y,z> = K[] sage: G.reynolds_operator(x*y^5, chi) 1/3*x*y^5 + (2/3*izeta3^3 + izeta3^2 + 8/3*izeta3 + 1)*x^5*z + (-2/3*izeta3^3 - izeta3^2 - 8/3*izeta3 - 4/3)*y*z^5 sage: R.<x,y,z> = QQbar[] sage: G.reynolds_operator(x*y^5, chi) 1/3*x*y^5 + (-0.1666666666666667? - 0.2886751345948129?*I)*x^5*z + (-0.1666666666666667? + 0.2886751345948129?*I)*y*z^5 :: sage: K.<i> = CyclotomicField(4) sage: Tetra = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0]) sage: chi = Tetra.character(Tetra.character_table()[4]) sage: L.<v> = QuadraticField(-3) sage: R.<x,y> = L[] sage: Tetra.reynolds_operator(x^4) 0 sage: Tetra.reynolds_operator(x^4, chi) 1/4*x^4 + (1/2*v)*x^2*y^2 + 1/4*y^4 sage: R.<x>=L[] sage: LL.<w> = L.extension(x^2+v) sage: R.<x,y> = LL[] sage: Tetra.reynolds_operator(x^4, chi) Traceback (most recent call last): ... NotImplementedError: only implemented for absolute fields :: sage: G = MatrixGroup(DihedralGroup(4)) sage: chi = G.character(G.character_table()[1]) sage: R.<x,y> = QQ[] sage: f = x^4 sage: G.reynolds_operator(f, chi) Traceback (most recent call last): ... TypeError: number of variables in polynomial must match size of matrices sage: R.<x,y,z,w> = QQ[] sage: f = x^3*y sage: G.reynolds_operator(f, chi) 1/8*x^3*y - 1/8*x*y^3 + 1/8*y^3*z - 1/8*y*z^3 - 1/8*x^3*w + 1/8*z^3*w + 1/8*x*w^3 - 1/8*z*w^3 Characteristic p>0 examples:: sage: G = MatrixGroup([[0,1,1,0]]) sage: R.<w,x> = GF(2)[] sage: G.reynolds_operator(x) Traceback (most recent call last): ... NotImplementedError: not implemented when characteristic divides group order :: sage: i = GF(7)(3) sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]]) sage: chi = G.character(G.character_table()[4]) sage: R.<w,x> = GF(7)[] sage: f = w^5*x + x^6 sage: G.reynolds_operator(f, chi) Traceback (most recent call last): ... NotImplementedError: nontrivial characters not implemented for characteristic > 0 sage: G.reynolds_operator(f) x^6 :: sage: K = GF(3^2,'t') sage: G = MatrixGroup([matrix(K,2,2, [0,K.gen(),1,0])]) sage: R.<x,y> = GF(3)[] sage: G.reynolds_operator(x^8) -x^8 - y^8 :: sage: K = GF(3^2,'t') sage: G = MatrixGroup([matrix(GF(3),2,2, [0,1,1,0])]) sage: R.<x,y> = K[] sage: f = -K.gen()*x sage: G.reynolds_operator(f) (t)*x + (t)*y """ if poly.parent().ngens() != self.degree(): raise TypeError("number of variables in polynomial must match size of matrices") R = FractionField(poly.base_ring()) C = FractionField(self.base_ring()) if chi is None: #then this is the trivial character if R.characteristic() == 0: #non-modular case if C == QQbar or R == QQbar: L = QQbar elif not C.is_absolute() or not R.is_absolute(): raise NotImplementedError("only implemented for absolute fields") else: #create the compositum if C.absolute_degree() == 1: L = R elif R.absolute_degree() == 1: L = C else: L = C.composite_fields(R)[0] elif not R.characteristic().divides(self.order()): if R.characteristic() != C.characteristic(): raise ValueError("base fields must have same characteristic") else: if R.degree() >= C.degree(): L = R else: L = C else: raise NotImplementedError("not implemented when characteristic divides group order") poly = poly.change_ring(L) poly_gens = vector(poly.parent().gens()) F = L.zero() for g in self: F += poly(*g.matrix()*vector(poly.parent().gens())) F /= self.order() return F #non-trivial character case K = chi.values()[0].parent() if R.characteristic() == 0: #extend base_ring to compositum if C == QQbar or K == QQbar or R == QQbar: L = QQbar elif not C.is_absolute() or not K.is_absolute() or not R.is_absolute(): raise NotImplementedError("only implemented for absolute fields") else: fields = [] for M in [R,K,C]: if M.absolute_degree() != 1: fields.append(M) l = len(fields) if l == 0: # all are QQ L = R elif l == 1: #only one is an extension L = fields[0] elif l == 2: #only two are extensions L = fields[0].composite_fields(fields[1])[0] else: #all three are extensions L1 = fields[0].composite_fields(fields[1])[0] L = L1.composite_fields(fields[2])[0] else: raise NotImplementedError("nontrivial characters not implemented for characteristic > 0") poly = poly.change_ring(L) poly_gens = vector(poly.parent().gens()) F = L.zero() for g in self: F += L(chi(g)) * poly(*g.matrix().change_ring(L)*poly_gens) F /= self.order() try: # attempt to move F to base_ring of polyomial F = F.change_ring(R) except (TypeError, ValueError): pass return F
def dimension(self): r""" Return the dimension of the algebraic subscheme. OUTPUT: Integer. EXAMPLES:: sage: X.<x,y,z,w,u,v> = ProductProjectiveSpaces([2,2],QQ) sage: L = (-w - v)*x + (-w*y - u*z) sage: Q = (-u*w - v^2)*x^2 + ((-w^2 - u*w + (-u*v - u^2))*y + (-w^2 - u*v)*z)*x + \ ((-w^2 - u*w - u^2)*y^2 + (-u*w - v^2)*z*y + (-w^2 + (-v - u)*w)*z^2) sage: W = X.subscheme([L,Q]) sage: W.dimension() 2 :: sage: PP.<x,y,z,u,v,s,t> = ProductProjectiveSpaces([2,1,1], QQ) sage: X = PP.subscheme([x^3, x^5+y^5, z^6, x*u-v*y, s^2-t^2]) sage: X.dimension() -1 :: sage: PP = ProductProjectiveSpaces([2,1,3], CC, 't') sage: PP.subscheme([]).dimension() 6 :: sage: PP = ProductProjectiveSpaces([1,3,1], ZZ, 't') sage: PP.subscheme([]).dimension() 5 :: sage: PP.<x,y,u,v,s,t> = ProductProjectiveSpaces([1,1,1], CC) sage: X = PP.subscheme([x^2-y^2, u-v, s^2-t^2]) sage: X.dimension() 0 """ try: return self.__dimension except AttributeError: try: #move to field to compute radical X = self.change_ring(FractionField(self.base_ring())) PP = X.ambient_space() I = X.defining_ideal().radical() #check if the irrelevant ideal of any component is in the radical if any( all(t in I for t in PS.gens()) for PS in PP.components()): self.__dimension = -1 else: self.__dimension = I.dimension() - PP.num_components() except TypeError: #cannot compute radical for this base ring phi = self.segre_embedding() self.__dimension = phi.codomain().defining_ideal().dimension( ) - 1 return self.__dimension
def molien_series(self, chi=None, return_series=True, prec=20, variable='t'): r""" Compute the Molien series of this finite group with respect to the character ``chi``. It can be returned either as a rational function in one variable or a power series in one variable. The base field must be a finite field, the rationals, or a cyclotomic field. Note that the base field characteristic cannot divide the group order (i.e., the non-modular case). ALGORITHM: For a finite group `G` in characteristic zero we construct the Molien series as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\text{det}(I-tg)}, where `I` is the identity matrix and `t` an indeterminate. For characteristic `p` not dividing the order of `G`, let `k` be the base field and `N` the order of `G`. Define `\lambda` as a primitive `N`-th root of unity over `k` and `\omega` as a primitive `N`-th root of unity over `\QQ`. For each `g \in G` define `k_i(g)` to be the positive integer such that `e_i = \lambda^{k_i(g)}` for each eigenvalue `e_i` of `g`. Then the Molien series is computed as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\prod_{i=1}^n(1 - t\omega^{k_i(g)})}, where `t` is an indeterminant. [Dec1998]_ INPUT: - ``chi`` -- (default: trivial character) a linear group character of this group - ``return_series`` -- boolean (default: ``True``) if ``True``, then returns the Molien series as a power series, ``False`` as a rational function - ``prec`` -- integer (default: 20); power series default precision - ``variable`` -- string (default: ``'t'``); Variable name for the Molien series OUTPUT: single variable rational function or power series with integer coefficients EXAMPLES:: sage: MatrixGroup(matrix(QQ,2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: only implemented for finite groups sage: MatrixGroup(matrix(GF(3),2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: characteristic cannot divide group order Tetrahedral Group:: sage: K.<i> = CyclotomicField(4) sage: Tetra = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0]) sage: Tetra.molien_series(prec=30) 1 + t^8 + 2*t^12 + t^16 + 2*t^20 + 3*t^24 + 2*t^28 + O(t^30) sage: mol = Tetra.molien_series(return_series=False); mol (t^8 - t^4 + 1)/(t^16 - t^12 - t^4 + 1) sage: mol.parent() Fraction Field of Univariate Polynomial Ring in t over Integer Ring sage: chi = Tetra.character(Tetra.character_table()[1]) sage: Tetra.molien_series(chi, prec=30, variable='u') u^6 + u^14 + 2*u^18 + u^22 + 2*u^26 + 3*u^30 + 2*u^34 + O(u^36) sage: chi = Tetra.character(Tetra.character_table()[2]) sage: Tetra.molien_series(chi) t^10 + t^14 + t^18 + 2*t^22 + 2*t^26 + O(t^30) :: sage: S3 = MatrixGroup(SymmetricGroup(3)) sage: mol = S3.molien_series(prec=10); mol 1 + t + 2*t^2 + 3*t^3 + 4*t^4 + 5*t^5 + 7*t^6 + 8*t^7 + 10*t^8 + 12*t^9 + O(t^10) sage: mol.parent() Power Series Ring in t over Integer Ring Octahedral Group:: sage: K.<v> = CyclotomicField(8) sage: a = v-v^3 #sqrt(2) sage: i = v^2 sage: Octa = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [(1+i)/a,0, 0,(1-i)/a]) sage: Octa.molien_series(prec=30) 1 + t^8 + t^12 + t^16 + t^18 + t^20 + 2*t^24 + t^26 + t^28 + O(t^30) Icosahedral Group:: sage: K.<v> = CyclotomicField(10) sage: z5 = v^2 sage: i = z5^5 sage: a = 2*z5^3 + 2*z5^2 + 1 #sqrt(5) sage: Ico = MatrixGroup([[z5^3,0, 0,z5^2], [0,1, -1,0], [(z5^4-z5)/a, (z5^2-z5^3)/a, (z5^2-z5^3)/a, -(z5^4-z5)/a]]) sage: Ico.molien_series(prec=40) 1 + t^12 + t^20 + t^24 + t^30 + t^32 + t^36 + O(t^40) :: sage: G = MatrixGroup(CyclicPermutationGroup(3)) sage: chi = G.character(G.character_table()[1]) sage: G.molien_series(chi, prec=10) t + 2*t^2 + 3*t^3 + 5*t^4 + 7*t^5 + 9*t^6 + 12*t^7 + 15*t^8 + 18*t^9 + 22*t^10 + O(t^11) :: sage: K = GF(5) sage: S = MatrixGroup(SymmetricGroup(4)) sage: G = MatrixGroup([matrix(K,4,4,[K(y) for u in m.list() for y in u])for m in S.gens()]) sage: G.molien_series(return_series=False) 1/(t^10 - t^9 - t^8 + 2*t^5 - t^2 - t + 1) :: sage: i = GF(7)(3) sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]]) sage: chi = G.character(G.character_table()[4]) sage: G.molien_series(chi) 3*t^5 + 6*t^11 + 9*t^17 + 12*t^23 + O(t^25) """ if not self.is_finite(): raise NotImplementedError("only implemented for finite groups") if chi is None: chi = self.trivial_character() M = self.matrix_space() R = FractionField(self.base_ring()) N = self.order() if R.characteristic() == 0: P = PolynomialRing(R, variable) t = P.gen() #it is possible the character is over a larger cyclotomic field K = chi.values()[0].parent() if K.degree() != 1: if R.degree() != 1: L = K.composite_fields(R)[0] else: L = K else: L = R mol = P(0) for g in self: mol += L(chi(g)) / (M.identity_matrix()-t*g.matrix()).det().change_ring(L) elif R.characteristic().divides(N): raise NotImplementedError("characteristic cannot divide group order") else: #char p>0 #find primitive Nth roots of unity over base ring and QQ F = cyclotomic_polynomial(N).change_ring(R) w = F.roots(ring=R.algebraic_closure(), multiplicities=False)[0] #don't need to extend further in this case since the order of #the roots of unity in the character divide the order of the group L = CyclotomicField(N, 'v') v = L.gen() #construct Molien series P = PolynomialRing(L, variable) t = P.gen() mol = P(0) for g in self: #construct Phi phi = L(chi(g)) for e in g.matrix().eigenvalues(): #find power such that w**n = e n = 1 while w**n != e and n < N+1: n += 1 #raise v to that power phi *= (1-t*v**n) mol += P(1)/phi #We know the coefficients will be integers mol = mol.numerator().change_ring(ZZ) / mol.denominator().change_ring(ZZ) #divide by group order mol /= N if return_series: PS = PowerSeriesRing(ZZ, variable, default_prec=prec) return PS(mol) return mol
def molien_series(self, chi=None, return_series=True, prec=20, variable='t'): r""" Compute the Molien series of this finite group with respect to the character ``chi``. It can be returned either as a rational function in one variable or a power series in one variable. The base field must be a finite field, the rationals, or a cyclotomic field. Note that the base field characteristic cannot divide the group order (i.e., the non-modular case). ALGORITHM: For a finite group `G` in characteristic zero we construct the Molien series as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\text{det}(I-tg)}, where `I` is the identity matrix and `t` an indeterminate. For characteristic `p` not dividing the order of `G`, let `k` be the base field and `N` the order of `G`. Define `\lambda` as a primitive `N`-th root of unity over `k` and `\omega` as a primitive `N`-th root of unity over `\QQ`. For each `g \in G` define `k_i(g)` to be the positive integer such that `e_i = \lambda^{k_i(g)}` for each eigenvalue `e_i` of `g`. Then the Molien series is computed as .. MATH:: \frac{1}{|G|}\sum_{g \in G} \frac{\chi(g)}{\prod_{i=1}^n(1 - t\omega^{k_i(g)})}, where `t` is an indeterminant. [Dec1998]_ INPUT: - ``chi`` -- (default: trivial character) a linear group character of this group - ``return_series`` -- boolean (default: ``True``) if ``True``, then returns the Molien series as a power series, ``False`` as a rational function - ``prec`` -- integer (default: 20); power series default precision - ``variable`` -- string (default: ``'t'``); Variable name for the Molien series OUTPUT: single variable rational function or power series with integer coefficients EXAMPLES:: sage: MatrixGroup(matrix(QQ,2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: only implemented for finite groups sage: MatrixGroup(matrix(GF(3),2,2,[1,1,0,1])).molien_series() Traceback (most recent call last): ... NotImplementedError: characteristic cannot divide group order Tetrahedral Group:: sage: K.<i> = CyclotomicField(4) sage: Tetra = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [0,i, -i,0]) sage: Tetra.molien_series(prec=30) 1 + t^8 + 2*t^12 + t^16 + 2*t^20 + 3*t^24 + 2*t^28 + O(t^30) sage: mol = Tetra.molien_series(return_series=False); mol (t^8 - t^4 + 1)/(t^16 - t^12 - t^4 + 1) sage: mol.parent() Fraction Field of Univariate Polynomial Ring in t over Integer Ring sage: chi = Tetra.character(Tetra.character_table()[1]) sage: Tetra.molien_series(chi, prec=30, variable='u') u^6 + u^14 + 2*u^18 + u^22 + 2*u^26 + 3*u^30 + 2*u^34 + O(u^36) sage: chi = Tetra.character(Tetra.character_table()[2]) sage: Tetra.molien_series(chi) t^10 + t^14 + t^18 + 2*t^22 + 2*t^26 + O(t^30) :: sage: S3 = MatrixGroup(SymmetricGroup(3)) sage: mol = S3.molien_series(prec=10); mol 1 + t + 2*t^2 + 3*t^3 + 4*t^4 + 5*t^5 + 7*t^6 + 8*t^7 + 10*t^8 + 12*t^9 + O(t^10) sage: mol.parent() Power Series Ring in t over Integer Ring Octahedral Group:: sage: K.<v> = CyclotomicField(8) sage: a = v-v^3 #sqrt(2) sage: i = v^2 sage: Octa = MatrixGroup([(-1+i)/2,(-1+i)/2, (1+i)/2,(-1-i)/2], [(1+i)/a,0, 0,(1-i)/a]) sage: Octa.molien_series(prec=30) 1 + t^8 + t^12 + t^16 + t^18 + t^20 + 2*t^24 + t^26 + t^28 + O(t^30) Icosahedral Group:: sage: K.<v> = CyclotomicField(10) sage: z5 = v^2 sage: i = z5^5 sage: a = 2*z5^3 + 2*z5^2 + 1 #sqrt(5) sage: Ico = MatrixGroup([[z5^3,0, 0,z5^2], [0,1, -1,0], [(z5^4-z5)/a, (z5^2-z5^3)/a, (z5^2-z5^3)/a, -(z5^4-z5)/a]]) sage: Ico.molien_series(prec=40) 1 + t^12 + t^20 + t^24 + t^30 + t^32 + t^36 + O(t^40) :: sage: G = MatrixGroup(CyclicPermutationGroup(3)) sage: chi = G.character(G.character_table()[1]) sage: G.molien_series(chi, prec=10) t + 2*t^2 + 3*t^3 + 5*t^4 + 7*t^5 + 9*t^6 + 12*t^7 + 15*t^8 + 18*t^9 + 22*t^10 + O(t^11) :: sage: K = GF(5) sage: S = MatrixGroup(SymmetricGroup(4)) sage: G = MatrixGroup([matrix(K,4,4,[K(y) for u in m.list() for y in u])for m in S.gens()]) sage: G.molien_series(return_series=False) 1/(t^10 - t^9 - t^8 + 2*t^5 - t^2 - t + 1) :: sage: i = GF(7)(3) sage: G = MatrixGroup([[i^3,0,0,-i^3],[i^2,0,0,-i^2]]) sage: chi = G.character(G.character_table()[4]) sage: G.molien_series(chi) 3*t^5 + 6*t^11 + 9*t^17 + 12*t^23 + O(t^25) """ if not self.is_finite(): raise NotImplementedError("only implemented for finite groups") if chi is None: chi = self.trivial_character() M = self.matrix_space() R = FractionField(self.base_ring()) N = self.order() if R.characteristic() == 0: P = PolynomialRing(R, variable) t = P.gen() #it is possible the character is over a larger cyclotomic field K = chi.values()[0].parent() if K.degree() != 1: if R.degree() != 1: L = K.composite_fields(R)[0] else: L = K else: L = R mol = P(0) for g in self: mol += L(chi(g)) / (M.identity_matrix() - t * g.matrix()).det().change_ring(L) elif R.characteristic().divides(N): raise NotImplementedError( "characteristic cannot divide group order") else: #char p>0 #find primitive Nth roots of unity over base ring and QQ F = cyclotomic_polynomial(N).change_ring(R) w = F.roots(ring=R.algebraic_closure(), multiplicities=False)[0] #don't need to extend further in this case since the order of #the roots of unity in the character divide the order of the group L = CyclotomicField(N, 'v') v = L.gen() #construct Molien series P = PolynomialRing(L, variable) t = P.gen() mol = P(0) for g in self: #construct Phi phi = L(chi(g)) for e in g.matrix().eigenvalues(): #find power such that w**n = e n = 1 while w**n != e and n < N + 1: n += 1 #raise v to that power phi *= (1 - t * v**n) mol += P(1) / phi #We know the coefficients will be integers mol = mol.numerator().change_ring(ZZ) / mol.denominator().change_ring( ZZ) #divide by group order mol /= N if return_series: PS = PowerSeriesRing(ZZ, variable, default_prec=prec) return PS(mol) return mol
def closed_form(self, n='n'): r""" Return a symbolic expression in ``n``, which equals the n-th term of the sequence. It is a well-known property of C-finite sequences ``a_n`` that they have a closed form of the type: .. MATH:: a_n = \sum_{i=1}^d c_i(n) \cdot r_i^n, where ``r_i`` are the roots of the characteristic equation and ``c_i(n)`` is a polynomial (whose degree equals the multiplicity of ``r_i`` minus one). This is a natural generalization of Binet's formula for Fibonacci numbers. See, for instance, [KP2011, Theorem 4.1]. Note that if the o.g.f. has a polynomial part, that is, if the numerator degree is not strictly less than the denominator degree, then this closed form holds only when ``n`` exceeds the degree of that polynomial part. In that case, the returned expression will differ from the sequence for small ``n``. EXAMPLES:: sage: CFiniteSequence(1/(1-x)).closed_form() 1 sage: CFiniteSequence(x^2/(1-x)).closed_form() 1 sage: CFiniteSequence(1/(1-x^2)).closed_form() 1/2*(-1)^n + 1/2 sage: CFiniteSequence(1/(1+x^3)).closed_form() 1/3*(-1)^n + 1/3*(1/2*I*sqrt(3) + 1/2)^n + 1/3*(-1/2*I*sqrt(3) + 1/2)^n sage: CFiniteSequence(1/(1-x)/(1-2*x)/(1-3*x)).closed_form() 9/2*3^n - 4*2^n + 1/2 Binet's formula for the Fibonacci numbers:: sage: CFiniteSequence(x/(1-x-x^2)).closed_form() sqrt(1/5)*(1/2*sqrt(5) + 1/2)^n - sqrt(1/5)*(-1/2*sqrt(5) + 1/2)^n sage: [_.subs(n=k).full_simplify() for k in range(6)] [0, 1, 1, 2, 3, 5] sage: CFiniteSequence((4*x+3)/(1-2*x-5*x^2)).closed_form() 1/2*(sqrt(6) + 1)^n*(7*sqrt(1/6) + 3) - 1/2*(-sqrt(6) + 1)^n*(7*sqrt(1/6) - 3) Examples with multiple roots:: sage: CFiniteSequence(x*(x^2+4*x+1)/(1-x)^5).closed_form() 1/4*n^4 + 1/2*n^3 + 1/4*n^2 sage: CFiniteSequence((1+2*x-x^2)/(1-x)^4/(1+x)^2).closed_form() 1/12*n^3 - 1/8*(-1)^n*(n + 1) + 3/4*n^2 + 43/24*n + 9/8 sage: CFiniteSequence(1/(1-x)^3/(1-2*x)^4).closed_form() 4/3*(n^3 - 3*n^2 + 20*n - 36)*2^n + 1/2*n^2 + 19/2*n + 49 sage: CFiniteSequence((x/(1-x-x^2))^2).closed_form() 1/5*(n - sqrt(1/5))*(1/2*sqrt(5) + 1/2)^n + 1/5*(n + sqrt(1/5))*(-1/2*sqrt(5) + 1/2)^n """ from sage.arith.all import binomial from sage.rings.qqbar import QQbar from sage.symbolic.ring import SR n = SR(n) expr = SR.zero() R = FractionField(PolynomialRing(QQbar, self.parent().variable_name())) ogf = R(self.ogf()) __, parts = ogf.partial_fraction_decomposition(decompose_powers=False) for part in parts: denom = part.denominator().factor() denom_base, denom_exp = denom[0] # denominator is of the form (x+b)^{m+1} m = denom_exp - 1 b = denom_base.constant_coefficient() # check that the partial fraction decomposition was indeed done correctly # (that is, there is only one factor, of degree 1, and monic) assert len(denom) == 1 and len(denom_base.list( )) == 2 and denom_base[1] == 1 and denom.unit() == 1 r = SR((-1 / b).radical_expression()) c = SR.zero() for k, a in enumerate(part.numerator()): a = -QQbar(a) if k % 2 else QQbar(a) bino = binomial(n + m - k, m) c += bino * SR((a * b**(k - m - 1)).radical_expression()) expr += c.expand() * r**n return expr
def canonical_height(self, F, **kwds): r""" Evaluates the (absolute) canonical height of ``self`` with respect to ``F``. Must be over number field or order of a number field. Specify either the number of terms of the series to evaluate or the error bound required. ALGORITHM: The sum of the Green's function at the archimedean places and the places of bad reduction. INPUT: - ``F`` - a projective morphism kwds: - ``badprimes`` - a list of primes of bad reduction - ``N`` - positive integer. number of terms of the series to use in the local green functions - ``prec`` - positive integer, float point or p-adic precision - ``error_bound`` - a positive real number OUTPUT: - a real number EXAMPLES:: sage: P.<x,y> = ProjectiveSpace(ZZ,1) sage: H = Hom(P,P) sage: f = H([x^2+y^2,2*x*y]); sage: Q = P(2,1) sage: f.canonical_height(f(Q)) 2.1965476757927038111992627081 sage: f.canonical_height(Q) 1.0979353871245941198040174712 Notice that preperiodic points may not be exactly 0. :: sage: P.<x,y> = ProjectiveSpace(QQ,1) sage: H = Hom(P,P) sage: f = H([x^2-29/16*y^2,y^2]); sage: Q = P(5,4) sage: f.canonical_height(Q, N=30) 1.4989058602918874235833076226e-9 :: sage: P.<x,y,z> = ProjectiveSpace(QQ,2) sage: X = P.subscheme(x^2-y^2); sage: H = Hom(X,X) sage: f = H([x^2,y^2,30*z^2]); sage: Q = X([4,4,1]) sage: f.canonical_height(Q, badprimes=[2,3,5], prec=200) 2.7054056208276961889784303469356774912979228770208655455481 """ bad_primes = kwds.pop("badprimes", None) prec = kwds.get("prec", 100) error_bound = kwds.get("error_bound", None) K = FractionField(self.codomain().base_ring()) if not K in _NumberFields: raise NotImplementedError("Must be over a NumberField or a NumberField Order") if bad_primes is None: bad_primes = [] for b in self: if K == QQ: bad_primes += b.denominator().prime_factors() else: bad_primes += b.denominator_ideal().prime_factors() bad_primes += K(F.resultant()).support() bad_primes = list(set(bad_primes)) emb = K.places(prec=prec) num_places = len(emb) + len(bad_primes) if not error_bound is None: error_bound /= num_places R = RealField(prec) h = R(0) # Archimedean local heights # :: WARNING: If places is fed the default Sage precision of 53 bits, # it uses Real or Complex Double Field in place of RealField(prec) or ComplexField(prec) # the function is_RealField does not identify RDF as real, so we test for that ourselves. for v in emb: if is_RealField(v.codomain()) or v.codomain() is RDF: dv = R(1) else: dv = R(2) h += dv*self.green_function(F, v, **kwds) #arch Green function # Non-Archimedean local heights for v in bad_primes: if K == QQ: dv = R(1) else: dv = R(v.residue_class_degree() * v.absolute_ramification_index()) h += dv * self.green_function(F, v, **kwds) #non-arch Green functions return h
def __init__(self, parent, polys, check=True): r""" The Python constructor. See :class:`SchemeMorphism_polynomial` for details. INPUT: - ``parent`` -- Hom. - ``polys`` -- list or tuple of polynomial or rational functions. - ``check`` -- Boolean. OUTPUT: - :class:`SchemeMorphism_polynomial_affine_space`. EXAMPLES:: sage: A.<x,y> = AffineSpace(ZZ, 2) sage: H = Hom(A, A) sage: H([3/5*x^2, y^2/(2*x^2)]) Scheme endomorphism of Affine Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x, y) to (3*x^2/5, y^2/(2*x^2)) :: sage: A.<x,y> = AffineSpace(ZZ, 2) sage: H = Hom(A, A) sage: H([3*x^2/(5*y), y^2/(2*x^2)]) Scheme endomorphism of Affine Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x, y) to (3*x^2/(5*y), y^2/(2*x^2)) :: sage: A.<x,y> = AffineSpace(QQ, 2) sage: H = Hom(A, A) sage: H([3/2*x^2, y^2]) Scheme endomorphism of Affine Space of dimension 2 over Rational Field Defn: Defined on coordinates by sending (x, y) to (3/2*x^2, y^2) :: sage: A.<x,y> = AffineSpace(QQ, 2) sage: X = A.subscheme([x-y^2]) sage: H = Hom(X, X) sage: H([9/4*x^2, 3/2*y]) Scheme endomorphism of Closed subscheme of Affine Space of dimension 2 over Rational Field defined by: -y^2 + x Defn: Defined on coordinates by sending (x, y) to (9/4*x^2, 3/2*y) sage: P.<x,y,z> = ProjectiveSpace(ZZ, 2) sage: H = Hom(P, P) sage: f = H([5*x^3 + 3*x*y^2-y^3, 3*z^3 + y*x^2, x^3-z^3]) sage: f.dehomogenize(2) Scheme endomorphism of Affine Space of dimension 2 over Integer Ring Defn: Defined on coordinates by sending (x, y) to ((5*x^3 + 3*x*y^2 - y^3)/(x^3 - 1), (x^2*y + 3)/(x^3 - 1)) If you pass in quotient ring elements, they are reduced:: sage: A.<x,y,z> = AffineSpace(QQ, 3) sage: X = A.subscheme([x-y]) sage: H = Hom(X,X) sage: u,v,w = X.coordinate_ring().gens() sage: H([u, v, u+v]) Scheme endomorphism of Closed subscheme of Affine Space of dimension 3 over Rational Field defined by: x - y Defn: Defined on coordinates by sending (x, y, z) to (y, y, 2*y) You must use the ambient space variables to create rational functions:: sage: A.<x,y,z> = AffineSpace(QQ, 3) sage: X = A.subscheme([x^2-y^2]) sage: H = Hom(X,X) sage: u,v,w = X.coordinate_ring().gens() sage: H([u, v, (u+1)/v]) Traceback (most recent call last): ... ArithmeticError: Division failed. The numerator is not a multiple of the denominator. sage: H([x, y, (x+1)/y]) Scheme endomorphism of Closed subscheme of Affine Space of dimension 3 over Rational Field defined by: x^2 - y^2 Defn: Defined on coordinates by sending (x, y, z) to (x, y, (x + 1)/y) :: sage: R.<t> = PolynomialRing(QQ) sage: A.<x,y,z> = AffineSpace(R, 3) sage: X = A.subscheme(x^2-y^2) sage: H = End(X) sage: H([x^2/(t*y), t*y^2, x*z]) Scheme endomorphism of Closed subscheme of Affine Space of dimension 3 over Univariate Polynomial Ring in t over Rational Field defined by: x^2 - y^2 Defn: Defined on coordinates by sending (x, y, z) to (x^2/(t*y), t*y^2, x*z) """ if check: if not isinstance(polys, (list, tuple)): raise TypeError("polys (=%s) must be a list or tuple" % polys) source_ring = parent.domain().ambient_space().coordinate_ring() target = parent.codomain().ambient_space() if len(polys) != target.ngens(): raise ValueError("there must be %s polynomials" % target.ngens()) try: polys = [source_ring(poly) for poly in polys] except TypeError: #maybe given quotient ring elements try: polys = [source_ring(poly.lift()) for poly in polys] except (TypeError, AttributeError): #must be a rational function since we cannot have #rational functions for quotient rings try: if not all(p.base_ring().fraction_field() == source_ring.base_ring().fraction_field() for p in polys): raise TypeError( "polys (=%s) must be rational functions in %s" % (polys, source_ring)) K = FractionField(source_ring) polys = [K(p) for p in polys] #polys = [source_ring(poly.numerator())/source_ring(poly.denominator()) for poly in polys] except TypeError: #can't seem to coerce raise TypeError( "polys (=%s) must be rational functions in %s" % (polys, source_ring)) self._is_prime_finite_field = is_PrimeFiniteField( polys[0].base_ring()) # Needed for _fast_eval and _fastpolys SchemeMorphism_polynomial.__init__(self, parent, polys, False)