From 97d3bc384f9ff05e4a2cd881530998d897da291b Mon Sep 17 00:00:00 2001 From: Kwankyu Lee Date: Thu, 17 Jan 2019 18:21:15 +0900 Subject: [PATCH] Add places to global function fields --- .../en/reference/function_fields/index.rst | 2 + src/sage/rings/function_field/element.pyx | 59 +- .../rings/function_field/function_field.py | 362 +++++++- .../function_field_valuation.py | 32 +- src/sage/rings/function_field/ideal.py | 191 ++++- src/sage/rings/function_field/maps.py | 28 +- src/sage/rings/function_field/place.py | 775 ++++++++++++++++++ .../rings/function_field/valuation_ring.py | 216 +++++ 8 files changed, 1615 insertions(+), 50 deletions(-) create mode 100644 src/sage/rings/function_field/place.py create mode 100644 src/sage/rings/function_field/valuation_ring.py diff --git a/src/doc/en/reference/function_fields/index.rst b/src/doc/en/reference/function_fields/index.rst index fa39b438361..a670cafe8da 100644 --- a/src/doc/en/reference/function_fields/index.rst +++ b/src/doc/en/reference/function_fields/index.rst @@ -13,6 +13,8 @@ a divisor, are available for global function fields. sage/rings/function_field/element sage/rings/function_field/order sage/rings/function_field/ideal + sage/rings/function_field/place + sage/rings/function_field/valuation_ring sage/rings/function_field/maps sage/rings/function_field/constructor diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index 6af8017a680..4d15125222b 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -11,6 +11,12 @@ Arithmetic with rational functions:: sage: f = t - 1 sage: g = t^2 - 3 sage: h = f^2/g^3 + sage: h.valuation(t-1) + 2 + sage: h.valuation(t) + 0 + sage: h.valuation(t^2 - 3) + -3 AUTHORS: @@ -67,7 +73,7 @@ def make_FunctionFieldElement(parent, element_class, representing_element): sage: from sage.rings.function_field.element import make_FunctionFieldElement sage: K. = FunctionField(QQ) - sage: make_FunctionFieldElement(K, K._element_class, (x+1)/x) + sage: make_FunctionFieldElement(K, K.element_class, (x+1)/x) (x + 1)/x """ return element_class(parent, representing_element, reduce=False) @@ -792,27 +798,43 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): """ return self._x.denominator() - def valuation(self, v): + def valuation(self, place): """ - Return the valuation of the element with respect to a prime element. + Return the valuation of the rational function at the place. + + Rational function field places are associated with irreducible + polynomials. INPUT: - - ``v`` -- a prime element of the function field + - ``place`` -- a place or an irreducible polynomial EXAMPLES:: sage: K. = FunctionField(QQ) - sage: f = (t-1)^2 * (t+1) / (t^2 - 1/3)^3 - sage: f.valuation(t-1) + sage: f = (t - 1)^2*(t + 1)/(t^2 - 1/3)^3 + sage: f.valuation(t - 1) 2 sage: f.valuation(t) 0 sage: f.valuation(t^2 - 1/3) -3 + + sage: K. = FunctionField(GF(2)) + sage: p = K.places_finite()[0] + sage: (1/x^2).valuation(p) + -2 """ - R = self._parent._ring - return self._x.valuation(R(self._parent(v)._x)) + from .place import FunctionFieldPlace + + if not isinstance(place, FunctionFieldPlace): + # place is an irreducible polynomial + R = self._parent._ring + return self._x.valuation(R(self._parent(place)._x)) + + prime = place.prime_ideal() + ideal = prime.ring().ideal(self) + return prime.valuation(ideal) def is_square(self): """ @@ -907,4 +929,23 @@ cdef class FunctionFieldElement_global(FunctionFieldElement_polymod): """ Elements of global function fields """ - pass + + def valuation(self, place): + """ + Return the valuation of the element at the place. + + INPUT: + + - ``place`` -- a place of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_infinite()[0] + sage: y.valuation(p) + -1 + """ + prime = place.prime_ideal() + ideal = prime.ring().ideal(self) + return prime.valuation(ideal) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 1905f4bbee0..72a42e3b9a9 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -936,7 +936,9 @@ class FunctionField_polymod(FunctionField): False False """ - def __init__(self, polynomial, names, element_class=FunctionFieldElement_polymod, category=FunctionFields()): + Element = FunctionFieldElement_polymod + + def __init__(self, polynomial, names, category=None): """ Create a function field defined as an extension of another function field by adjoining a root of a univariate polynomial. @@ -984,14 +986,16 @@ def __init__(self, polynomial, names, element_class=FunctionFieldElement_polymod base_field = polynomial.base_ring() if not isinstance(base_field, FunctionField): raise TypeError("polynomial must be over a FunctionField") - self._element_class = element_class + self._base_field = base_field self._polynomial = polynomial - FunctionField.__init__(self, base_field, names=names, category = category) + FunctionField.__init__(self, base_field, names=names, + category=FunctionFields().or_subcategory(category)) self._hash = hash(polynomial) self._ring = self._polynomial.parent() + self._populate_coercion_lists_(coerce_list=[base_field, self._ring]) self._gen = self(self._ring.gen()) @@ -1026,8 +1030,8 @@ def _element_constructor_(self, x): y """ if isinstance(x, FunctionFieldElement): - return self._element_class(self, self._ring(x.element())) - return self._element_class(self, self._ring(x)) + return self.element_class(self, self._ring(x.element())) + return self.element_class(self, self._ring(x)) def gen(self, n=0): """ @@ -2464,6 +2468,8 @@ class FunctionField_global(FunctionField_polymod): sage: L Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) """ + Element = FunctionFieldElement_global + def __init__(self, polynomial, names): """ Initialize. @@ -2474,8 +2480,11 @@ def __init__(self, polynomial, names): sage: L.=K.extension(Y^3-(x^3-1)/(x^3-2)) sage: TestSuite(L).run() """ - FunctionField_polymod.__init__(self, polynomial, names, - element_class=FunctionFieldElement_global) + from .place import FunctionFieldPlace_global + + FunctionField_polymod.__init__(self, polynomial, names) + + self._place_class = FunctionFieldPlace_global def maximal_order(self): """ @@ -2576,6 +2585,201 @@ def _inversion_isomorphism(self): return M, F2self*M2F, F2M*self2F + def place_set(self): + """ + Return the set of all places of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: L.place_set() + Set of places of Function field in y defined by y^2 + y + (x^2 + 1)/x + """ + from .place import PlaceSet + return PlaceSet(self) + + def residue_field(self, place, name=None): + """ + Return the residue field associated with the place along with the maps + from and to the residue field. + + INPUT: + + - ``place`` -- place of the function field + + - ``name`` -- string; name of the generator of the residue field + + The domain of the map to the residue field is the discrete valuation + ring associated with the place. + + The discrete valuation ring is defined as the ring of all elements of + the function field with nonnegative valuation at the place. The maximal + ideal is the set of elements of positive valuation. The residue field + is then the quotient of the discrete valuation ring by its maximal + ideal. + + If an element not in the valuation ring is applied to the map, an + exception ``TypeError`` is raised. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: R, fr_R, to_R = L.residue_field(p) + sage: R + Finite Field of size 2 + sage: f = 1 + y + sage: f.valuation(p) + -1 + sage: to_R(f) + Traceback (most recent call last): + ... + TypeError: ... + sage: (1+1/f).valuation(p) + 0 + sage: to_R(1 + 1/f) + 1 + sage: [fr_R(e) for e in R] + [0, 1] + """ + return place.residue_field(name=name) + + def places(self, degree=1): + """ + Return a list of the places with ``degree``. + + INPUT: + + - ``degree`` -- positive integer (default: `1`) + + EXAMPLES:: + + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4 + t - x^5) + sage: L.places(1) + [Place (1/x, 1/x^4*y^3), Place (x, y), Place (x, y + 1)] + """ + return self.places_infinite(degree) + self.places_finite(degree) + + def places_finite(self, degree=1): + """ + Return a list of the finite places with ``degree``. + + INPUT: + + - ``degree`` -- positive integer (default: `1`) + + EXAMPLES:: + + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4+t-x^5) + sage: L.places_finite(1) + [Place (x, y), Place (x, y + 1)] + """ + return list(self._places_finite(degree)) + + def _places_finite(self, degree): + """ + Return a generator of finite places with ``degree``. + + INPUT: + + - ``degree`` -- positive integer + + EXAMPLES:: + + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4+t-x^5) + sage: L._places_finite(1) + + """ + O = self.maximal_order() + K = self.base_field() + + from sage.rings.integer import Integer + degree = Integer(degree) + + for d in degree.divisors(): + for p in K.places_finite(degree=d): + for prime,_,_ in O.decomposition(p.prime_ideal()): + place = prime.place() + if place.degree() == degree: + yield place + + def places_infinite(self, degree=1): + """ + Return a list of the infinite places with ``degree``. + + INPUT: + + - ``degree`` -- positive integer (default: `1`) + + EXAMPLES:: + + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4+t-x^5) + sage: L.places_infinite(1) + [Place (1/x, 1/x^4*y^3)] + """ + return list(self._places_infinite(degree)) + + def _places_infinite(self, degree): + """ + Return a generator of *infinite* places with ``degree``. + + INPUT: + + - ``degree`` -- positive integer + + EXAMPLES:: + + sage: F. = GF(2) + sage: K. = FunctionField(F) + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4+t-x^5) + sage: L._places_infinite(1) + + """ + Oinf = self.maximal_order_infinite() + for prime,_,_ in Oinf.decomposition(): + place = prime.place() + if place.degree() == degree: + yield place + + def valuation_ring(self, place): + """ + Return the valuation ring associated with the place. + + INPUT: + + - ``place`` -- place of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: L.valuation_ring(p) + Valuation ring at Place (x, x*y) + + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) + sage: p = L.places_infinite()[0] + sage: L.valuation_ring(p) + Valuation ring at Place (1/x, ((2*x^3 + 1)/x^3)*y + 3) + """ + return place.valuation_ring() + class FunctionField_global_integral(FunctionField_global): """ Global function fields defined by an irreducible and separable polynomial, @@ -2786,9 +2990,9 @@ class RationalFunctionField(FunctionField): To: Rational function field in tbar over Rational Field Defn: t |--> tbar """ - def __init__(self, constant_field, names, - element_class = FunctionFieldElement_rational, - category=FunctionFields()): + Element = FunctionFieldElement_rational + + def __init__(self, constant_field, names, category=None): """ Initialize. @@ -2814,14 +3018,19 @@ def __init__(self, constant_field, names, names = (names, ) if not constant_field.is_field(): raise TypeError("constant_field must be a field") - self._element_class = element_class + self._constant_field = constant_field - FunctionField.__init__(self, self, names=names, category = category) + + FunctionField.__init__(self, self, names=names, category=FunctionFields().or_subcategory(category)) + + from .place import FunctionFieldPlace_rational + self._place_class = FunctionFieldPlace_rational + R = constant_field[names[0]] self._hash = hash((constant_field, names)) self._ring = R - self._field = R.fraction_field() + hom = Hom(self._field, self) from .maps import FractionFieldToFunctionField self.register_coercion(hom.__make_element_class__(FractionFieldToFunctionField)(hom.domain(), hom.codomain())) @@ -2911,7 +3120,7 @@ def _element_constructor_(self, x): """ if isinstance(x, FunctionFieldElement): - return FunctionFieldElement_rational(self, self._field(x._x)) + return self.element_class(self, self._field(x._x)) try: x = self._field(x) except TypeError as Err: @@ -2921,7 +3130,7 @@ def _element_constructor_(self, x): except AttributeError: pass raise Err - return FunctionFieldElement_rational(self, x) + return self.element_class(self, x) def _to_constant_base_field(self, f): r""" @@ -3350,6 +3559,19 @@ def field(self): """ return self._field + def place_set(self): + """ + Return the set of all places of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)) + sage: K.place_set() + Set of places of Rational function field in t over Finite Field of size 7 + """ + from .place import PlaceSet + return PlaceSet(self) + @cached_method def maximal_order(self): """ @@ -3499,4 +3721,112 @@ class RationalFunctionField_global(RationalFunctionField): """ Rational function field over finite fields. """ - pass + def places(self, degree=1): + """ + Return all places of the degree. + + INPUT: + + - ``degree`` -- (default: 1) a positive integer + + EXAMPLES:: + + sage: F. = FunctionField(GF(5)) + sage: F.places() + [Place (1/x), + Place (x), + Place (x + 1), + Place (x + 2), + Place (x + 3), + Place (x + 4)] + """ + if degree == 1: + return [self.place_infinite()] + self.places_finite(degree) + else: + return self.places_finite(degree) + + def places_finite(self, degree=1): + """ + Return the finite places of the degree. + + INPUT: + + - ``degree`` -- (default: 1) a positive integer + + EXAMPLES:: + + sage: F. = FunctionField(GF(5)) + sage: F.places_finite() + [Place (x), Place (x + 1), Place (x + 2), Place (x + 3), Place (x + 4)] + """ + return list(self._places_finite(degree)) + + def _places_finite(self, degree=1): + """ + Return a generator for all monic irreducible polynomials of the degree. + + INPUT: + + - ``degree`` -- (default: 1) a positive integer + + EXAMPLES:: + + sage: F. = FunctionField(GF(5)) + sage: F._places_finite() + + """ + O = self.maximal_order() + R = O._ring + G = R.polynomials(of_degree=degree) + for g in G: + if not (g.is_monic() and g.is_irreducible()): + continue + yield O.ideal(g).place() + + def place_infinite(self): + """ + Return the unique place at infinity. + + EXAMPLES:: + + sage: F. = FunctionField(GF(5)) + sage: F.place_infinite() + Place (1/x) + """ + return self.maximal_order_infinite().prime_ideal().place() + + def residue_field(self, place, name=None): + """ + Return the residue field of the place along with the maps from + and to it. + + INPUT: + + - ``place`` -- place of the function field + + - ``name`` -- string; name of the generator of the residue field + + EXAMPLES:: + + sage: F. = FunctionField(GF(5)) + sage: p = F.places_finite(2)[0] + sage: R, fr_R, to_R = F.residue_field(p) + sage: R + Finite Field in z2 of size 5^2 + sage: to_R(x) in R + True + """ + return place.residue_field(name=name) + + def valuation_ring(self, place): + """ + Return the valuation ring at the place. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) + sage: p = F.place_infinite() + sage: F.valuation_ring(p) + Valuation ring at Place (1/x) + """ + return place.valuation_ring() diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/function_field_valuation.py index 7d78b17e07d..d2d2364ae7f 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/function_field_valuation.py @@ -170,7 +170,7 @@ class FunctionFieldValuationFactory(UniqueFactory): isomorphisms to and from that function field EXAMPLES:: - + sage: K. = FunctionField(QQ) sage: v = K.valuation(1); v # indirect doctest (x - 1)-adic valuation @@ -202,7 +202,7 @@ def create_key_and_extra_args(self, domain, prime): The normalization is, however, not smart enough, to unwrap substitutions that turn out to be trivial:: - + sage: w = GaussValuation(R, QQ.valuation(2)) sage: w = K.valuation(w) sage: w is K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))) @@ -695,7 +695,7 @@ def __init__(self, parent, base_valuation): sage: from sage.rings.function_field.function_field_valuation import InducedRationalFunctionFieldValuation_base sage: isinstance(v, InducedRationalFunctionFieldValuation_base) True - + """ FunctionFieldValuation_base.__init__(self, parent) @@ -714,7 +714,7 @@ def uniformizer(self): sage: K. = FunctionField(QQ) sage: K.valuation(x).uniformizer() x - + """ return self.domain()(self._base_valuation.uniformizer()) @@ -840,7 +840,7 @@ def extensions(self, L): # comes from an extension of the field of constants # Condition "L.base() is L" is important so we do not call this # code for extensions from K(x) to K(x)(y) - + # We extend the underlying valuation on the polynomial ring W = self._base_valuation.extensions(L._ring) return [L.valuation(w) for w in W] @@ -857,7 +857,7 @@ def _call_(self, f): sage: v = K.valuation(x) # indirect doctest sage: v((x+1)/x^2) -2 - + """ return self._base_valuation(f.numerator()) - self._base_valuation(f.denominator()) @@ -896,7 +896,7 @@ def simplify(self, f, error=None, force=False): Produce an element which differs from ``f`` by an element of valuation strictly greater than the valuation of ``f`` (or strictly greater than ``error`` if set.) - + If ``force`` is not set, then expensive simplifications may be avoided. EXAMPLES:: @@ -959,7 +959,7 @@ def _relative_size(self, f): of coefficients is going to lead to a significant shrinking of the coefficients of ``f``. - EXAMPLES:: + EXAMPLES:: sage: K. = FunctionField(QQ) sage: v = K.valuation(0) @@ -1012,13 +1012,13 @@ class FiniteRationalFunctionFieldValuation(InducedRationalFunctionFieldValuation def __init__(self, parent, base_valuation): r""" TESTS:: - + sage: K. = FunctionField(QQ) sage: v = K.valuation(x + 1) sage: from sage.rings.function_field.function_field_valuation import FiniteRationalFunctionFieldValuation sage: isinstance(v, FiniteRationalFunctionFieldValuation) True - + """ InducedRationalFunctionFieldValuation_base.__init__(self, parent, base_valuation) ClassicalFunctionFieldValuation_base.__init__(self, parent) @@ -1175,7 +1175,7 @@ class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValua isomorphic function field. EXAMPLES:: - + sage: K. = FunctionField(GF(2)) sage: v = K.valuation(1/x); v Valuation at the infinite place @@ -1184,13 +1184,13 @@ class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValua def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_valuation_domain): r""" TESTS:: - + sage: K. = FunctionField(GF(2)) sage: v = K.valuation(1/x) sage: from sage.rings.function_field.function_field_valuation import FunctionFieldMappedValuation_base sage: isinstance(v, FunctionFieldMappedValuation_base) True - + """ FunctionFieldValuation_base.__init__(self, parent) MappedValuation_base.__init__(self, parent, base_valuation) @@ -1296,7 +1296,7 @@ class FunctionFieldMappedValuationRelative_base(FunctionFieldMappedValuation_bas other function field is the identity on the constant field. EXAMPLES:: - + sage: K. = FunctionField(GF(2)) sage: v = K.valuation(1/x); v Valuation at the infinite place @@ -1305,13 +1305,13 @@ class FunctionFieldMappedValuationRelative_base(FunctionFieldMappedValuation_bas def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_valuation_domain): r""" TESTS:: - + sage: K. = FunctionField(GF(2)) sage: v = K.valuation(1/x) sage: from sage.rings.function_field.function_field_valuation import FunctionFieldMappedValuationRelative_base sage: isinstance(v, FunctionFieldMappedValuationRelative_base) True - + """ FunctionFieldMappedValuation_base.__init__(self, parent, base_valuation, to_base_valuation_domain, from_base_valuation_domain) if self.domain().constant_base_field() is not base_valuation.domain().constant_base_field(): diff --git a/src/sage/rings/function_field/ideal.py b/src/sage/rings/function_field/ideal.py index a78afaf5da6..8ae7a9bdd13 100644 --- a/src/sage/rings/function_field/ideal.py +++ b/src/sage/rings/function_field/ideal.py @@ -88,12 +88,11 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -import operator import itertools -from functools import reduce from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import lazy_import +from sage.misc.lazy_attribute import lazy_attribute from sage.structure.parent import Parent from sage.structure.element import Element @@ -109,6 +108,7 @@ from sage.rings.ideal import Ideal_generic lazy_import('sage.matrix.constructor', 'matrix') +lazy_import('sage.rings.function_field.divisor', 'FunctionFieldDivisor') class FunctionFieldIdeal(Element): @@ -479,6 +479,53 @@ def gens_over_base(self): """ return (self._gen,) + def valuation(self, ideal): + """ + Return the valuation of the ideal at this prime ideal. + + INPUT: + + - ``ideal`` -- fractional ideal + + EXAMPLES:: + + sage: F. = FunctionField(QQ) + sage: O = F.maximal_order() + sage: I = O.ideal(x^2*(x^2+x+1)^3) + sage: [f.valuation(I) for f,_ in I.factor()] + [2, 3] + """ + if not self.is_prime(): + raise TypeError("not a prime ideal") + + O = self.ring() + d = ideal.denominator() + return self._valuation(d*ideal) - self._valuation(O.ideal(d)) + + def _valuation(self, ideal): + """ + Return the valuation of the integral ideal at this prime ideal. + + INPUT: + + - ``ideal`` -- ideal + + EXAMPLES:: + + sage: F. = FunctionField(QQ) + sage: O = F.maximal_order() + sage: p = O.ideal(x) + sage: p.valuation(O.ideal(x+1)) # indirect doctest + 0 + sage: p.valuation(O.ideal(x^2)) # indirect doctest + 2 + sage: p.valuation(O.ideal(1/x^3)) # indirect doctest + -3 + sage: p.valuation(O.ideal(0)) # indirect doctest + +Infinity + """ + return ideal.gen().valuation(self.gen()) + def factor(self): """ Return the factorization of this ideal. @@ -518,6 +565,28 @@ def _factor(self): factors.append( (self.ring().ideal(f), m) ) return factors + def place(self): + """ + Return the place associated with the prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x^2+x+1) + sage: I.place() + Traceback (most recent call last): + ... + TypeError: not a prime ideal + sage: I = O.ideal(x^3+x+1) + sage: I.place() + Place (x^3 + x + 1) + """ + if not self.is_prime(): + raise TypeError("not a prime ideal") + + place_set = self.ring().fraction_field().place_set() + return place_set.element_class(place_set, self) class FunctionFieldIdeal_module(FunctionFieldIdeal, Ideal_generic): """ @@ -1281,8 +1350,8 @@ def module(self): @cached_method def gens_over_base(self): """ - Return the generators of this ideal as a module over the - maximal order of the base rational function field. + Return the generators of this ideal as a module over the maximal order + of the base rational function field. EXAMPLES:: @@ -1299,11 +1368,28 @@ def gens_over_base(self): sage: I.gens_over_base() (x^3 + 1, y + x) """ + gens, d = self._gens_over_base + return tuple([~d * b for b in gens]) + + @lazy_attribute + def _gens_over_base(self): + """ + Return the generators of the integral ideal, which is the denominator + times the fractional ideal, together with the denominator. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(y^2 - x^3*y - x) + sage: O = L.maximal_order() + sage: I = O.ideal(1/y) + sage: I._gens_over_base + ([x, y], x) + """ gens = [] for row in self._hnf: gens.append(sum([c1*c2 for c1,c2 in zip(row, self._ring.basis())])) - denom_inv = ~self._denominator - return tuple([denom_inv * b for b in gens]) + return gens, self._denominator def gens(self): """ @@ -1393,7 +1479,9 @@ def _gens_two(self): hnf = self._hnf - norm = reduce(operator.mul, hnf.diagonal()) + norm = 1 + for e in hnf.diagonal(): + norm *= e if norm.is_constant(): # unit ideal return (F(1),) @@ -1650,7 +1738,10 @@ def norm(self): sage: i2.norm() == y.norm() True """ - return reduce(operator.mul, self.basis_matrix().diagonal()) + n = 1 + for e in self.basis_matrix().diagonal(): + n *= e + return n @cached_method def is_prime(self): @@ -1785,6 +1876,32 @@ def prime_below(self): """ return self._prime_below + def place(self): + """ + Return the place corresponding to the prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: [f.place() for f,_ in I.factor()] + [Place (x, (1/(x^3 + x^2 + x))*y^2), + Place (x^2 + x + 1, (1/(x^3 + x^2 + x))*y^2)] + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: [f.place() for f,_ in I.factor()] + [Place (x, x*y), Place (x + 1, x*y)] + """ + if not self.is_prime(): + raise TypeError("not a prime ideal") + + place_set = self.ring().fraction_field().place_set() + return place_set.element_class(place_set, self) class FunctionFieldIdealInfinite(FunctionFieldIdeal): """ @@ -2023,6 +2140,25 @@ def factor(self): factors = [(self.ring().ideal(g), m)] return Factorization(factors, cr=True) + def place(self): + """ + Return the place corresponding to the prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x+1)/(x^3+1)) + sage: p = I.factor()[0][0] + sage: p.place() + Place (1/x) + """ + if not self.is_prime(): + raise TypeError("not a prime ideal") + + place_set = self.ring().fraction_field().place_set() + return place_set.element_class(place_set, self) + def valuation(self, ideal): """ Return the valuation of ``ideal`` at this prime ideal. @@ -2708,6 +2844,45 @@ def _factor(self): return factors + def place(self): + """ + Return the place corresponding to the prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 + t^2 - x^4) + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() + (Ideal (1/x,1/x^3*y^2) of Maximal infinite order of Function field + in y defined by y^3 + y^2 + 2*x^4)^3 + sage: J = I.factor()[0][0] + sage: J.is_prime() + True + sage: J.place() + Place (1/x, 1/x^3*y^2) + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(1/x) + sage: I.factor() + (Ideal (1/x,1/x*y) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x)^2 + sage: J = I.factor()[0][0] + sage: J.is_prime() + True + sage: J.place() + Place (1/x, 1/x*y) + """ + if not self.is_prime(): + raise ValueError("not a prime ideal") + + place_set = self.ring().fraction_field().place_set() + + return place_set.element_class(place_set, self) + def valuation(self, ideal): """ Return the valuation of ``ideal`` with respect to this prime ideal. diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index 80bd01208a4..7ed5c20a7ab 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -44,7 +44,7 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.categories.morphism import Morphism +from sage.categories.morphism import Morphism, SetMorphism from sage.categories.map import Map from sage.rings.morphism import RingHomomorphism @@ -924,3 +924,29 @@ def section(self): parent = Hom(self.codomain(), self.domain()) return parent.__make_element_class__(FunctionFieldToFractionField)(parent) +class FunctionFieldRingMorphism(SetMorphism): + """ + Ring homomorphism. + """ + def _repr_(self): + """ + Return the string representaton of the map. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: R = p.valuation_ring() + sage: k, fr_k, to_k = R.residue_field() + sage: k + Finite Field of size 2 + sage: fr_k + Ring morphism: + From: Finite Field of size 2 + To: Valuation ring at Place (x, x*y) + """ + s = "Ring morphism:" + s += "\n From: {}".format(self.domain()) + s += "\n To: {}".format(self.codomain()) + return s diff --git a/src/sage/rings/function_field/place.py b/src/sage/rings/function_field/place.py new file mode 100644 index 00000000000..760fe288bd7 --- /dev/null +++ b/src/sage/rings/function_field/place.py @@ -0,0 +1,775 @@ +""" +Places of function fields + +Sage can find and compute with places on global function fields. + +EXAMPLES: + +All rational places of the function field can be computed:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: L.places() + [Place (1/x, 1/x^3*y^2 + 1/x), + Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1), + Place (x, y)] + +The residue field associated with a place is given as an extension of the +constant field:: + + sage: F. = FunctionField(GF(2)) + sage: O = F.maximal_order() + sage: p = O.ideal(x^2 + x + 1).place() + sage: k, fr_k, to_k = p.residue_field() + sage: k + Finite Field in z2 of size 2^2 + +The isomorphisms are between the valuation ring and the residue field:: + + sage: fr_k + Ring morphism: + From: Finite Field in z2 of size 2^2 + To: Valuation ring at Place (x^2 + x + 1) + sage: to_k + Ring morphism: + From: Valuation ring at Place (x^2 + x + 1) + To: Finite Field in z2 of size 2^2 + +AUTHORS: + +- Kwankyu Lee (2017-04-30): initial version + +""" +#***************************************************************************** +# Copyright (C) 2016 Kwankyu Lee +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** +from __future__ import absolute_import + +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_import import lazy_import + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent +from sage.structure.element import Element +from sage.structure.richcmp import richcmp + +from sage.categories.sets_cat import Sets + +from sage.modules.free_module_element import vector + +lazy_import('sage.matrix.constructor', 'matrix') +lazy_import('sage.rings.function_field.divisor', 'prime_divisor') + +class FunctionFieldPlace(Element): + """ + Places of function fields. + + INPUT: + + - ``field`` -- function field + + - ``prime`` -- prime ideal associated with the place + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _. = K[] + sage: L.=K.extension(Y^3 + x + x^3*Y) + sage: L.places_finite()[0] + Place (x, y) + """ + def __init__(self, parent, prime): + """ + Initialize the place. + + TESTS:: + + sage: K.=FunctionField(GF(2)); _. = K[] + sage: L.=K.extension(Y^3 + x + x^3*Y) + sage: p = L.places_finite()[0] + sage: TestSuite(p).run() + """ + Element.__init__(self, parent) + + self._prime = prime + + def __hash__(self): + """ + Return the hash of the place. + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _. = K[] + sage: L.=K.extension(Y^3 + x + x^3*Y) + sage: p = L.places_finite()[0] + sage: {p: 1} + {Place (x, y): 1} + """ + return hash((self.function_field(), self._prime)) + + def _repr_(self): + """ + Return the string representation of the place. + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _.=K[] + sage: L.=K.extension(Y^3+x+x^3*Y) + sage: p = L.places_finite()[0] + sage: p + Place (x, y) + """ + try: + gens = self._prime.gens_two() + except AttributeError: + gens = self._prime.gens() + gens_str = ', '.join(repr(g) for g in gens) + return "Place ({})".format(gens_str) + + def _richcmp_(self, other, op): + """ + Compare the place with ``other`` place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: p1, p2, p3 = L.places()[:3] + sage: p1 < p2 + True + sage: p2 < p1 + False + sage: p1 == p3 + False + """ + # First compare the rings. In effect, infinite + # places are compared less than finite places so that + # they are listed first. + return richcmp((self._prime.ring(), self._prime), + (other._prime.ring(), other._prime), op) + + def function_field(self): + """ + Return the function field to which the place belongs. + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _.=K[] + sage: L.=K.extension(Y^3+x+x^3*Y) + sage: p = L.places()[0] + sage: p.function_field() == L + True + """ + return self.parent()._field + + def prime_ideal(self): + """ + Return the prime ideal associated with the place. + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _.=K[] + sage: L.=K.extension(Y^3+x+x^3*Y) + sage: p = L.places()[0] + sage: p.prime_ideal() + Ideal (1/x,1/x^3*y^2 + 1/x) of Maximal infinite order of Function field + in y defined by y^3 + x^3*y + x + """ + return self._prime + +class FunctionFieldPlace_rational(FunctionFieldPlace): + """ + Places of rational function field. + """ + def degree(self): + """ + Return the degree of the place. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) + sage: O = F.maximal_order() + sage: i = O.ideal(x^2+x+1) + sage: p = i.place() + sage: p.degree() + 2 + """ + if self.is_infinite_place(): + return 1 + else: + return self._prime.gen().numerator().degree() + + def is_infinite_place(self): + """ + Return ``True`` if the place is at infinite. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) + sage: F.places() + [Place (1/x), Place (x), Place (x + 1)] + sage: [p.is_infinite_place() for p in F.places()] + [True, False, False] + """ + F = self.function_field() + return self.prime_ideal().ring() == F.maximal_order_infinite() + + def local_uniformizer(self): + """ + Return a local uniformizer of the place. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) + sage: F.places() + [Place (1/x), Place (x), Place (x + 1)] + sage: [p.local_uniformizer() for p in F.places()] + [1/x, x, x + 1] + """ + return self.prime_ideal().gen() + + def residue_field(self, name=None): + """ + Return the residue field of the place. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) + sage: O = F.maximal_order() + sage: p = O.ideal(x^2 + x + 1).place() + sage: k, fr_k, to_k = p.residue_field() + sage: k + Finite Field in z2 of size 2^2 + sage: fr_k + Ring morphism: + From: Finite Field in z2 of size 2^2 + To: Valuation ring at Place (x^2 + x + 1) + sage: to_k + Ring morphism: + From: Valuation ring at Place (x^2 + x + 1) + To: Finite Field in z2 of size 2^2 + """ + return self.valuation_ring().residue_field(name=name) + + def _residue_field(self, name=None): + """ + Return the residue field of the place along with the maps from + and to it. + + INPUT: + + - ``name`` -- string; name of the generator of the residue field + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) + sage: O = F.maximal_order() + sage: i = O.ideal(x^2+x+1) + sage: p = i.place() + sage: R, fr, to = p._residue_field() + sage: R + Finite Field in z2 of size 2^2 + sage: [fr(e) for e in R.list()] + [0, x, x + 1, 1] + sage: to(x*(x+1)) == to(x) * to(x+1) + True + """ + F = self.function_field() + prime = self.prime_ideal() + + if self.is_infinite_place(): + K = F.constant_base_field() + + def from_K(e): + return F(e) + + def to_K(f): + n = f.numerator() + d = f.denominator() + + n_deg = n.degree() + d_deg =d.degree() + + if n_deg < d_deg: + return K(0) + elif n_deg == d_deg: + return n.lc() / d.lc() + else: + raise TypeError("not in the valuation ring") + else: + O = F.maximal_order() + K, from_K, _to_K = O._residue_field(prime, name=name) + + def to_K(f): + if f in O: # f.denominator() is 1 + return _to_K(f.numerator()) + else: + d = F(f.denominator()) + n = d * f + + nv = prime.valuation(O.ideal(n)) + dv = prime.valuation(O.ideal(d)) + + if nv > dv: + return K(0) + elif dv > nv: + raise TypeError("not in the valuation ring") + + s = ~prime.gen() + rd = d * s**dv # in O but not in prime + rn = n * s**nv # in O but not in prime + return to_K(rn) / to_K(rd) + + return K, from_K, to_K + + def valuation_ring(self): + """ + Return the valuation ring at the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: p.valuation_ring() + Valuation ring at Place (x, x*y) + """ + from .valuation_ring import FunctionFieldValuationRing_global + + return FunctionFieldValuationRing_global(self.function_field(), self) + +class FunctionFieldPlace_global(FunctionFieldPlace): + """ + Places of function fields + """ + def place_below(self): + """ + Return the place lying below the place. + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _.=K[] + sage: L.=K.extension(Y^3+x+x^3*Y) + sage: OK = K.maximal_order() + sage: OL = L.maximal_order() + sage: p = OK.ideal(x^2 + x + 1) + sage: dec = OL.decomposition(p) + sage: q = dec[0][0].place() + sage: q.place_below() + Place (x^2 + x + 1) + """ + return self.prime_ideal().prime_below().place() + + def relative_degree(self): + """ + Return the relative degree of the place. + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _.=K[] + sage: L.=K.extension(Y^3+x+x^3*Y) + sage: OK = K.maximal_order() + sage: OL = L.maximal_order() + sage: p = OK.ideal(x^2 + x + 1) + sage: dec = OL.decomposition(p) + sage: q = dec[0][0].place() + sage: q.relative_degree() + 1 + """ + return self._prime._relative_degree + + def degree(self): + """ + Return the degree of the place. + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _.=K[] + sage: L.=K.extension(Y^3+x+x^3*Y) + sage: OK = K.maximal_order() + sage: OL = L.maximal_order() + sage: p = OK.ideal(x^2 + x + 1) + sage: dec = OL.decomposition(p) + sage: q = dec[0][0].place() + sage: q.degree() + 2 + """ + return self.relative_degree() * self.place_below().degree() + + def is_infinite_place(self): + """ + Return ``True`` if the place is above the unique infinite place + of the underlying rational function field. + + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _.=K[] + sage: L.=K.extension(Y^3+x+x^3*Y) + sage: pls = L.places() + sage: [p.is_infinite_place() for p in pls] + [True, True, False] + sage: [p.place_below() for p in pls] + [Place (1/x), Place (1/x), Place (x)] + """ + return self.place_below().is_infinite_place() + + def local_uniformizer(self): + """ + Return an element of the function field that has a simple zero + at the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^3 + x^3*Y + x) + sage: pls = L.places() + sage: [p.local_uniformizer().valuation(p) for p in pls] + [1, 1, 1, 1, 1] + """ + gens = self._prime.gens() + for g in gens: + if g.valuation(self) == 1: + return g + assert False, "Internal error" + + def residue_field(self, name=None): + """ + Return the residue field of the place. + + INPUT: + + - ``name`` -- string; name of the generator of the residue field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: k, fr_k, to_k = p.residue_field() + sage: k + Finite Field of size 2 + sage: fr_k + Ring morphism: + From: Finite Field of size 2 + To: Valuation ring at Place (x, x*y) + sage: to_k + Ring morphism: + From: Valuation ring at Place (x, x*y) + To: Finite Field of size 2 + sage: to_k(y) + Traceback (most recent call last): + ... + TypeError: y fails to convert into the map's domain + Valuation ring at Place (x, x*y)... + sage: to_k(1/y) + 0 + sage: to_k(y/(1+y)) + 1 + """ + return self.valuation_ring().residue_field(name=name) + + @cached_method + def _residue_field(self, name=None): + """ + Return the residue field of the place along with the functions + mapping from and to it. + + INPUT: + + - ``name`` -- string (default: `None`); name of the generator + of the residue field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: k,fr_k,to_k = p._residue_field() + sage: k + Finite Field of size 2 + sage: [fr_k(e) for e in k] + [0, 1] + + sage: K. = FunctionField(GF(9)); _. = K[] + sage: L. = K.extension(Y^3 + Y - x^4) + sage: p = L.places()[-1] + sage: p.residue_field() + (Finite Field in z2 of size 3^2, Ring morphism: + From: Finite Field in z2 of size 3^2 + To: Valuation ring at Place (x + 1, y + 2*z2), Ring morphism: + From: Valuation ring at Place (x + 1, y + 2*z2) + To: Finite Field in z2 of size 3^2) + """ + F = self.function_field() + prime = self.prime_ideal() + + if self.is_infinite_place(): + _F, from_F, to_F = F._inversion_isomorphism() + _prime = prime._ideal + _place = _prime.place() + + K, _from_K, _to_K = _place._residue_field(name=name) + + from_K = lambda e: from_F(_from_K(e)) + to_K = lambda f: _to_K(to_F(f)) + return K, from_K, to_K + + O = F.maximal_order() + Obasis = O.basis() + + M = prime.hnf() + R = M.base_ring() # univariate polynomial ring + n = M.nrows() # extension degree of the function field + + degs = [M[i,i].degree() for i in range(n)] + deg = sum(degs) # degree of the place + + # Step 1: construct a vector space representing the residue field + k = F.constant_base_field() + #V = k**deg + + def to_V(e): + """ + An example to show the idea: Suppose that + + [x 0 0] + M = [0 1 0] and v = (x^10, x^7 + x^3, x^7 + x^4 + x^3 + 1) + [1 0 1] + + Then to_V(e) = [1] + """ + v = O._coordinate_vector(e) + vec = [] + for i in reversed(range(n)): + q,r = v[i].quo_rem(M[i,i]) + v -= q * M[i] + for j in range(degs[i]): + vec.append(r[j]) + return vector(vec) + + def fr_V(vec): # to_O + vec = vec.list() + pos = 0 + e = F(0) + for i in reversed(range(n)): + if degs[i] == 0: continue + else: + end = pos + degs[i] + e += R(vec[pos:end]) * Obasis[i] + pos = end + return e + + # Step 2: find a generator of the residue field + def candidates(): + # Trial 1: this suffices for places obtained from Kummers' theorem + + # Note that a = O._kummer_gen is a simple generator of O/prime over + # o/p. If b is a simple generator of o/p over the constant base field + # k, then the set a + k * b contains a simple generator of O/prime + # over k (as there are finite number of intermediate fields). + a = O._kummer_gen + if a is not None: + K,fr_K,_ = self.place_below().residue_field() + b = fr_K(K.gen()) + for c in reversed(k.list()): + yield a + c * b + + # Trial 2: basis elements of the maximal order + for gen in reversed(Obasis): + yield gen + + import itertools + + # Trial 3: exhaustive search in O using only polynomials + # with coefficients 0 or 1 + for d in range(deg): + G = itertools.product(itertools.product([0,1],repeat=d+1), repeat=n) + for g in G: + gen = sum([R(c1)*c2 for c1,c2 in zip(g, Obasis)]) + yield gen + + # Trial 4: exhaustive search in O using all polynomials + for d in range(deg): + G = itertools.product(R.polynomials(max_degree=d), repeat=n) + for g in G: + # discard duplicate cases + if max(c.degree() for c in g) != d: continue + for j in range(n): + if g[j] != 0: break + if g[j].leading_coefficient() != 1: continue + + gen = sum([c1*c2 for c1,c2 in zip(g, Obasis)]) + yield gen + + for gen in candidates(): + g = F.one() + m = [] + for i in range(deg): + m.append(to_V(g)) + g *= gen + mat = matrix(m) + if mat.rank() == deg: + break + + # Step 3: compute the minimal polynomial of g + min_poly = R((-mat.solve_left(to_V(g))).list() + [1]) + + if deg > 1: + # Step 4: construct the finite field + K = k.extension(deg, name=name) + alpha = min_poly.roots(K)[0][0] + W, from_W, to_W = K.vector_space(k, basis=[alpha**i for i in range(deg)], map=True) + + # Step 5: compute the matrix of change of basis + C = mat.inverse() + + # Step 6: construct an isomorphism + def from_K(e): + return fr_V(to_W(e) * mat) + else: # deg == 1 + # Step 4: construct the finite field + K = k + + # Step 5: compute the matrix of change of basis + C = mat.inverse() + + # Step 6: construct an isomorphism + def from_K(e): + return fr_V(vector([e]) * mat) + + p = prime.prime_below().gen().numerator() + beta = prime._beta + alpha = ~p * sum(c1*c2 for c1,c2 in zip(beta, O.basis())) + alpha_powered_by_ramification_index = alpha ** prime._ramification_index + + def to_K(f): + if not f in O: + den = O.coordinate_vector(f).denominator() + num = den * f + + # s powered by the valuation of den at the prime + alpha_power = alpha_powered_by_ramification_index ** den.valuation(p) + rn = num * alpha_power # in O + rd = den * alpha_power # in O but not in prime + + # Note that rn is not in O if and only if f is + # not in the valuation ring. Hence f is in the + # valuation ring if and only if this procedure + # does not fall into an infinite loop. + return to_K(rn) / to_K(rd) + + e = (to_V(f)*C) + if deg > 1: + return from_W(e) + else: # len(e) == 1 + return K(e[0]) + + return K, from_K, to_K + + def valuation_ring(self): + """ + Return the valuation ring at the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: p.valuation_ring() + Valuation ring at Place (x, x*y) + """ + from .valuation_ring import FunctionFieldValuationRing_global + + return FunctionFieldValuationRing_global(self.function_field(), self) + +class PlaceSet(UniqueRepresentation, Parent): + """ + Sets of Places of function fields. + + INPUT: + + - ``field`` -- function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: L.place_set() + Set of places of Function field in y defined by y^3 + x^3*y + x + """ + Element = FunctionFieldPlace + + def __init__(self, field): + """ + Initialize the set of places of the function ``field``. + + TESTS:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: places = L.place_set() + sage: TestSuite(places).run() + """ + self.Element = field._place_class + Parent.__init__(self, category = Sets().Infinite()) + + self._field = field + + def _repr_(self): + """ + Return the string representation of the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: L.place_set() + Set of places of Function field in y defined by y^3 + x^3*y + x + """ + return "Set of places of {}".format(self._field) + + def _element_constructor_(self, ideal): + """ + Create a place from the prime ``ideal``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: places = L.place_set() + sage: O = L.maximal_order() + sage: places(O.ideal(x,y)) + Place (x, y) + """ + if not ideal.is_prime(): + raise TypeError("not a prime ideal") + + return self.element_class(self, ideal) + + def _an_element_(self): + """ + Return a place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: places = L.place_set() + sage: places.an_element() # random + Ideal (x) of Maximal order of Rational function field in x + over Finite Field of size 2 + """ + d = 1 + while True: + try: + p = self._field.places(d).pop() + except IndexError: + d = d + 1 + else: + break + return p + diff --git a/src/sage/rings/function_field/valuation_ring.py b/src/sage/rings/function_field/valuation_ring.py new file mode 100644 index 00000000000..1e46848b3af --- /dev/null +++ b/src/sage/rings/function_field/valuation_ring.py @@ -0,0 +1,216 @@ +r""" +Valuation rings of function fields + +A valuation ring of a function field is associated with a place of the +function field. The valuation ring consists of all elements of the function +field that have nonnegative valuation at the place. + +EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: p + Place (x, x*y) + sage: R = p.valuation_ring() + sage: R + Valuation ring at Place (x, x*y) + sage: R.place() == p + True + +Thus any nonzero element or its inverse of the function field lies in the +valuation ring, as shown in the following example:: + + sage: f = y/(1+y) + sage: f in R + True + sage: f not in R + False + sage: f.valuation(p) + 0 + +The residue field at the place is defined as the quotient ring of the valuaion +ring modulo its unique maximal ideal. In a global function field, the +:meth:`residue_field()` method returns a finite field isomorphic to the residue +field:: + + sage: k,phi,psi = R.residue_field() + sage: k + Finite Field of size 2 + sage: phi + Ring morphism: + From: Finite Field of size 2 + To: Valuation ring at Place (x, x*y) + sage: psi + Ring morphism: + From: Valuation ring at Place (x, x*y) + To: Finite Field of size 2 + sage: psi(f) in k + True + +AUTHORS: + +- Kwankyu Lee (2017-04-30): initial version + +""" +#***************************************************************************** +# Copyright (C) 2016 Kwankyu Lee +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# http://www.gnu.org/licenses/ +#***************************************************************************** +from __future__ import absolute_import + +from sage.misc.cachefunc import cached_method + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent + +from sage.categories.homset import Hom +from sage.categories.rings import Rings + +class FunctionFieldValuationRing(UniqueRepresentation, Parent): + """ + Base class for valuation rings of function fields. + + INPUT: + + - ``field`` -- function field + + - ``place`` -- place of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: p.valuation_ring() + Valuation ring at Place (x, x*y) + """ + def __init__(self, field, place, category=None): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: R = p.valuation_ring() + sage: TestSuite(R).run() + """ + Parent.__init__(self, category=Rings().or_subcategory(category).Infinite(), facade=field) + + self._field = field + self._place = place + + def _element_constructor_(self, x): + """ + Construct an element of the function field belonging to the + valuation ring. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: R = p.valuation_ring() + sage: y in R + False + sage: 1/y in R + True + sage: x + y in R + False + sage: 1/(x + y) in R + True + """ + x = self._field(x) + if x.valuation(self._place) >= 0: + return x + else: + raise TypeError + + def _repr_(self): + """ + Return the string representation of the valuation ring. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: p.valuation_ring() + Valuation ring at Place (x, x*y) + """ + return 'Valuation ring at {}'.format(self._place) + + def place(self): + """ + Return the place associated with the valuation ring. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: R = p.valuation_ring() + sage: p == R.place() + True + """ + return self._place + +class FunctionFieldValuationRing_global(FunctionFieldValuationRing): + """ + Valuation rings of global function fields. + """ + @cached_method + def residue_field(self, name=None): + """ + Return the residue field of the valuation ring together with + the maps from and to it. + + INPUT: + + - ``name`` -- string; name of the generator of the residue field + + OUTPUT: + + - a finite field isomorphic to the residue field + + - a ring homomorphism from the valuation ring to the finite field + + - a ring homomorphism from the finite field to the valuation ring + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: p = L.places_finite()[0] + sage: R = p.valuation_ring() + sage: k, fr_k, to_k = R.residue_field() + sage: k + Finite Field of size 2 + sage: fr_k + Ring morphism: + From: Finite Field of size 2 + To: Valuation ring at Place (x, x*y) + sage: to_k + Ring morphism: + From: Valuation ring at Place (x, x*y) + To: Finite Field of size 2 + sage: to_k(1/y) + 0 + sage: to_k(y/(1+y)) + 1 + """ + from .maps import FunctionFieldRingMorphism as morphism + + k, from_k, to_k = self._place._residue_field(name=name) + mor_from_k = morphism(Hom(k,self), from_k) + mor_to_k = morphism(Hom(self,k), to_k) + return k, mor_from_k, mor_to_k + + +