From e777520afbd36613e52df9ed04144efc5cb4d0c6 Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Tue, 12 May 2015 13:42:59 -0400 Subject: [PATCH 1/3] 18409: fix output ring for projective dynatomic polynomial --- .../schemes/projective/projective_morphism.py | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 09b7e3bde09..a0c02d97cd2 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -508,7 +508,10 @@ def dynatomic_polynomial(self, period): For a map `f:\mathbb{P}^1 \to \mathbb{P}^1` this function computes the dynatomic polynomial. The dynatomic polynomial is the analog of the cyclotomic - polynomial and its roots are the points of formal period `period`. + polynomial and its roots are the points of formal period `period`. If possible the division is + done in the coordinate ring of ``self`` and a polynomial is returned. In rings where that is not possible, + a FractionField element will be returned. In certain cases, when the conversion back to a polynomial + fails, a SymbolRing element will be returned. ALGORITHM: @@ -543,12 +546,12 @@ def dynatomic_polynomial(self, period): OUTPUT: - If possible, a two variable polynomial in the coordinate ring of ``self``. - Otherwise a fraction field element of the coordinate ring of ``self`` + Otherwise a fraction field element of the coordinate ring of ``self``. Or, + a Symbolic Ring element. .. TODO:: - Do the division when the base ring is p-adic or a function field - so that the output is a polynomial. + Do the division when the base ring is p-adic so that the output is a polynomial. EXAMPLES:: @@ -732,16 +735,18 @@ def dynatomic_polynomial(self, period): except TypeError: # something Singular can't handle pass from sage.rings.padics.generic_nodes import is_pAdicField, is_pAdicRing - BR = self.domain().base_ring().base_ring() - if not (is_pAdicRing(BR) or is_pAdicField(BR)): - try: - PHI = PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage() - # do it again to divide out by denominators of coefficients - PHI = PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage() - if PHI.denominator() == 1: - PHI = self.coordinate_ring()(PHI) - except TypeError: #something Maxima can't handle - pass + if period != [0,1]: #period==[0,1] we don't need to do any division + BR = self.domain().base_ring().base_ring() + if not (is_pAdicRing(BR) or is_pAdicField(BR)): + try: + PHI = PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage() + # do it again to divide out by denominators of coefficients + PHI = PHI.numerator()._maxima_().divide(PHI.denominator())[0].sage() + if not is_FractionFieldElement(PHI): + from sage.symbolic.expression_conversions import polynomial + PHI = polynomial(PHI, ring=self.coordinate_ring()) + except (TypeError, NotImplementedError): #something Maxima, or the conversion, can't handle + pass return(PHI) def nth_iterate_map(self, n): From ac037e2e778d739335216429a5a1fb92ab71729f Mon Sep 17 00:00:00 2001 From: Ben Hutz Date: Thu, 14 May 2015 11:23:14 -0400 Subject: [PATCH 2/3] 18409: fix suggestions from review --- .../schemes/projective/projective_morphism.py | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index a0c02d97cd2..23a7016cbe9 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -551,7 +551,9 @@ def dynatomic_polynomial(self, period): .. TODO:: - Do the division when the base ring is p-adic so that the output is a polynomial. + - Do the division when the base ring is p-adic so that the output is a polynomial. + + - Convert back to a polynomial when the base ring is a function field (not over `\QQ` or `F_p`) EXAMPLES:: @@ -683,9 +685,9 @@ def dynatomic_polynomial(self, period): sage: P. = ProjectiveSpace(CC, 1) sage: H = Hom(P,P) - sage: f = H([x^2+(1+CC.0)*y^2,y^2]) + sage: f = H([x^2-CC.0/3*y^2,y^2]) sage: f.dynatomic_polynomial(2) - x^2 + x*y + (2.00000000000000 + 1.00000000000000*I)*y^2 + 0.666666666666667*x^2 + 0.333333333333333*y^2 :: @@ -695,45 +697,75 @@ def dynatomic_polynomial(self, period): sage: f = H ([x^2 + (t ^ 2 + 1) * y^2 , y^2 ]) sage: f.dynatomic_polynomial(2) x^2 + x*y + (t^2 + 2)*y^2 + + TESTS: + + We check that the dynatomic polynomial has the right parent (see :trac:`18409`):: + + sage: P. = ProjectiveSpace(QQbar,1) + sage: H = End(P) + sage: R = P.coordinate_ring() + sage: f = H([x^2-1/3*y^2,y^2]) + sage: f.dynatomic_polynomial(2).parent() + Multivariate Polynomial Ring in x, y over Algebraic Field + + :: + + sage: T. = QuadraticField(33) + sage: S. = PolynomialRing(T) + sage: P. = ProjectiveSpace(FractionField(S),1) + sage: H = End(P) + sage: f = H([t*x^2-1/t*y^2,y^2]) + sage: f.dynatomic_polynomial([1,2]).parent() + Multivariate Polynomial Ring in x, y over Fraction Field of Univariate Polynomial + Ring in t over Number Field in v with defining polynomial x^2 - 33 + + This one still does not work, some function fields still return Symoblic Ring elements:: + + sage: S. = FunctionField(CC) + sage: P. = ProjectiveSpace(S,1) + sage: H = End(P) + sage: R = P.coordinate_ring() + sage: f = H([t*x^2-1*y^2,t*y^2]) + sage: f.dynatomic_polynomial([1,2]).parent() + Symbolic Ring """ if self.domain().ngens() > 2: raise TypeError("Does not make sense in dimension >1") - if (isinstance(period, (list, tuple)) is False): + if not isinstance(period, (list, tuple)): period = [0, period] + x = self.domain().gen(0) + y = self.domain().gen(1) + F = self._polys + f = F if period[0] != 0: m = period[0] fm = self.nth_iterate_map(m) fm1 = self.nth_iterate_map(m - 1) n = period[1] PHI = 1; - x = self.domain().gen(0) - y = self.domain().gen(1) - F = self._polys - f = F - for d in range(1, n + 1): + for d in range(1, n): if n % d == 0: PHI = PHI * ((y*F[0] - x*F[1]) ** moebius(n/d)) - if d != n: # avoid extra iteration - F = [f[0](F[0], F[1]), f[1](F[0], F[1])] + F = [f[0](F[0], F[1]), f[1](F[0], F[1])] + PHI = PHI * (y*F[0] - x*F[1]) if m != 0: PHI = PHI(fm._polys)/ PHI(fm1._polys ) else: PHI = 1 - x = self.domain().gen(0) - y = self.domain().gen(1) - F = self._polys - f = F - for d in range(1, period[1] + 1): + for d in range(1, period[1]): if period[1] % d == 0: PHI = PHI * ((y*F[0] - x*F[1]) ** moebius(period[1]/d)) - if d != period[1]: # avoid extra iteration - F = [f[0](F[0], F[1]), f[1](F[0], F[1])] + F = [f[0](F[0], F[1]), f[1](F[0], F[1])] + PHI = PHI * (y*F[0] - x*F[1]) try: QR = PHI.numerator().quo_rem(PHI.denominator()) if QR[1] == 0: return(QR[0]) except TypeError: # something Singular can't handle pass + #even when the ring can be passed to singular in quo_rem, + #it can't always do the division, so we call Maxima from sage.rings.padics.generic_nodes import is_pAdicField, is_pAdicRing if period != [0,1]: #period==[0,1] we don't need to do any division BR = self.domain().base_ring().base_ring() From ef769e235d26291f297b88a18b5f438698186768 Mon Sep 17 00:00:00 2001 From: Vincent Delecroix <20100.delecroix@gmail.com> Date: Thu, 14 May 2015 22:51:47 +0200 Subject: [PATCH 3/3] 18409: further cleanup --- .../schemes/projective/projective_morphism.py | 52 +++++++++---------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/src/sage/schemes/projective/projective_morphism.py b/src/sage/schemes/projective/projective_morphism.py index 23a7016cbe9..385cdf42719 100644 --- a/src/sage/schemes/projective/projective_morphism.py +++ b/src/sage/schemes/projective/projective_morphism.py @@ -736,31 +736,29 @@ def dynatomic_polynomial(self, period): period = [0, period] x = self.domain().gen(0) y = self.domain().gen(1) - F = self._polys - f = F + f0, f1 = F0, F1 = self._polys + PHI = self.base_ring().one() + n = period[1] if period[0] != 0: m = period[0] fm = self.nth_iterate_map(m) fm1 = self.nth_iterate_map(m - 1) - n = period[1] - PHI = 1; for d in range(1, n): if n % d == 0: - PHI = PHI * ((y*F[0] - x*F[1]) ** moebius(n/d)) - F = [f[0](F[0], F[1]), f[1](F[0], F[1])] - PHI = PHI * (y*F[0] - x*F[1]) + PHI = PHI * ((y*F0 - x*F1) ** moebius(n/d)) + F0, F1 = f0(F0, F1), f1(F0, F1) + PHI = PHI * (y*F0 - x*F1) if m != 0: PHI = PHI(fm._polys)/ PHI(fm1._polys ) else: - PHI = 1 - for d in range(1, period[1]): - if period[1] % d == 0: - PHI = PHI * ((y*F[0] - x*F[1]) ** moebius(period[1]/d)) - F = [f[0](F[0], F[1]), f[1](F[0], F[1])] - PHI = PHI * (y*F[0] - x*F[1]) + for d in range(1, n): + if n % d == 0: + PHI = PHI * ((y*F0 - x*F1) ** moebius(n//d)) + F0, F1 = f0(F0, F1), f1(F0, F1) + PHI = PHI * (y*F0 - x*F1) try: QR = PHI.numerator().quo_rem(PHI.denominator()) - if QR[1] == 0: + if not QR[1]: return(QR[0]) except TypeError: # something Singular can't handle pass @@ -779,7 +777,7 @@ def dynatomic_polynomial(self, period): PHI = polynomial(PHI, ring=self.coordinate_ring()) except (TypeError, NotImplementedError): #something Maxima, or the conversion, can't handle pass - return(PHI) + return PHI def nth_iterate_map(self, n): r""" @@ -849,26 +847,28 @@ def nth_iterate_map(self, n): Defn: Defined on coordinates by sending (x : y : z) to (x^4 : x^2*z^2 : z^4) """ - if self.domain() != self.codomain(): + E = self.domain() + if E is not self.codomain(): raise TypeError("Domain and Codomain of function not equal") - if n < 0: + + D = int(n) + if D < 0: raise TypeError("Iterate number must be a positive integer") N = self.codomain().ambient_space().dimension_relative() + 1 F = list(self._polys) - D = Integer(n).digits(2) #need base 2 Coord_ring = self.codomain().coordinate_ring() if isinstance(Coord_ring, QuotientRing_generic): PHI = [Coord_ring.gen(i).lift() for i in range(N)] else: PHI = [Coord_ring.gen(i) for i in range(N)] - for i in range(len(D)): - T = tuple([F[j] for j in range(N)]) - for k in range(D[i]): - PHI = [PHI[j](T) for j in range(N)] - if i != len(D) - 1: #avoid extra iterate - F = [F[j](T) for j in range(N)] #'square' - H = Hom(self.domain(), self.codomain()) - return(H(PHI)) + + while D: + if D&1: + PHI = [PHI[j](*F) for j in range(N)] + if D > 1: #avoid extra iterate + F = [F[j](*F) for j in range(N)] #'square' + D >>= 1 + return End(E)(PHI) def nth_iterate(self, P, n, normalize=False): r"""