Skip to content

Commit 1136114

Browse files
Release Managervbraun
Release Manager
authored andcommitted
Trac #23167: Coercions between Function Fields
The following are desired features. Coercions between base fields should induce coercions on function fields: {{{ sage: K.<x> = FunctionField(QQ) sage: L.<x> = FunctionField(GaussianIntegers().fraction_field()) sage: L.has_coerce_map_from(K) True }}} Also in towers: {{{ sage: K.<x> = FunctionField(QQ) sage: R.<y> = K[] sage: L.<y> = K.extension(y^3 + 1) sage: K.<x> = FunctionField(GaussianIntegers().fraction_field()) sage: R.<y> = K[] sage: M.<y> = K.extension(y^3 + 1) sage: M.has_coerce_map_from(L) True }}} And when the base ring coerces into the extension: {{{ sage: K.<x> = FunctionField(QQ) sage: R.<I> = K[] sage: L.<I> = K.extension(I^2 + 1) sage: M.<x> = FunctionField(GaussianIntegers().fraction_field()) sage: M.has_coerce_map_from(L) # not tested, base_morphism is not implemented True }}} URL: https://trac.sagemath.org/23167 Reported by: saraedum Ticket author(s): David Roe Reviewer(s): Julian Rüth
2 parents b76c5a3 + e2ad9b3 commit 1136114

File tree

3 files changed

+116
-28
lines changed

3 files changed

+116
-28
lines changed

src/sage/categories/rings.py

+1-5
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,7 @@ def is_injective(self):
9494
Unless the codomain is the zero ring::
9595
9696
sage: codomain = Integers(1)
97-
sage: f = K.hom([codomain(1)]); f
98-
Function Field morphism:
99-
From: Rational function field in x over Rational Field
100-
To: Ring of integers modulo 1
101-
Defn: x |--> 0
97+
sage: f = QQ.hom([Zmod(1)(0)], check=False)
10298
sage: f.is_injective()
10399
False
104100

src/sage/rings/function_field/function_field.py

+89-20
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ def order(self, x, check=True):
333333
raise NotImplementedError
334334
return self.order_with_basis(basis, check=check)
335335

336-
def _coerce_map_from_(self, R):
336+
def _coerce_map_from_(self, source):
337337
"""
338338
Return True if there is a coerce map from R to self.
339339
@@ -343,14 +343,77 @@ def _coerce_map_from_(self, R):
343343
sage: L.equation_order()
344344
Order in Function field in y defined by y^3 + x^3 + 4*x + 1
345345
sage: L._coerce_map_from_(L.equation_order())
346-
True
346+
Conversion map:
347+
From: Order in Function field in y defined by y^3 + x^3 + 4*x + 1
348+
To: Function field in y defined by y^3 + x^3 + 4*x + 1
347349
sage: L._coerce_map_from_(GF(7))
348-
False
350+
351+
sage: K.<x> = FunctionField(QQ)
352+
sage: L.<x> = FunctionField(GaussianIntegers().fraction_field())
353+
sage: L.has_coerce_map_from(K)
354+
True
355+
356+
sage: K.<x> = FunctionField(QQ)
357+
sage: R.<y> = K[]
358+
sage: L.<y> = K.extension(y^3 + 1)
359+
sage: K.<x> = FunctionField(GaussianIntegers().fraction_field())
360+
sage: R.<y> = K[]
361+
sage: M.<y> = K.extension(y^3 + 1)
362+
sage: M.has_coerce_map_from(L) # not tested (the constant field including into a function field is not yet known to be injective)
363+
True
364+
365+
sage: K.<x> = FunctionField(QQ)
366+
sage: R.<I> = K[]
367+
sage: L.<I> = K.extension(I^2 + 1)
368+
sage: M.<x> = FunctionField(GaussianIntegers().fraction_field())
369+
sage: M.has_coerce_map_from(L)
370+
True
349371
"""
350372
from .function_field_order import FunctionFieldOrder
351-
if isinstance(R, FunctionFieldOrder) and R.fraction_field() == self:
352-
return True
353-
return False
373+
if isinstance(source, FunctionFieldOrder):
374+
K = source.fraction_field()
375+
source_to_K = K._generic_convert_map(source)
376+
if K is self:
377+
return source_to_K
378+
K_to_self = self.coerce_map_from(K)
379+
if K_to_self:
380+
return K_to_self * source_to_K
381+
from sage.categories.function_fields import FunctionFields
382+
if source in FunctionFields():
383+
if source.base_field() is source:
384+
if self.base_field() is self:
385+
# source and self are rational function fields
386+
if source.variable_name() == self.variable_name():
387+
# ... in the same variable
388+
base_coercion = self.constant_field().coerce_map_from(source.constant_field())
389+
if base_coercion is not None:
390+
return source.hom([self.gen()], base_morphism=base_coercion)
391+
else:
392+
# source is an extensions of rational function fields
393+
base_coercion = self.coerce_map_from(source.base_field())
394+
if base_coercion is not None and base_coercion.is_injective():
395+
# the base field of source coerces into the base field of self
396+
self_polynomial = source.polynomial().map_coefficients(base_coercion)
397+
# try to find a root of the defining polynomial in self
398+
if self_polynomial(self.gen()) == 0:
399+
# The defining polynomial of source has a root in self,
400+
# therefore there is a map. To be sure that it is
401+
# canonical, we require a root of the defining polynomial
402+
# of self to be a root of the defining polynomial of
403+
# source (and that the variables are named equally):
404+
if source.variable_name() == self.variable_name():
405+
return source.hom([self.gen()], base_morphism=base_coercion)
406+
407+
try:
408+
sourcegen_in_self = self(source.variable_name())
409+
except TypeError:
410+
pass
411+
else:
412+
if self_polynomial(sourcegen_in_self) == 0:
413+
# The defining polynomial of source has a root in self,
414+
# therefore there is a map. To be sure that it is
415+
# canonical, we require the names of the roots to match
416+
return source.hom([sourcegen_in_self], base_morphism=base_coercion)
354417

355418
def _test_derivation(self, **options):
356419
"""
@@ -1203,12 +1266,16 @@ def hom(self, im_gens, base_morphism=None):
12031266
Defn: y |--> -y
12041267
x |--> x
12051268
1206-
The usage of the keyword base_morphism is not implemented yet::
1269+
You can also specify a morphism on the base::
12071270
1208-
sage: L.hom([-y, x-1], base_morphism=phi)
1209-
Traceback (most recent call last):
1210-
...
1211-
NotImplementedError: Function field homorphisms with optional argument base_morphism are not implemented yet. Please specify the images of the generators of the base fields manually.
1271+
sage: R1.<r> = K[]
1272+
sage: L1.<r> = K.extension(r^2 - (x+1)^3 - 1)
1273+
sage: L.hom(r, base_morphism=phi)
1274+
Function Field morphism:
1275+
From: Function field in y defined by y^2 - x^3 - 1
1276+
To: Function field in r defined by r^2 - x^3 - 3*x^2 - 3*x - 2
1277+
Defn: y |--> r
1278+
x |--> x + 1
12121279
12131280
We make another extension of a rational function field::
12141281
@@ -1246,9 +1313,6 @@ def hom(self, im_gens, base_morphism=None):
12461313
yy |--> y
12471314
12481315
"""
1249-
if base_morphism is not None:
1250-
raise NotImplementedError("Function field homorphisms with optional argument base_morphism are not implemented yet. Please specify the images of the generators of the base fields manually.")
1251-
12521316
if not isinstance(im_gens, (list,tuple)):
12531317
im_gens = [im_gens]
12541318
if len(im_gens) == 0:
@@ -1260,8 +1324,8 @@ def hom(self, im_gens, base_morphism=None):
12601324
# the codomain of this morphism is the field containing all the im_gens
12611325
codomain = im_gens[0].parent();
12621326
if base_morphism is not None:
1263-
if base_morphism.codomain().has_coerce_map_from(codomain):
1264-
codomain = base_morphism.codomain();
1327+
from sage.categories.pushout import pushout
1328+
codomain = pushout(codomain, base_morphism.codomain())
12651329

12661330
from .maps import FunctionFieldMorphism_polymod
12671331
return FunctionFieldMorphism_polymod(self.Hom(codomain), im_gens[0], base_morphism)
@@ -1821,12 +1885,14 @@ def base_field(self):
18211885

18221886
def hom(self, im_gens, base_morphism=None):
18231887
"""
1824-
Create a homomorphism from self to another function field.
1888+
Create a homomorphism from self to another ring.
18251889
18261890
INPUT:
18271891
1828-
- ``im_gens`` -- exactly one element of some function field
1829-
- ``base_morphism`` -- ignored
1892+
- ``im_gens`` -- exactly one element of some ring. It must be invertible and trascendental over
1893+
the image of ``base_morphism``; this is not checked.
1894+
- ``base_morphism`` -- a homomorphism from the base field into the other ring.
1895+
If ``None``, try to use a coercion map.
18301896
18311897
OUTPUT:
18321898
@@ -1864,8 +1930,11 @@ def hom(self, im_gens, base_morphism=None):
18641930
if len(im_gens) != 1:
18651931
raise ValueError("there must be exactly one generator")
18661932
x = im_gens[0]
1933+
R = x.parent()
1934+
if base_morphism is None and not R.has_coerce_map_from(self.constant_field()):
1935+
raise ValueError("You must specify a morphism on the base field")
18671936
from .maps import FunctionFieldMorphism_rational
1868-
return FunctionFieldMorphism_rational(self.Hom(x.parent()), x)
1937+
return FunctionFieldMorphism_rational(self.Hom(R), x, base_morphism)
18691938

18701939
def field(self):
18711940
"""

src/sage/rings/function_field/maps.py

+26-3
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ class FunctionFieldMorphism_rational(FunctionFieldMorphism):
655655
Function Field endomorphism of Rational function field in x over Rational Field
656656
Defn: x |--> 1/x
657657
"""
658-
def __init__(self, parent, im_gen):
658+
def __init__(self, parent, im_gen, base_morphism):
659659
"""
660660
EXAMPLES::
661661
@@ -666,7 +666,7 @@ def __init__(self, parent, im_gen):
666666
sage: type(f)
667667
<class 'sage.rings.function_field.maps.FunctionFieldMorphism_rational'>
668668
"""
669-
FunctionFieldMorphism.__init__(self, parent, im_gen, None)
669+
FunctionFieldMorphism.__init__(self, parent, im_gen, base_morphism)
670670

671671
def _call_(self, x):
672672
"""
@@ -680,9 +680,32 @@ def _call_(self, x):
680680
(x + 1)/x
681681
sage: 1/x + 1
682682
(x + 1)/x
683+
684+
You can specify a morphism on the base ring::
685+
686+
sage: Qi = GaussianIntegers().fraction_field()
687+
sage: i = Qi.gen()
688+
sage: K.<x> = FunctionField(Qi)
689+
sage: phi1 = Qi.hom([CC.gen()])
690+
sage: phi2 = Qi.hom([-CC.gen()])
691+
sage: f = K.hom(CC.pi(),phi1)
692+
sage: f(1+i+x)
693+
4.14159265358979 + 1.00000000000000*I
694+
sage: g = K.hom(CC.pi(),phi2)
695+
sage: g(1+i+x)
696+
4.14159265358979 - 1.00000000000000*I
683697
"""
684698
a = x.element()
685-
return a.subs({a.parent().gen():self._im_gen})
699+
if self._base_morphism is None:
700+
return a.subs({a.parent().gen():self._im_gen})
701+
else:
702+
f = self._base_morphism
703+
num = a.numerator()
704+
den = a.denominator()
705+
R = self._im_gen.parent()['X']
706+
num = R([f(c) for c in num.list()])
707+
den = R([f(c) for c in den.list()])
708+
return num.subs(self._im_gen) / den.subs(self._im_gen)
686709

687710
class FunctionFieldConversionToConstantBaseField(Map):
688711
r"""

0 commit comments

Comments
 (0)