diff --git a/src/sage/schemes/curves/affine_curve.py b/src/sage/schemes/curves/affine_curve.py index ce8c0ff0655..e1032861b2f 100644 --- a/src/sage/schemes/curves/affine_curve.py +++ b/src/sage/schemes/curves/affine_curve.py @@ -2118,8 +2118,10 @@ def function(self, f): INPUT: - - ``f`` -- an element of the coordinate ring of either the curve or its - ambient space. + - ``f`` -- an element of the fraction field of the coordinate ring of + the ambient space or the coordinate ring of the curve + + OUTPUT: An element of the function field of this curve. EXAMPLES:: @@ -2141,7 +2143,7 @@ def function(self, f): if f not in R and f.parent() is self.coordinate_ring(): f = f.lift() - phi = self._lift_to_function_field + phi = self._map_to_function_field num = R(f.numerator()) den = R(f.denominator()) return phi(num) / phi(den) @@ -2161,15 +2163,51 @@ def coordinate_functions(self): """ return self._coordinate_functions + def pull_from_function_field(self, f): + """ + Return the fraction corresponding to ``f``. + + INPUT: + + - ``f`` -- an element of the function field + + OUTPUT: + + A fraction of polynomials in the coordinate ring of the ambient space + of the curve. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(8), 2) + sage: C = Curve(x^5 + y^5 + x*y + 1) + sage: F = C.function_field() + sage: C.pull_from_function_field(F.gen()) + y + sage: C.pull_from_function_field(F.one()) + 1 + sage: C.pull_from_function_field(F.zero()) + 0 + sage: f1 = F.gen() + sage: f2 = F.base_ring().gen() + sage: C.function(C.pull_from_function_field(f1)) == f1 + True + sage: C.function(C.pull_from_function_field(f2)) == f2 + True + """ + return self._map_from_function_field(f) + @lazy_attribute def _nonsingular_model(self): """ Return the data of a nonsingular model of the curve. - The data consists of an abstract function field `M` and a map from the - coordinate ring `R` of the ambient space of the curve into the function - field. The coordinate ring of the curve is thus the quotient of `R` by - the kernel of the map. + The data consists of an abstract function field `M`, a map from the + fraction field of the coordinate ring `R` of the ambient space of the + curve to the function field, and the inverse map. + + The coordinate ring of the curve is the quotient of `R` by the kernel + of the map restricted to `R`. TESTS:: @@ -2178,21 +2216,28 @@ def _nonsingular_model(self): sage: C._nonsingular_model (Function field in z defined by z^3 + 10*x, Ring morphism: - From: Multivariate Polynomial Ring in x, y, z + From: Fraction Field of Multivariate Polynomial Ring in x, y, z over Finite Field of size 11 To: Function field in z defined by z^3 + 10*x Defn: x |--> x y |--> z^2 - z |--> z) + z |--> z, + Ring morphism: + From: Function field in z defined by z^3 + 10*x + To: Fraction Field of Multivariate Polynomial Ring in x, y, z + over Finite Field of size 11) """ + from sage.structure.sequence import Sequence + from sage.rings.fraction_field import FractionField from sage.rings.function_field.constructor import FunctionField + from sage.rings.function_field.maps import FunctionFieldRingMorphism k = self.base_ring() - I0 = self.defining_ideal() + I = self.defining_ideal() # invlex is the lex order with x < y < z for R = k[x,y,z] for instance - R = I0.parent().ring().change_ring(order='invlex') - I0 = I0.change_ring(R) + R = I.parent().ring().change_ring(order='invlex') + I0 = I.change_ring(R) n = R.ngens() names = R.variable_names() @@ -2228,6 +2273,7 @@ def _nonsingular_model(self): # syzygy for z. Now x is the generator of a rational function field F0; # y is the generator of the extension F1 of F0 by f3; z is the # generator of the extension F2 of F1 by f2. + basis = list(gbasis) syzygy = {} for i in range(n): @@ -2241,11 +2287,11 @@ def _nonsingular_model(self): basis.append(f) break - indep = [i for i in range(n) if i not in syzygy] - if len(indep) != 1: + # sanity check + indeps = [i for i in range(n) if i not in syzygy] + if len(indeps) != 1: raise TypeError("not a curve") - else: - indep = indep[0] + indep = indeps[0] F = FunctionField(k, names[indep]) coords = {indep: F.gen()} @@ -2258,20 +2304,60 @@ def _nonsingular_model(self): F = F.extension(f, names[i]) coords[i] = F.gen() - if F.base_field() is not F: # proper extension + proper_extension = F.base_field() is not F + + if proper_extension: N, from_N, to_N = F.simple_model() M, from_M, to_M = N.separable_model() coordinate_functions = tuple([to_M(to_N(F(coords[i]))) for i in range(n)]) - else: # rational function field - M = F + else: + M = F # is rational function field coordinate_functions = tuple([coords[i] for i in range(n)]) - lift_to_function_field = hom(R, M, coordinate_functions) + # map to M + + FR = FractionField(I.ring()) + map_to_function_field = hom(FR, M, coordinate_functions) + + # map from M + + def convert(f, i): + if i == indep: + i = i - 1 + if i < 0: + return f._x # fraction representing rational function field element + fx = f._x # polynomial representing function field element + if not fx: + fxlist = [fx.base_ring().zero()] + else: + fxlist = fx.list() + coeffs = Sequence(convert(c, i - 1) for c in fxlist) + B = coeffs.universe() + S = B[names[i]] + return S(coeffs) + + z = M.gen() + + if proper_extension: + Z = FR(convert(from_N(from_M(z)), n - 1)) + + def evaluate(f): + coeffs = f._x.list() + v = 0 + while coeffs: + v = v * Z + coeffs.pop()._x + return FR(v) + else: + def evaluate(f): + return FR(f._x) + + map_from_function_field = FunctionFieldRingMorphism(Hom(M, FR), evaluate) # sanity check - assert all(lift_to_function_field(f).is_zero() for f in I0.gens()) + assert all(map_to_function_field(f).is_zero() for f in I.gens()) + assert map_to_function_field(map_from_function_field(z)) == z - return M, lift_to_function_field + return M, map_to_function_field, map_from_function_field @lazy_attribute def _function_field(self): @@ -2288,17 +2374,17 @@ def _function_field(self): return self._nonsingular_model[0] @lazy_attribute - def _lift_to_function_field(self): + def _map_to_function_field(self): """ - Return the map to function field of the curve. + Return the map to the function field of the curve. TESTS:: sage: A. = AffineSpace(GF(11), 3) sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) - sage: C._lift_to_function_field + sage: C._map_to_function_field Ring morphism: - From: Multivariate Polynomial Ring in x, y, z + From: Fraction Field of Multivariate Polynomial Ring in x, y, z over Finite Field of size 11 To: Function field in z defined by z^3 + 10*x Defn: x |--> x @@ -2321,20 +2407,39 @@ def _coordinate_functions(self): """ return self._nonsingular_model[1].im_gens() + @lazy_attribute + def _map_from_function_field(self): + """ + Return the map from the function field of the curve. + + TESTS:: + + sage: A. = AffineSpace(GF(11), 3) + sage: C = Curve([x*z - y^2, y - z^2, x - y*z], A) + sage: C._map_from_function_field + Ring morphism: + From: Function field in z defined by z^3 + 10*x + To: Fraction Field of Multivariate Polynomial Ring in x, y, z + over Finite Field of size 11 + """ + return self._nonsingular_model[2] + @lazy_attribute def _singularities(self): """ - Return a list of the pairs of singular closed points and the places above it. + Return a list of the pairs of a singular closed point and the places + above it. TESTS:: - sage: A. = AffineSpace(GF(7^2), 2) # needs sage.rings.finite_rings - sage: C = Curve(x^2 - x^4 - y^4) # needs sage.rings.finite_rings - sage: C._singularities # long time # needs sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(7^2), 2) + sage: C = Curve(x^2 - x^4 - y^4) + sage: C._singularities # long time [(Point (x, y), [Place (x, 1/x*y^3 + 1/x*y^2 + 1), Place (x, 1/x*y^3 + 1/x*y^2 + 6)])] """ - to_F = self._lift_to_function_field + to_F = self._map_to_function_field sing = self.singular_subscheme() funcs = [] @@ -2370,9 +2475,10 @@ def singular_closed_points(self): EXAMPLES:: - sage: A. = AffineSpace(GF(7^2), 2) # needs sage.rings.finite_rings - sage: C = Curve(x^2 - x^4 - y^4) # needs sage.rings.finite_rings - sage: C.singular_closed_points() # needs sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(7^2), 2) + sage: C = Curve(x^2 - x^4 - y^4) + sage: C.singular_closed_points() [Point (x, y)] :: @@ -2550,7 +2656,7 @@ def places_on(self, point): sage: Cp = Curve(x^3*y + y^3*z + x*z^3) sage: C = Cp.affine_patch(0) """ - phi = self._lift_to_function_field + phi = self._map_to_function_field gs = [phi(g) for g in point.prime_ideal().gens()] fs = [g for g in gs if not g.is_zero()] f = fs.pop() @@ -2726,11 +2832,12 @@ class IntegralAffinePlaneCurve_finite_field(AffinePlaneCurve_finite_field, Integ EXAMPLES:: - sage: A. = AffineSpace(GF(8), 2) # needs sage.rings.finite_rings - sage: C = Curve(x^5 + y^5 + x*y + 1); C # needs sage.rings.finite_rings + sage: # needs sage.rings.finite_rings + sage: A. = AffineSpace(GF(8), 2) + sage: C = Curve(x^5 + y^5 + x*y + 1); C Affine Plane Curve over Finite Field in z3 of size 2^3 defined by x^5 + y^5 + x*y + 1 - sage: C.function_field() # needs sage.rings.finite_rings + sage: C.function_field() Function field in y defined by y^5 + x*y + x^5 + 1 """ _point = IntegralAffinePlaneCurvePoint_finite_field diff --git a/src/sage/schemes/curves/projective_curve.py b/src/sage/schemes/curves/projective_curve.py index 5a4f9f5f4ce..7c82026b3c1 100644 --- a/src/sage/schemes/curves/projective_curve.py +++ b/src/sage/schemes/curves/projective_curve.py @@ -2325,7 +2325,14 @@ def __call__(self, *args): def function(self, f): """ - Return the function field element coerced from ``x``. + Return the function field element corresponding to ``f``. + + INPUT: + + - ``f`` -- a fraction of homogeneous polynomials of the coordinate ring + of the ambient space of the curve + + OUTPUT: An element of the function field. EXAMPLES:: @@ -2343,7 +2350,7 @@ def function(self, f): - Place (y, z + 1) """ S = self.ambient_space().coordinate_ring() - phi = self._lift_to_function_field + phi = self._map_to_function_field num = S(f.numerator()) den = S(f.denominator()) if num.degree() != den.degree(): @@ -2373,6 +2380,40 @@ def coordinate_functions(self, i=None): inv = ~coords[i] return tuple([coords[j]*inv for j in range(len(coords)) if j != i]) + def pull_from_function_field(self, f): + """ + Return the fraction corresponding to ``f``. + + INPUT: + + - ``f`` -- an element of the function field + + OUTPUT: + + A fraction of homogeneous polynomials in the coordinate ring of the + ambient space of the curve. + + EXAMPLES:: + + sage: # needs sage.rings.finite_rings + sage: P. = ProjectiveSpace(GF(4), 2) + sage: C = Curve(x^5 + y^5 + x*y*z^3 + z^5) + sage: F = C.function_field() + sage: C.pull_from_function_field(F.gen()) + z/x + sage: C.pull_from_function_field(F.one()) + 1 + sage: C.pull_from_function_field(F.zero()) + 0 + sage: f1 = F.gen() + sage: f2 = F.base_ring().gen() + sage: C.function(C.pull_from_function_field(f1)) == f1 + True + sage: C.function(C.pull_from_function_field(f2)) == f2 + True + """ + return self._map_from_function_field(f) + @lazy_attribute def _function_field(self): """ @@ -2388,15 +2429,15 @@ def _function_field(self): return self._open_affine._function_field @lazy_attribute - def _lift_to_function_field(self): + def _map_to_function_field(self): """ - Return the map to function field of the curve. + Return the map to the function field of the curve. TESTS:: sage: P. = ProjectiveSpace(GF(5), 2) sage: C = Curve(y^2*z^7 - x^9 - x*z^8) - sage: C._lift_to_function_field + sage: C._map_to_function_field Ring morphism: From: Multivariate Polynomial Ring in x, y, z over Finite Field of size 5 To: Function field in z defined by z^8 + 4*y^2*z^7 + 1 @@ -2425,6 +2466,33 @@ def _coordinate_functions(self): coords.insert(self._open_affine_index, self._function_field.one()) return tuple(coords) + @lazy_attribute + def _map_from_function_field(self): + """ + Return the map from the function field of the curve. + + TESTS:: + + sage: P. = ProjectiveSpace(GF(5), 2) + sage: C = Curve(y^2*z^7 - x^9 - x*z^8) + sage: F = C.function_field() + sage: f = F.random_element() + sage: C.function(C._map_from_function_field(f)) == f + True + """ + F = self._function_field + S = self.ambient_space().coordinate_ring() + phi = self._open_affine._nonsingular_model[2] + i = self._open_affine_index + + def m(f): + pf = phi(f) + num = S(pf.numerator()).homogenize(i) + den = S(pf.denominator()).homogenize(i) + return num / den * S.gen(i) ** (den.total_degree() - num.total_degree()) + + return m + @lazy_attribute def _singularities(self): """ @@ -2442,7 +2510,7 @@ def _singularities(self): """ S = self.ambient_space().coordinate_ring() - to_F = self._lift_to_function_field + to_F = self._map_to_function_field sing = self.singular_subscheme() # singular locus # for each affine patch, places on which the dehomogenized polynomials @@ -2628,7 +2696,7 @@ def places_on(self, point): if not S.gen(i) in prime: break - phi = self._lift_to_function_field + phi = self._map_to_function_field denom = self._coordinate_functions[i] gs = [phi(f)/denom**f.degree() for f in prime.gens()] fs = [g for g in gs if not g.is_zero()]