diff --git a/src/doc/en/reference/function_fields/index.rst b/src/doc/en/reference/function_fields/index.rst index 5df664fc6ce..29498b16e62 100644 --- a/src/doc/en/reference/function_fields/index.rst +++ b/src/doc/en/reference/function_fields/index.rst @@ -16,6 +16,7 @@ A reference for the basic theory of algebraic function fields is [Stich2009]_. sage/rings/function_field/order sage/rings/function_field/ideal sage/rings/function_field/place + sage/rings/function_field/divisor sage/rings/function_field/valuation_ring sage/rings/function_field/maps sage/rings/function_field/constructor diff --git a/src/sage/rings/function_field/divisor.py b/src/sage/rings/function_field/divisor.py new file mode 100644 index 00000000000..5eb75d0b62f --- /dev/null +++ b/src/sage/rings/function_field/divisor.py @@ -0,0 +1,859 @@ +""" +Divisors of function fields + +Sage allows extensive computations with divisors on global function fields. + +EXAMPLES: + +The divisor of an element of the function field is the formal sum of poles and zeros +of the element with multiplicities:: + + sage: K. = FunctionField(GF(2)); R. = K[] + sage: L. = K.extension(t^3 + x^3*t + x) + sage: f = x/(y+1) + sage: f.divisor() + - Place (1/x, 1/x^3*y^2 + 1/x) + + Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) + + 3*Place (x, y) + - Place (x^3 + x + 1, y + 1) + +The Riemann-Roch space of a divisor can be computed. We can get a basis of +the space as a vector space over the constant field:: + + sage: p = L.places_finite()[0] + sage: q = L.places_infinite()[0] + sage: (3*p + 2*q).basis_function_space() + [1/x*y^2 + x^2, 1, 1/x] + +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 + +import random + +from sage.misc.cachefunc import cached_method + +from sage.arith.all import lcm + +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent +from sage.structure.element import ModuleElement +from sage.structure.richcmp import richcmp + +from sage.categories.homset import Hom +from sage.categories.morphism import SetMorphism +from sage.categories.commutative_additive_groups import CommutativeAdditiveGroups + +from sage.matrix.constructor import matrix + +from sage.modules.free_module_element import vector + +from sage.rings.integer_ring import IntegerRing +from sage.rings.integer import Integer + +from .place import PlaceSet + +def divisor(field, data): + """ + Construct a divisor from the data. + + INPUT: + + - ``field`` -- function field + + - ``data`` -- dictionary of place and multiplicity pairs + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: from sage.rings.function_field.divisor import divisor + sage: p, q, r = F.places() + sage: divisor(F, {p: 1, q: 2, r: 3}) + Place (1/x, 1/x^2*y + 1) + + 2*Place (x, (1/(x^3 + x^2 + x))*y^2) + + 3*Place (x + 1, y + 1) + """ + divisor_group = field.divisor_group() + return divisor_group.element_class(divisor_group, data) + +def prime_divisor(field, place, m=1): + """ + Construct a prime divisor from the place. + + INPUT: + + - ``field`` -- function field + + - ``place`` -- place of the function field + + - ``m`` -- (default: 1) a positive integer; multiplicity at the place + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: p = F.places()[0] + sage: from sage.rings.function_field.divisor import prime_divisor + sage: d = prime_divisor(F, p) + sage: 3 * d == prime_divisor(F, p, 3) + True + """ + divisor_group = field.divisor_group() + return divisor_group.element_class(divisor_group, {place: Integer(m)}) + +class FunctionFieldDivisor(ModuleElement): + """ + Divisors of function fields. + + INPUT: + + - ``parent`` -- divisor group + + - ``data`` -- dict of place and multiplicity pairs + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: f = x/(y+1) + sage: f.divisor() + Place (1/x, 1/x^4*y^2 + 1/x^2*y + 1) + + Place (1/x, 1/x^2*y + 1) + + 3*Place (x, (1/(x^3 + x^2 + x))*y^2) + - 6*Place (x + 1, y + 1) + """ + def __init__(self, parent, data): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: G = L.divisor_group() + sage: TestSuite(G).run() + """ + ModuleElement.__init__(self, parent) + self._data = data + + def _repr_(self, split=True): + """ + Return a string representation of the divisor. + + INPUT: + + - ``split`` -- boolean; if ``True``, split at the end of each place + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) + sage: f = x/(y+1) + sage: d = f.divisor() + sage: d._repr_(split=False) + 'Place (1/x, 1/x^4*y^2 + 1/x^2*y + 1) + Place (1/x, 1/x^2*y + 1) + + 3*Place (x, (1/(x^3 + x^2 + x))*y^2) - 6*Place (x + 1, y + 1)' + """ + mul = '*' + plus = ' + ' + minus = ' - ' + + if split: + cr = '\n' + else: + cr = '' + + places = sorted(self._data) + + if len(places) == 0: + return '0' + + p = places.pop(0) + m = self._data[p] + if m == 1: + r = repr(p) + elif m == -1: + r = '- ' + repr(p) # seems more readable than `-` + else: # nonzero + r = repr(m) + mul + repr(p) + for p in places: + m = self._data[p] + if m == 1: + r += cr + plus + repr(p) + elif m == -1: + r += cr + minus + repr(p) + elif m > 0: + r += cr + plus + repr(m) + mul + repr(p) + elif m < 0: + r += cr + minus + repr(-m) + mul + repr(p) + return r + + def _richcmp_(self, other, op): + """ + Compare the divisor and the other divisor with respect to the operator. + + Divisors are compared lexicographically, viewed as lists of pairs of + place and multiplicity. + + INPUT: + + - ``other`` -- divisor + + - ``op`` -- comparison operator + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: pls1 = L.places() + sage: D1 = pls1[0] + pls1[1] + sage: D2 = pls1[1] + 2*pls1[2] + sage: (D1 < D2) == (not D2 < D1) + True + sage: D1 + D2 == D2 + D1 + True + """ + s = sorted(self._data) + o = sorted(other._data) + while s and o: + skey = s[-1] + okey = o[-1] + if skey == okey: + svalue = self._data[skey] + ovalue = other._data[okey] + if svalue == ovalue: + s.pop() + o.pop() + continue + return richcmp(svalue, ovalue, op) + return richcmp(skey, okey, op) + return richcmp(len(s), len(o), op) + + def _neg_(self): + """ + Return the additive inverse of the divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: f = x/(y+1) + sage: D = f.divisor() + sage: D + - Place (1/x, 1/x^3*y^2 + 1/x) + + Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) + + 3*Place (x, y) + - Place (x^3 + x + 1, y + 1) + sage: -D + Place (1/x, 1/x^3*y^2 + 1/x) + - Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) + - 3*Place (x, y) + + Place (x^3 + x + 1, y + 1) + """ + divisor_group = self.parent() + data = {} + for place in self._data: + data[place] = -self._data[place] + return divisor_group.element_class(divisor_group, data) + + def _add_(self, other): + """ + Add the divisor to the other divisor. + + INPUT: + + - ``other`` -- divisor + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: f = x/(y+1) + sage: D = f.divisor() + sage: D + 2*D == 3*D + True + sage: 3*D - D == 2*D + True + """ + divisor_group = self.parent() + places = set(self.support()).union( set(other.support())) + data = {} + for place in places: + m = self.multiplicity(place) + other.multiplicity(place) + if m != 0: + data[place] = m + return divisor_group.element_class(divisor_group, data) + + def _rmul_(self, i): + """ + Multiply integer `i` to the divisor. + + INPUT: + + - ``i`` -- integer + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: f = x/(y+1) + sage: D = f.divisor() + sage: (-3)*(2*D) == -6*D + True + """ + divisor_group = self.parent() + data = {} + for place in self._data: + m = i * self._data[place] + if m != 0: + data[place] = m + return divisor_group.element_class(divisor_group, data) + + def dict(self): + """ + Return the dictionary representing the divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: f = x/(y+1) + sage: D = f.divisor() + sage: D.dict() + {Place (1/x, 1/x^3*y^2 + 1/x): -1, + Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1): 1, + Place (x, y): 3, + Place (x^3 + x + 1, y + 1): -1} + """ + return self._data + + def list(self): + """ + Return the list of place and multiplicity pairs of the divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: f = x/(y+1) + sage: D = f.divisor() + sage: D.list() + [(Place (1/x, 1/x^3*y^2 + 1/x), -1), + (Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1), 1), + (Place (x, y), 3), + (Place (x^3 + x + 1, y + 1), -1)] + """ + return sorted(self._data.items()) + + def support(self): + """ + Return the support of the divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: f = x/(y+1) + sage: D = f.divisor() + sage: D.support() + [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), + Place (x^3 + x + 1, y + 1)] + """ + return sorted(self._data) + + def multiplicity(self, place): + """ + Return the multiplicity of the divisor at the place. + + INPUT: + + - ``place`` -- place of a function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: p1,p2 = L.places()[:2] + sage: D = 2*p1 - 3*p2 + sage: D.multiplicity(p1) + 2 + sage: D.multiplicity(p2) + -3 + """ + if not place in self._data: + return 0 + return self._data[place] + + def degree(self): + """ + Return the degree of the divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _.=K[] + sage: L. = K.extension(Y^3+x+x^3*Y) + sage: p1,p2 = L.places()[:2] + sage: D = 2*p1 - 3*p2 + sage: D.degree() + -1 + """ + return sum([p.degree() * m for p, m in self.list()]) + + def dimension(self): + """ + Return the dimension of the Riemann-Roch space of the divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) + sage: O = F.maximal_order() + sage: I = O.ideal(x-2) + sage: P1 = I.divisor().support()[0] + sage: Pinf = F.places_infinite()[0] + sage: D = 3*Pinf+2*P1 + sage: D.dimension() + 5 + """ + return len(self.basis_function_space()) + + def basis_function_space(self): + """ + Return a basis of the Riemann-Roch space of the divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = K[] + sage: F. = K.extension(t^2 - x^3 - 1) + sage: O = F.maximal_order() + sage: I = O.ideal(x-2) + sage: D = I.divisor() + sage: D.basis_function_space() + [x/(x + 3), 1/(x + 3)] + """ + basis,_ = self._function_space() + return basis + + @cached_method + def function_space(self): + """ + Return the vector space of the Riemann-Roch space of the divisor. + + OUTPUT: + + - a vector space, an isomorphism from the vector space + to the Riemann-Roch space, and its inverse. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) + sage: O = F.maximal_order() + sage: I = O.ideal(x-2) + sage: D = I.divisor() + sage: V, from_V, to_V = D.function_space() + sage: all(to_V(from_V(e)) == e for e in V) + True + """ + F = self.parent()._field + k = F.constant_base_field() + + basis, coordinates = self._function_space() + + n = len(basis) + V = k ** n + + def from_V(v): + return sum(v[i] * basis[i] for i in range(n)) + + def to_V(f): + return vector(coordinates(f)) + + from sage.rings.function_field.maps import ( + FunctionFieldLinearMap, FunctionFieldLinearMapSection) + + mor_from_V = FunctionFieldLinearMap(Hom(V,F), from_V) + mor_to_V = FunctionFieldLinearMapSection(Hom(F,V), to_V) + + return V, mor_from_V, mor_to_V + + @cached_method + def _function_space(self): + """ + Return an (echelon) basis and coordinates function for the Riemann-Roch + space of the divisor. + + The return values are cached so that :meth:`basis_function_space` and + :meth:`function_space` methods give consistent outputs. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) + sage: O = F.maximal_order() + sage: I = O.ideal(x-2) + sage: D = I.divisor() + sage: basis, coordinates = D._function_space() + sage: basis + [x/(x + 3), 1/(x + 3)] + sage: coordinates(basis[0]) + [1, 0] + sage: coordinates(basis[1]) + [0, 1] + sage: coordinates(basis[0] + basis[1]) + [1, 1] + sage: coordinates((x + 4)/(x + 3)) + [1, 4] + """ + basis, coordinates_func = self._echelon_basis(self._basis()) + + return basis, coordinates_func + + def _basis(self): + """ + Return a basis of the Riemann-Roch space of the divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) + sage: O = F.maximal_order() + sage: I = O.ideal(x-2) + sage: D = I.divisor() + sage: D._basis() + [1/(x + 3), x/(x + 3)] + + This implements Hess' algorithm 6.1 in [Hes2002]_ + """ + F = self.parent()._field + n = F.degree() + O = F.maximal_order() + Oinf = F.maximal_order_infinite() + + # Step 1: The ideal I is the inverse of the product of prime ideals attached + # to the finite places in the divisor while the ideal J corresponds + # to the infinite places in the divisor. The later steps are basically + # to compute the intersection of the ideals I and J. + I = O.ideal(1) + J = Oinf.ideal(1) + for p in self._data: + m = self._data[p] + if p.is_infinite_place(): + J *= p.prime_ideal() ** (-m) + else: + I *= p.prime_ideal() ** (-m) + + # Step 2: construct matrix M of rational functions in x such that + # M * B == C where B = [b1,b1,...,bn], C =[v1,v2,...,vn] + V,fr,to = F.vector_space() + B = matrix([to(b) for b in J.gens_over_base()]) + C = matrix([to(v) for v in I.gens_over_base()]) + M = C * B.inverse() + + # Step 2.5: get the denonimator d of M and set mat = d * M + den = lcm([e.denominator() for e in M.list()]) + R = den.parent() # polynomial ring + one = R.one() + mat = matrix(R, n, [e.numerator() for e in (den*M).list()]) + gens = list(I.gens_over_base()) + + # Step 3: transform mat to a weak Popov form, together with gens + + # initialise pivot_row and conflicts list + pivot_row = [[] for i in range(n)] + conflicts = [] + for i in range(n): + bestp = -1 + best = -1 + for c in range(n): + d = mat[i,c].degree() + if d >= best: + bestp = c + best = d + + if best >= 0: + pivot_row[bestp].append((i,best)) + if len(pivot_row[bestp]) > 1: + conflicts.append(bestp) + + # while there is a conflict, do a simple transformation + while conflicts: + c = conflicts.pop() + row = pivot_row[c] + i,ideg = row.pop() + j,jdeg = row.pop() + + if jdeg > ideg: + i,j = j,i + ideg,jdeg = jdeg,ideg + + coeff = - mat[i,c].lc() / mat[j,c].lc() + s = coeff * one.shift(ideg - jdeg) + + mat.add_multiple_of_row(i, j, s) + gens[i] += s * gens[j] + + row.append((j,jdeg)) + + bestp = -1 + best = -1 + for c in range(n): + d = mat[i,c].degree() + if d >= best: + bestp = c + best = d + + if best >= 0: + pivot_row[bestp].append((i,best)) + if len(pivot_row[bestp]) > 1: + conflicts.append(bestp) + + # Step 4: build a Riemann-Roch basis from the data in mat and gens. + # Note that the values mat[i,j].degree() - den.degree() are known as + # invariants of M. + basis = [] + for j in range(n): + i,ideg = pivot_row[j][0] + for k in range( den.degree() - ideg + 1 ): + basis.append(one.shift(k) * gens[i]) + # Done! + return basis + + def _echelon_basis(self, basis): + """ + This is a helper method to compute an echelonized basis of the subspace + generated by ``basis`` over `k`. + + The entries of ``basis`` vectors are function field elements, viewed + as vectors of rational functions over `k`. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = PolynomialRing(K) + sage: F. = K.extension(t^2 - x^3 - 1) + sage: D = F.divisor_group().zero() + sage: echelon_basis, coordinates = D._echelon_basis([x/y, (x + 1)/y]) + sage: echelon_basis + [(x/(x^3 + 1))*y, (1/(x^3 + 1))*y] + sage: f1, f2 = echelon_basis + sage: coordinates(f1) + [1, 0] + sage: coordinates(f2) + [0, 1] + sage: coordinates(x/y) + [1, 0] + sage: coordinates((x + 1)/y) + [1, 1] + sage: 1 * f1 + 1 * f2 == (x + 1)/y + True + """ + F = self.parent()._field + k = F.constant_base_field() + V, fr_V, to_V = F.vector_space() + n = V.degree() + m = len(basis) + + vbasis = [to_V(f) for f in basis] + + # compute the pivot position for nonzero vector v over rational functions + def pivot(v): + for i in range(n): + e = v[i] + if e != 0: + return (i,e.numerator().degree() - e.denominator().degree()) + + def greater(v,w): # v and w are not equal + return v[0] < w[0] or v[0] == w[0] and v[1] > w[1] + + # collate rows by their pivot position + pivot_rows = {} + for i in range(m): + p = pivot(vbasis[i]) + if p in pivot_rows: + pivot_rows[p].append(i) + else: + pivot_rows[p] = [i] + + # compute "echelon" basis in which pivot positions decrease strictly + nbasis = [] + npivots = [] + while pivot_rows: + pivots = list(pivot_rows) + + head = pivots[0] + for p in pivots[1:]: + if not greater(head,p): + head = p + + rows = pivot_rows[head] + if len(rows) > 1: + r = rows[0] + vr = vbasis[r][head[0]] + cr = vr.numerator().lc() / vr.denominator().lc() + for i in rows[1:]: + vi = vbasis[i][head[0]] + ci = vi.numerator().lc() / vi.denominator().lc() + vbasis[i] -= ci/cr * vbasis[r] + p = pivot(vbasis[i]) + if p in pivot_rows: + pivot_rows[p].append(i) + else: + pivot_rows[p] = [i] + nbasis.append(vbasis[rows[0]]) + npivots.append(head) + del pivot_rows[head] + + def coordinates(f): + v = to_V(f) + coords = [k(0) for i in range(m)] + while v != 0: + p = pivot(v) + ind = npivots.index(p) # an exception implies x is not in the domain + w = nbasis[ind] + cv = v[p[0]].numerator().lc() / v[p[0]].denominator().lc() + cw = w[p[0]].numerator().lc() / w[p[0]].denominator().lc() + c = cv/cw + v -= c * w + coords[ind] = c + return coords + + newbasis = [fr_V(f) for f in nbasis] + + return newbasis, coordinates + +class DivisorGroup(UniqueRepresentation, Parent): + """ + Groups of divisors of function fields. + + INPUT: + + - ``field`` -- function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = PolynomialRing(K) + sage: F. = K.extension(t^2 - x^3 - 1) + sage: F.divisor_group() + Divisor group of Function field in y defined by y^2 + 4*x^3 + 4 + """ + Element = FunctionFieldDivisor + + def __init__(self, field): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(5)); _. = PolynomialRing(K) + sage: F. = K.extension(t^2 - x^3 - 1) + sage: G = F.divisor_group() + sage: TestSuite(G).run() + """ + Parent.__init__(self, base=IntegerRing(), category=CommutativeAdditiveGroups()) + + self._field = field # function field + + def _repr_(self): + """ + Return the string representation of the group of divisors. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = PolynomialRing(K) + sage: F. = K.extension(t^2 - x^3 - 1) + sage: F.divisor_group() + Divisor group of Function field in y defined by y^2 + 4*x^3 + 4 + """ + return "Divisor group of %s"%(self._field,) + + def _element_constructor_(self, x): + """ + Construct a divisor from ``x``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = PolynomialRing(K) + sage: F. = K.extension(t^2 - x^3 - 1) + sage: G = F.divisor_group() + sage: G(0) + 0 + """ + if x == 0: + return self.element_class(self, {}) + raise NotImplementedError + + def _coerce_map_from_(self, S): + """ + Define coercions. + + EXAMPLES: + + A place is converted to a prime divisor:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2 - x^3 - 1) + sage: O = F.maximal_order() + sage: I = O.ideal(x + 1,y) + sage: P = I.place() + sage: y.divisor() + P + -3*Place (1/x, 1/x^2*y) + + 2*Place (x + 1, y) + + Place (x^2 + 4*x + 1, y) + """ + if isinstance(S, PlaceSet): + func = lambda place: prime_divisor(self._field, place) + return SetMorphism(Hom(S,self), func) + + def function_field(self): + """ + Return the function field to which the divisor group is attached. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = PolynomialRing(K) + sage: F. = K.extension(t^2 - x^3 - 1) + sage: G = F.divisor_group() + sage: G.function_field() + Function field in y defined by y^2 + 4*x^3 + 4 + """ + return self._field + + def _an_element_(self): + """ + Return a divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: G = L.divisor_group() + sage: G.an_element() # random + Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) + + 2*Place (x^2 + x + 1, y + x + 1) + + 2*Place (x^3 + x + 1, y + x^2) + + Place (x^3 + x^2 + 1, y + x^2 + 1) + """ + N = 10 + d = 1 + places = [] + while len(places) <= N: # collect at least N places + places += self._field.places(d) + d += 1 + e = self.element_class(self, {}) + for i in range(random.randint(0,N)): + e += random.choice(places) + return e diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index 4d15125222b..c8b5bdd5433 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -18,6 +18,15 @@ Arithmetic with rational functions:: sage: h.valuation(t^2 - 3) -3 +The divisor of an element of a global function field:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: y.divisor() + - Place (1/x, 1/x*y) + - Place (x, x*y) + + 2*Place (x + 1, x*y) + AUTHORS: - William Stein: initial version @@ -925,6 +934,67 @@ cdef class FunctionFieldElement_rational(FunctionFieldElement): assert self._x.denominator() == 1 return self.parent()(self._x.numerator().inverse_mod(f.numerator())) + @cached_method + def divisor(self): + """ + Return the divisor of the element. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) + sage: f = 1/(x^3 + x^2 + x) + sage: f.divisor() + 3*Place (1/x) + - Place (x) + - Place (x^2 + x + 1) + """ + if self.is_zero(): + raise ValueError("divisor not defined for zero") + + F = self.parent() + I = F.maximal_order().ideal(self) + J = F.maximal_order_infinite().ideal(self) + return I.divisor() + J.divisor() + + def divisor_of_zeros(self): + """ + Return the divisor of zeros for the element. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) + sage: f = 1/(x^3 + x^2 + x) + sage: f.divisor_of_zeros() + 3*Place (1/x) + """ + if self.is_zero(): + raise ValueError("divisor of zeros not defined for zero") + + F = self.parent() + I = F.maximal_order().ideal(self) + J = F.maximal_order_infinite().ideal(self) + return I.divisor_of_zeros() + J.divisor_of_zeros() + + def divisor_of_poles(self): + """ + Return the divisor of poles for the element. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) + sage: f = 1/(x^3 + x^2 + x) + sage: f.divisor_of_poles() + Place (x) + + Place (x^2 + x + 1) + """ + if self.is_zero(): + raise ValueError("divisor of poles not defined for zero") + + F = self.parent() + I = F.maximal_order().ideal(self) + J = F.maximal_order_infinite().ideal(self) + return I.divisor_of_poles() + J.divisor_of_poles() + cdef class FunctionFieldElement_global(FunctionFieldElement_polymod): """ Elements of global function fields @@ -949,3 +1019,63 @@ cdef class FunctionFieldElement_global(FunctionFieldElement_polymod): prime = place.prime_ideal() ideal = prime.ring().ideal(self) return prime.valuation(ideal) + + @cached_method + def divisor(self): + """ + Return the divisor for the element. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: y.divisor() + - Place (1/x, 1/x*y) + - Place (x, x*y) + + 2*Place (x + 1, x*y) + """ + if self.is_zero(): + raise ValueError("not defined for zero") + + F = self.parent() + I = F.maximal_order().ideal(self) + J = F.maximal_order_infinite().ideal(self) + return I.divisor() + J.divisor() + + def divisor_of_zeros(self): + """ + Return divisor of zeros for the element. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: (x/y).divisor_of_zeros() + 3*Place (x, x*y) + """ + if self.is_zero(): + raise ValueError("divisor of zeros not defined for zero") + + F = self.parent() + I = F.maximal_order().ideal(self) + J = F.maximal_order_infinite().ideal(self) + return I.divisor_of_zeros() + J.divisor_of_zeros() + + def divisor_of_poles(self): + """ + Return the divisor of poles for the element. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: (x/y).divisor_of_poles() + Place (1/x, 1/x*y) + 2*Place (x + 1, x*y) + """ + if self.is_zero(): + raise ValueError("divisor of poles not defined for zero") + + F = self.parent() + I = F.maximal_order().ideal(self) + J = F.maximal_order_infinite().ideal(self) + return I.divisor_of_poles() + J.divisor_of_poles() diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 72a42e3b9a9..18aa445f909 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -2599,6 +2599,20 @@ def place_set(self): from .place import PlaceSet return PlaceSet(self) + def divisor_group(self): + """ + Return the group of divisors attached to the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = K[] + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) + sage: L.divisor_group() + Divisor group of Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) + """ + from .divisor import DivisorGroup + return DivisorGroup(self) + def residue_field(self, place, name=None): """ Return the residue field associated with the place along with the maps @@ -2756,29 +2770,104 @@ def _places_infinite(self, degree): if place.degree() == degree: yield place - def valuation_ring(self, place): + def different(self): + """ + Return the different of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: F.different() + 2*Place (x, (1/(x^3 + x^2 + x))*y^2) + + 2*Place (x^2 + x + 1, (1/(x^3 + x^2 + x))*y^2) + """ + O = self.maximal_order() + Oinf = self.maximal_order_infinite() + return O.different().divisor() + Oinf.different().divisor() + + def constant_field(self): """ - Return the valuation ring associated with the place. + Return the algebraic closure of the base constant field in the function + field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)); _. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.constant_field() + Finite Field of size 3 + """ + return self.exact_constant_field()[0] + + def exact_constant_field(self, name='t'): + """ + Return the exact constant field and its embedding into the function field. INPUT: - - ``place`` -- place of the function field + - ``name`` -- name (default: `t`) of the generator of the exact constant 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(3)); _. = K[] + sage: f = Y^2 - x*Y + x^2 + 1 # irreducible but not absolutely irreducible + sage: L. = K.extension(f) + sage: L.genus() + 0 + sage: L.exact_constant_field() + (Finite Field in t of size 3^2, Ring morphism: + From: Finite Field in t of size 3^2 + To: Function field in y defined by y^2 + 2*x*y + x^2 + 1 + Defn: t |--> y + x) + sage: (y+x).divisor() + 0 + """ + # A basis of the full constant field is obtained from + # computing a Riemann-Roch basis of zero divisor. + basis = self.divisor_group().zero().basis_function_space() - 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) + dim = len(basis) + + for e in basis: + _min_poly = e.minimal_polynomial(name) + if _min_poly.degree() == dim: + break + k = self.constant_base_field() + R = k[name] + min_poly = R([k(c) for c in _min_poly.list()]) + + k_ext = k.extension(min_poly, name) + + if k_ext.is_prime_field(): + # The cover of the quotient ring k_ext is the integer ring + # whose generator is 1. This is different from the generator + # of k_ext. + embedding = k_ext.hom([self(1)], self) + else: + embedding = k_ext.hom([e], self) + + return k_ext, embedding + + def genus(self): """ - return place.valuation_ring() + Return the genus of the function field. + + EXAMPLES:: + + sage: F. = GF(16) + sage: K. = FunctionField(F); K + Rational function field in x over Finite Field in a of size 2^4 + sage: R. = PolynomialRing(K) + sage: L. = K.extension(t^4+t-x^5) + sage: L.genus() + 6 + + The genus is computed by the Hurwitz genus formula. + """ + k, _ = self.exact_constant_field() + different_degree = self.different().degree() # must be even + return different_degree // 2 - self.degree() / k.degree() + 1 class FunctionField_global_integral(FunctionField_global): """ @@ -3629,6 +3718,21 @@ def constant_base_field(self): constant_field = constant_base_field + def different(self): + """ + Return the different of the rational function field. + + For a rational function field, the different is simply the zero + divisor. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.different() + 0 + """ + return self.divisor_group().zero() + def genus(self): """ Return the genus of the function field, namely 0. @@ -3717,6 +3821,19 @@ def derivation(self): raise NotImplementedError("not implemented for non-perfect base fields") return FunctionFieldDerivation_rational(self, self.one()) + def divisor_group(self): + """ + Return the group of divisors of the rational function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.divisor_group() + Divisor group of Rational function field in t over Rational Field + """ + from .divisor import DivisorGroup + return DivisorGroup(self) + class RationalFunctionField_global(RationalFunctionField): """ Rational function field over finite fields. @@ -3817,16 +3934,3 @@ def residue_field(self, place, name=None): 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/ideal.py b/src/sage/rings/function_field/ideal.py index 8ae7a9bdd13..e9438f795fd 100644 --- a/src/sage/rings/function_field/ideal.py +++ b/src/sage/rings/function_field/ideal.py @@ -91,7 +91,6 @@ import itertools 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 @@ -107,13 +106,13 @@ from sage.rings.infinity import infinity from sage.rings.ideal import Ideal_generic -lazy_import('sage.matrix.constructor', 'matrix') -lazy_import('sage.rings.function_field.divisor', 'FunctionFieldDivisor') +from sage.matrix.constructor import matrix +from .divisor import divisor, prime_divisor class FunctionFieldIdeal(Element): """ - Fractional ideals of function fields. + Base class of fractional ideals of function fields. INPUT: @@ -208,6 +207,233 @@ def base_ring(self): """ return self.ring() + def place(self): + """ + Return the place associated with this 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) + + 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) + + 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)] + + 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 TypeError("not a prime ideal") + + place_set = self.ring().fraction_field().place_set() + return place_set.element_class(place_set, self) + + def factor(self): + """ + Return the factorization of this ideal. + + Subclass of this class should define :meth:`_factor` method that + returns a list of prime ideal and multiplicity pairs. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x^3*(x + 1)^2) + sage: I.factor() + (Ideal (x) of Maximal order of Rational function field in x + over Finite Field in z2 of size 2^2)^3 * + (Ideal (x + 1) of Maximal order of Rational function field in x + over Finite Field in z2 of size 2^2)^2 + + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: I.factor() + (Ideal (1/x) of Maximal infinite order of Rational function field in x + over Finite Field in z2 of size 2^2)^2 + + 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: I == I.factor().prod() + True + + sage: Oinf = F.maximal_order_infinite() + sage: f= 1/x + sage: I = Oinf.ideal(f) + sage: I.factor() + (Ideal (1/x,1/x^4*y^2 + 1/x^2*y + 1) of Maximal infinite order + of Function field in y defined by y^3 + x^6 + x^4 + x^2) * + (Ideal (1/x,1/x^2*y + 1) of Maximal infinite order + of Function field in y defined by y^3 + x^6 + x^4 + x^2) + """ + return Factorization(self._factor(), cr=True) + + def divisor(self): + """ + Return the divisor corresponding to the ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) + sage: I.divisor() + Place (x) + 2*Place (x + 1) - Place (x + z2) - Place (x + z2 + 1) + + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: I.divisor() + 2*Place (1/x) + + 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: I.divisor() + 2*Place (x, (1/(x^3 + x^2 + x))*y^2) + + 2*Place (x^2 + x + 1, (1/(x^3 + x^2 + x))*y^2) + + sage: Oinf = F.maximal_order_infinite() + sage: I = Oinf.ideal(y) + sage: I.divisor() + -2*Place (1/x, 1/x^4*y^2 + 1/x^2*y + 1) + - 2*Place (1/x, 1/x^2*y + 1) + + 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: I.divisor() + - Place (x, x*y) + + 2*Place (x + 1, x*y) + + sage: Oinf = L.maximal_order_infinite() + sage: I = Oinf.ideal(y) + sage: I.divisor() + - Place (1/x, 1/x*y) + """ + if self.is_zero(): + raise ValueError("not defined for zero ideal") + + F = self.ring().fraction_field() + data = {prime.place(): multiplicity for prime, multiplicity in self._factor()} + return divisor(F, data) + + def divisor_of_zeros(self): + """ + Return the divisor of zeros corresponding to the ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) + sage: I.divisor_of_zeros() + Place (x) + 2*Place (x + 1) + + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: I.divisor_of_zeros() + 2*Place (1/x) + + 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: I.divisor_of_zeros() + 2*Place (x + 1, x*y) + """ + if self.is_zero(): + raise ValueError("not defined for zero ideal") + + F = self.ring().fraction_field() + data = {prime.place(): multiplicity for prime, multiplicity in self._factor() if multiplicity > 0} + return divisor(F, data) + + def divisor_of_poles(self): + """ + Return the divisor of poles corresponding to the ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) + sage: O = K.maximal_order() + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) + sage: I.divisor_of_poles() + Place (x + z2) + Place (x + z2 + 1) + + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) + sage: I.divisor_of_poles() + 0 + + 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: I.divisor_of_poles() + Place (x, x*y) + """ + if self.is_zero(): + raise ValueError("not defined for zero ideal") + + F = self.ring().fraction_field() + data = {prime.place(): - multiplicity for prime, multiplicity in self._factor() if multiplicity < 0} + return divisor(F, data) class FunctionFieldIdeal_rational(FunctionFieldIdeal): """ @@ -526,24 +752,6 @@ def _valuation(self, ideal): """ return ideal.gen().valuation(self.gen()) - def factor(self): - """ - Return the factorization of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)) - sage: O = K.maximal_order() - sage: I = O.ideal(x^3*(x+1)^2) - sage: I.factor() - (Ideal (x) of Maximal order of Rational function field in x - over Finite Field in z2 of size 2^2)^3 * - (Ideal (x + 1) of Maximal order of Rational function field in x - over Finite Field in z2 of size 2^2)^2 - """ - factors = self._factor() - return Factorization(factors, cr=True) - def _factor(self): """ Return the list of prime and multiplicity pairs of the @@ -565,29 +773,6 @@ 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): """ A fractional ideal specified by a finitely generated module over @@ -1645,59 +1830,6 @@ def ideal_below(self): return K.ideal(l) - def factor(self): - """ - Return the factorization of this 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: I == I.factor().prod() - True - - 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: I == I.factor().prod() - True - """ - return Factorization(self._factor(), cr=True) - - def _factor(self): - """ - Return the factorization of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = 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: I == I.factor().prod() # indirect doctest - True - """ - O = self.ring() - F = O.fraction_field() - o = F.base_field().maximal_order() - - # First we collect primes below self - d = self._denominator - i = d * self - - factors = [] - primes = set([o.ideal(p) for p,_ in d.factor()] + [p for p,_ in i.ideal_below().factor()]) - for prime in primes: - qs = [q[0] for q in O.decomposition(prime)] - for q in qs: - exp = q.valuation(self) - if exp != 0: - factors.append((q,exp)) - return factors - def norm(self): """ Return the norm of this fractional ideal. @@ -1876,32 +2008,36 @@ def prime_below(self): """ return self._prime_below - def place(self): + def _factor(self): """ - Return the place corresponding to the prime ideal. + Return the factorization of this ideal. EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) + sage: K. = FunctionField(GF(2)); _. = 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)] + sage: I == I.factor().prod() # indirect doctest + True """ - if not self.is_prime(): - raise TypeError("not a prime ideal") + O = self.ring() + F = O.fraction_field() + o = F.base_field().maximal_order() - place_set = self.ring().fraction_field().place_set() - return place_set.element_class(place_set, self) + # First we collect primes below self + d = self._denominator + i = d * self + + factors = [] + primes = set([o.ideal(p) for p,_ in d.factor()] + [p for p,_ in i.ideal_below().factor()]) + for prime in primes: + qs = [q[0] for q in O.decomposition(prime)] + for q in qs: + exp = q.valuation(self) + if exp != 0: + factors.append((q,exp)) + return factors class FunctionFieldIdealInfinite(FunctionFieldIdeal): """ @@ -2119,46 +2255,6 @@ def gens_over_base(self): """ return (self._gen,) - def factor(self): - """ - Return the factorization of this ideal into prime ideals. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal((x+1)/(x^3+1)) - sage: I.factor() - (Ideal (1/x) of Maximal infinite order of Rational function field - in x over Finite Field of size 2)^2 - """ - g = ~(self.ring().fraction_field().gen()) - m = self._gen.denominator().degree() - self._gen.numerator().degree() - if m == 0: - factors = [] - else: - 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. @@ -2186,6 +2282,25 @@ def valuation(self, ideal): else: return f.denominator().degree() - f.numerator().degree() + def _factor(self): + """ + Return the factorization of this ideal into prime ideals. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) + sage: Oinf = K.maximal_order_infinite() + sage: I = Oinf.ideal((x+1)/(x^3+1)) + sage: I._factor() + [(Ideal (1/x) of Maximal infinite order of Rational function field in x + over Finite Field of size 2, 2)] + """ + g = ~(self.ring().fraction_field().gen()) + m = self._gen.denominator().degree() - self._gen.numerator().degree() + if m == 0: + return [] + else: + return [(self.ring().ideal(g), m)] class FunctionFieldIdealInfinite_module(FunctionFieldIdealInfinite, Ideal_generic): """ @@ -2790,33 +2905,27 @@ def prime_below(self): K = F.base_field() return K.maximal_order_infinite().prime_ideal() - def factor(self): + def valuation(self, ideal): """ - Return factorization of this ideal. + Return the valuation of ``ideal`` with respect to this prime ideal. - EXAMPLES:: + INPUT: - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) - sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) - sage: Oinf = F.maximal_order_infinite() - sage: f= 1/x - sage: I = Oinf.ideal(f) - sage: I.factor() - (Ideal (1/x,1/x^4*y^2 + 1/x^2*y + 1) of Maximal infinite order - of Function field in y defined by y^3 + x^6 + x^4 + x^2) * - (Ideal (1/x,1/x^2*y + 1) of Maximal infinite order - of Function field in y defined by y^3 + x^6 + x^4 + x^2) + - ``ideal`` -- fractional ideal - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2+Y+x+1/x) + EXAMPLES:: + + sage: K.=FunctionField(GF(2)); _. = K[] + sage: L.=K.extension(Y^2 + Y + x + 1/x) sage: Oinf = L.maximal_order_infinite() - sage: f= 1/x - sage: I = Oinf.ideal(f) - 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: I = Oinf.ideal(y) + sage: [f.valuation(I) for f,_ in I.factor()] + [-1] """ - return Factorization(self._factor(), cr=True) + if not self.is_prime(): + raise TypeError("not a prime ideal") + + return self._ideal.valuation(self.ring()._to_iF(ideal)) def _factor(self): """ @@ -2829,83 +2938,22 @@ def _factor(self): sage: Oinf = F.maximal_order_infinite() sage: f= 1/x sage: I = Oinf.ideal(f) - sage: I.factor() # indirect doctest - (Ideal (1/x,1/x^4*y^2 + 1/x^2*y + 1) of Maximal infinite order - of Function field in y defined by y^3 + x^6 + x^4 + x^2) * - (Ideal (1/x,1/x^2*y + 1) of Maximal infinite order - of Function field in y defined by y^3 + x^6 + x^4 + x^2) + sage: I._factor() + [(Ideal (1/x,1/x^4*y^2 + 1/x^2*y + 1) of Maximal infinite order of Function field in y + defined by y^3 + x^6 + x^4 + x^2, 1), + (Ideal (1/x,1/x^2*y + 1) of Maximal infinite order of Function field in y + defined by y^3 + x^6 + x^4 + x^2, 1)] """ - O = self.ring() + if self._ideal.is_prime.is_in_cache() and self._ideal.is_prime(): + return [(self, 1)] + O = self.ring() factors = [] for iprime, exp in O._to_iF(self).factor(): prime = FunctionFieldIdealInfinite_global(O, iprime) factors.append((prime, exp)) - 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. - - INPUT: - - - ``ideal`` -- fractional ideal - - EXAMPLES:: - - 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(y) - sage: [f.valuation(I) for f,_ in I.factor()] - [-1] - """ - if not self.is_prime(): - raise TypeError("not a prime ideal") - - return self._ideal.valuation(self.ring()._to_iF(ideal)) - - class IdealMonoid(UniqueRepresentation, Parent): r""" The monoid of ideals in orders of function fields. diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index 7ed5c20a7ab..b1340c0746a 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -46,6 +46,7 @@ from sage.categories.morphism import Morphism, SetMorphism from sage.categories.map import Map +from sage.categories.homset import Hom from sage.rings.morphism import RingHomomorphism class FunctionFieldDerivation(Map): @@ -89,7 +90,6 @@ def __init__(self, K): if not is_FunctionField(K): raise ValueError("K must be a function field") self.__field = K - from sage.categories.homset import Hom from sage.categories.sets_cat import Sets Map.__init__(self, Hom(K,K,Sets())) @@ -430,7 +430,6 @@ def __init__(self, V, K): self._V = V self._K = K self._R = K.polynomial_ring() - from sage.categories.homset import Hom FunctionFieldVectorSpaceIsomorphism.__init__(self, Hom(V, K)) def _call_(self, v): @@ -543,7 +542,6 @@ def __init__(self, K, V): self._K = K self._zero = K.base_ring()(0) self._n = K.degree() - from sage.categories.homset import Hom FunctionFieldVectorSpaceIsomorphism.__init__(self, Hom(K, V)) def _call_(self, x): @@ -861,7 +859,6 @@ def section(self): """ - from sage.categories.all import Hom parent = Hom(self.codomain(), self.domain()) return parent.__make_element_class__(FractionFieldToFunctionField)(parent.domain(), parent.codomain()) @@ -920,7 +917,6 @@ def section(self): To: Fraction Field of Univariate Polynomial Ring in x over Rational Field """ - from sage.categories.all import Hom parent = Hom(self.codomain(), self.domain()) return parent.__make_element_class__(FunctionFieldToFractionField)(parent) @@ -950,3 +946,56 @@ def _repr_(self): s += "\n From: {}".format(self.domain()) s += "\n To: {}".format(self.codomain()) return s + +class FunctionFieldLinearMap(SetMorphism): + """ + Linear map to function fields. + """ + def _repr_(self): + """ + Return the string representaton of the map. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) + sage: O = F.maximal_order() + sage: I = O.ideal(x-2) + sage: D = I.divisor() + sage: V, from_V, to_V = D.function_space() + sage: from_V + Linear map: + From: Vector space of dimension 2 over Finite Field of size 5 + To: Function field in y defined by y^2 + 4*x^3 + 4 + """ + s = "Linear map:" + s += "\n From: {}".format(self.domain()) + s += "\n To: {}".format(self.codomain()) + return s + +class FunctionFieldLinearMapSection(SetMorphism): + """ + Section of linear map from function fields. + """ + def _repr_(self): + """ + Return the string representaton of the map. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) + sage: O = F.maximal_order() + sage: I = O.ideal(x-2) + sage: D = I.divisor() + sage: V, from_V, to_V = D.function_space() + sage: to_V + Section of linear map: + From: Function field in y defined by y^2 + 4*x^3 + 4 + To: Vector space of dimension 2 over Finite Field of size 5 + """ + s = "Section of linear map:" + s += "\n From: {}".format(self.domain()) + s += "\n To: {}".format(self.codomain()) + return s + diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py index d03903fe61e..9f6c1f1bc62 100644 --- a/src/sage/rings/function_field/order.py +++ b/src/sage/rings/function_field/order.py @@ -89,7 +89,6 @@ #***************************************************************************** from sage.misc.cachefunc import cached_method -from sage.misc.lazy_import import lazy_import from sage.modules.free_module_element import vector from sage.arith.all import lcm, gcd @@ -101,6 +100,9 @@ from sage.categories.principal_ideal_domains import PrincipalIdealDomains from sage.categories.euclidean_domains import EuclideanDomains +from sage.matrix.special import block_matrix +from sage.matrix.constructor import matrix + from .ideal import ( IdealMonoid, FunctionFieldIdeal, @@ -111,9 +113,6 @@ FunctionFieldIdealInfinite_rational, FunctionFieldIdealInfinite_global) -lazy_import('sage.matrix.special', 'block_matrix') -lazy_import('sage.matrix.constructor', 'matrix') - class FunctionFieldOrder_base(CachedRepresentation, Parent): """ Base class for orders in function fields. diff --git a/src/sage/rings/function_field/place.py b/src/sage/rings/function_field/place.py index 89b46efb490..be414851bef 100644 --- a/src/sage/rings/function_field/place.py +++ b/src/sage/rings/function_field/place.py @@ -60,7 +60,6 @@ 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 @@ -71,8 +70,7 @@ from sage.modules.free_module_element import vector -lazy_import('sage.matrix.constructor', 'matrix') -lazy_import('sage.rings.function_field.divisor', 'prime_divisor') +from sage.matrix.constructor import matrix class FunctionFieldPlace(Element): """ @@ -155,11 +153,79 @@ def _richcmp_(self, other, op): 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) + from sage.rings.function_field.order import FunctionFieldOrderInfinite + + # effect that places at infinity are ordered first + s = not isinstance(self._prime.ring(), FunctionFieldOrderInfinite) + o = not isinstance(other._prime.ring(), FunctionFieldOrderInfinite) + return richcmp((s, self._prime), (o, other._prime), op) + + def _acted_upon_(self, other, self_on_left): + """ + Define integer multiplication upon the prime divisor + of the place on the left. + + The output is a divisor. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) + sage: O = F.maximal_order() + sage: I = O.ideal(x+1,y) + sage: P = I.place() + sage: -3*P + 5*P + 2*Place (x + 1, y) + """ + if self_on_left: + raise TypeError("only left multiplication by integers is allowed") + return other * self.divisor() + + def _add_(self, other): + """ + Return the divisor that is the sum of the place and ``other``. + + 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 + p3 + 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) + """ + from .divisor import prime_divisor + return prime_divisor(self.function_field(), self) + other + + def __radd__(self, other): + """ + Return the prime divisor of the place if ``other`` is zero. + + This is only to support the ``sum`` function, that adds + the argument to initial (int) zero. + + EXAMPLES:: + + sage: k.=GF(2) + sage: K.=FunctionField(k) + sage: sum(K.places_finite()) + Place (x) + Place (x + 1) + + Note that this does not work, as wanted:: + + sage: 0 + K.place_infinite() + Traceback (most recent call last): + ... + TypeError: unsupported operand parent(s) for +: ... + + The reason is that the ``0`` is a Sage integer, for which + the coercion system applies. + """ + if other == 0: + from .divisor import prime_divisor + return prime_divisor(self.function_field(), self) + raise NotImplementedError def function_field(self): """ @@ -190,6 +256,23 @@ def prime_ideal(self): """ return self._prime + def divisor(self, multiplicity=1): + """ + Return the prime divisor corresponding to the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: F. = K.extension(t^2-x^3-1) + sage: O = F.maximal_order() + sage: I = O.ideal(x+1,y) + sage: P = I.place() + sage: P.divisor() + Place (x + 1, y) + """ + from .divisor import prime_divisor + return prime_divisor(self.function_field(), self, multiplicity) + class FunctionFieldPlace_rational(FunctionFieldPlace): """ Places of rational function field.