diff --git a/src/doc/en/developer/packaging_sage_library.rst b/src/doc/en/developer/packaging_sage_library.rst index 7e7a5cde56f..229c3dc01d7 100644 --- a/src/doc/en/developer/packaging_sage_library.rst +++ b/src/doc/en/developer/packaging_sage_library.rst @@ -567,6 +567,15 @@ doctests that depend on :class:`sage.symbolic.ring.SymbolicRing` for integration testing. Hence, these doctests are marked ``# optional - sage.symbolic``. +When defining new features for the purpose of doctest annotations, it may be a good +idea to hide implementation details from feature names. For example, all doctests that +use finite fields have to depend on PARI. However, we have defined a feature +:mod:`sage.rings.finite_rings` (which implies the presence of :mod:`sage.libs.pari`). +Annotating the doctests ``# optional - sage.rings.finite_rings`` expresses the +dependency in a clearer way than using ``# optional - sage.libs.pari``, and it +will be a smaller maintenance burden when implementation details change. + + Testing the distribution in virtual environments with tox --------------------------------------------------------- diff --git a/src/doc/en/reference/function_fields/index.rst b/src/doc/en/reference/function_fields/index.rst index 50c04560a33..9f9a7e8c42d 100644 --- a/src/doc/en/reference/function_fields/index.rst +++ b/src/doc/en/reference/function_fields/index.rst @@ -11,13 +11,27 @@ algebraic closure of `\QQ`. :maxdepth: 1 sage/rings/function_field/function_field + sage/rings/function_field/function_field_rational + sage/rings/function_field/function_field_polymod sage/rings/function_field/element + sage/rings/function_field/element_rational + sage/rings/function_field/element_polymod sage/rings/function_field/order + sage/rings/function_field/order_rational + sage/rings/function_field/order_basis + sage/rings/function_field/order_polymod sage/rings/function_field/ideal + sage/rings/function_field/ideal_rational + sage/rings/function_field/ideal_polymod sage/rings/function_field/place + sage/rings/function_field/place_rational + sage/rings/function_field/place_polymod sage/rings/function_field/divisor sage/rings/function_field/differential sage/rings/function_field/valuation_ring + sage/rings/function_field/derivations + sage/rings/function_field/derivations_rational + sage/rings/function_field/derivations_polymod sage/rings/function_field/maps sage/rings/function_field/extensions sage/rings/function_field/constructor diff --git a/src/doc/en/reference/valuations/index.rst b/src/doc/en/reference/valuations/index.rst index 90a8c3c97f7..bd09af43248 100644 --- a/src/doc/en/reference/valuations/index.rst +++ b/src/doc/en/reference/valuations/index.rst @@ -201,7 +201,7 @@ More Details sage/rings/valuation/mapped_valuation sage/rings/valuation/scaled_valuation - sage/rings/function_field/function_field_valuation + sage/rings/function_field/valuation sage/rings/padics/padic_valuation .. include:: ../footer.txt diff --git a/src/sage/categories/function_fields.py b/src/sage/categories/function_fields.py index 4536973233d..093d2ff6fac 100644 --- a/src/sage/categories/function_fields.py +++ b/src/sage/categories/function_fields.py @@ -52,14 +52,14 @@ def _call_(self, x): EXAMPLES:: sage: C = FunctionFields() - sage: K.=FunctionField(QQ) + sage: K. = FunctionField(QQ) sage: C(K) Rational function field in x over Rational Field sage: Ky. = K[] - sage: L = K.extension(y^2-x) - sage: C(L) + sage: L = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: C(L) # optional - sage.rings.function_field Function field in y defined by y^2 - x - sage: C(L.equation_order()) + sage: C(L.equation_order()) # optional - sage.rings.function_field Function field in y defined by y^2 - x """ try: diff --git a/src/sage/features/__init__.py b/src/sage/features/__init__.py index cb2e2a085c3..fc60ac746b7 100644 --- a/src/sage/features/__init__.py +++ b/src/sage/features/__init__.py @@ -52,6 +52,19 @@ As can be seen above, features try to produce helpful error messages. """ +# ***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# 2018 Jeroen Demeyer +# 2018 Timo Kaufmann +# 2019-2022 Matthias Koeppe +# 2021 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from __future__ import annotations import os diff --git a/src/sage/features/all.py b/src/sage/features/all.py index 2ec0c267b83..19eabe60126 100644 --- a/src/sage/features/all.py +++ b/src/sage/features/all.py @@ -2,6 +2,15 @@ Enumeration of all defined features """ +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + def all_features(): r""" Return an iterable of all features. diff --git a/src/sage/features/bliss.py b/src/sage/features/bliss.py index e8efd3e8f97..aeadedfebff 100644 --- a/src/sage/features/bliss.py +++ b/src/sage/features/bliss.py @@ -1,7 +1,18 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of ``bliss`` """ + +# ***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# 2018 Jeroen Demeyer +# 2021 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import CythonFeature, PythonModule from .join_feature import JoinFeature diff --git a/src/sage/features/cddlib.py b/src/sage/features/cddlib.py index 59a72a2120f..5badbd251c0 100644 --- a/src/sage/features/cddlib.py +++ b/src/sage/features/cddlib.py @@ -1,6 +1,16 @@ r""" Feature for testing the presence of ``cddlib`` """ + +# ***************************************************************************** +# Copyright (C) 2022 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import Executable diff --git a/src/sage/features/csdp.py b/src/sage/features/csdp.py index e86ec415d09..9c04779558c 100644 --- a/src/sage/features/csdp.py +++ b/src/sage/features/csdp.py @@ -3,6 +3,18 @@ Feature for testing the presence of ``csdp`` """ +# ***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# 2018 Jeroen Demeyer +# 2019 David Coudert +# 2021 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + import os import re import subprocess diff --git a/src/sage/features/cython.py b/src/sage/features/cython.py index f537843f4f4..8f03155ac2f 100644 --- a/src/sage/features/cython.py +++ b/src/sage/features/cython.py @@ -1,8 +1,16 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of ``cython`` """ +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import CythonFeature diff --git a/src/sage/features/databases.py b/src/sage/features/databases.py index d1e77a8ff32..7fa05ca099a 100644 --- a/src/sage/features/databases.py +++ b/src/sage/features/databases.py @@ -1,8 +1,21 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of various databases """ +# ***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# 2018-2019 Jeroen Demeyer +# 2018 Timo Kaufmann +# 2020-2022 Matthias Koeppe +# 2020-2022 Sebastian Oehms +# 2021 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import StaticFile, PythonModule from sage.env import ( diff --git a/src/sage/features/dvipng.py b/src/sage/features/dvipng.py index 44ef6c7074a..281084a6e72 100644 --- a/src/sage/features/dvipng.py +++ b/src/sage/features/dvipng.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Feature for testing the presence of ``dvipng`` """ diff --git a/src/sage/features/ffmpeg.py b/src/sage/features/ffmpeg.py index bb708478251..681af26494c 100644 --- a/src/sage/features/ffmpeg.py +++ b/src/sage/features/ffmpeg.py @@ -1,9 +1,8 @@ -# -*- coding: utf-8 -*- r""" Feature for testing the presence of ``ffmpeg`` """ # **************************************************************************** -# Copyright (C) 2018 Sebastien Labbe +# Copyright (C) 2018-2022 Sebastien Labbe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/features/gap.py b/src/sage/features/gap.py index 20add30c114..67c905a35c3 100644 --- a/src/sage/features/gap.py +++ b/src/sage/features/gap.py @@ -1,9 +1,18 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of GAP packages """ +# ***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# 2018 Jeroen Demeyer +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** -from . import Feature, FeatureTestResult +from . import Feature, FeatureTestResult, PythonModule +from .join_feature import JoinFeature class GapPackage(Feature): @@ -48,3 +57,34 @@ def _is_present(self): else: return FeatureTestResult(self, False, reason="`{command}` evaluated to `{presence}` in GAP.".format(command=command, presence=presence)) + + +class sage__libs__gap(JoinFeature): + r""" + A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.gap` + (the library interface to GAP) and :mod:`sage.interfaces.gap` (the pexpect + interface to GAP). By design, we do not distinguish between these two, in order + to facilitate the conversion of code from the pexpect interface to the library + interface. + + EXAMPLES:: + + sage: from sage.features.gap import sage__libs__gap + sage: sage__libs__gap().is_present() # optional - sage.libs.gap + FeatureTestResult('sage.libs.gap', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.gap import sage__libs__gap + sage: isinstance(sage__libs__gap(), sage__libs__gap) + True + """ + JoinFeature.__init__(self, 'sage.libs.gap', + [PythonModule('sage.libs.gap.libgap'), + PythonModule('sage.interfaces.gap')]) + + +def all_features(): + return [sage__libs__gap()] diff --git a/src/sage/features/gfan.py b/src/sage/features/gfan.py index 6551ca5340c..d9a74db9e1b 100644 --- a/src/sage/features/gfan.py +++ b/src/sage/features/gfan.py @@ -2,6 +2,15 @@ Features for testing the presence of ``gfan`` """ +# ***************************************************************************** +# Copyright (C) 2022 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import Executable diff --git a/src/sage/features/graph_generators.py b/src/sage/features/graph_generators.py index 0ecd63bad76..3d484c59f6b 100644 --- a/src/sage/features/graph_generators.py +++ b/src/sage/features/graph_generators.py @@ -1,8 +1,20 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of various graph generator programs """ +# ***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# 2018 Jeroen Demeyer +# 2019 Frédéric Chapoton +# 2021 Matthias Koeppe +# 2021 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + import os import subprocess diff --git a/src/sage/features/graphviz.py b/src/sage/features/graphviz.py index 0a0c8ba88e9..2105eda9d8c 100644 --- a/src/sage/features/graphviz.py +++ b/src/sage/features/graphviz.py @@ -1,9 +1,10 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of ``graphviz`` """ + # **************************************************************************** # Copyright (C) 2018 Sebastien Labbe +# 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -11,6 +12,7 @@ # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** + from . import Executable from .join_feature import JoinFeature diff --git a/src/sage/features/igraph.py b/src/sage/features/igraph.py index 04ac4efbb54..6bb83294a95 100644 --- a/src/sage/features/igraph.py +++ b/src/sage/features/igraph.py @@ -1,6 +1,17 @@ r""" Check for igraph """ + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + from . import PythonModule from .join_feature import JoinFeature diff --git a/src/sage/features/imagemagick.py b/src/sage/features/imagemagick.py index 59e35cb5a7a..b7aa3aeb9a2 100644 --- a/src/sage/features/imagemagick.py +++ b/src/sage/features/imagemagick.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Feature for testing the presence of ``imagemagick`` @@ -7,8 +6,9 @@ ``identify``, ``composite``, ``montage``, ``compare``, etc. could be also checked in this module. """ + # **************************************************************************** -# Copyright (C) 2018 Sebastien Labbe +# Copyright (C) 2018-2022 Sebastien Labbe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/features/interfaces.py b/src/sage/features/interfaces.py index b77c2cc4f88..e31e7b72d61 100644 --- a/src/sage/features/interfaces.py +++ b/src/sage/features/interfaces.py @@ -1,6 +1,17 @@ r""" Features for testing whether interpreter interfaces are functional """ + +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + import importlib from . import Feature, FeatureTestResult, PythonModule diff --git a/src/sage/features/internet.py b/src/sage/features/internet.py index 6e800120828..f1eb000fe92 100644 --- a/src/sage/features/internet.py +++ b/src/sage/features/internet.py @@ -2,6 +2,16 @@ Feature for testing if the Internet is available """ +# **************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + from . import Feature, FeatureTestResult diff --git a/src/sage/features/join_feature.py b/src/sage/features/join_feature.py index 94830eead1e..d02ad669833 100644 --- a/src/sage/features/join_feature.py +++ b/src/sage/features/join_feature.py @@ -2,6 +2,17 @@ Join features """ +# **************************************************************************** +# Copyright (C) 2021-2022 Matthias Koeppe +# 2021-2022 Kwankyu Lee +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + from . import Feature, FeatureTestResult diff --git a/src/sage/features/kenzo.py b/src/sage/features/kenzo.py index df2e658e975..655602427b8 100644 --- a/src/sage/features/kenzo.py +++ b/src/sage/features/kenzo.py @@ -1,8 +1,20 @@ -# -*- coding: utf-8 -*- r""" Feature for testing the presence of ``kenzo`` """ +# **************************************************************************** +# Copyright (C) 2020 Travis Scrimshaw +# 2021 Matthias Koeppe +# 2021 Michael Orlitzky +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + + from . import Feature, FeatureTestResult class Kenzo(Feature): diff --git a/src/sage/features/latex.py b/src/sage/features/latex.py index 02bcb57a0ca..1b01db39f67 100644 --- a/src/sage/features/latex.py +++ b/src/sage/features/latex.py @@ -1,9 +1,12 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of ``latex`` and equivalent programs """ + # **************************************************************************** # Copyright (C) 2021 Sebastien Labbe +# 2021 Matthias Koeppe +# 2022 Kwankyu Lee +# 2022 Sebastian Oehms # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/features/latte.py b/src/sage/features/latte.py index 6202152501f..e88752bd561 100644 --- a/src/sage/features/latte.py +++ b/src/sage/features/latte.py @@ -1,7 +1,20 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of ``latte_int`` """ + +# **************************************************************************** +# Copyright (C) 2018 Vincent Delecroix +# 2019 Frédéric Chapoton +# 2021 Matthias Koeppe +# 2021 Kwankyu Lee +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# **************************************************************************** + from . import Executable from .join_feature import JoinFeature diff --git a/src/sage/features/lrs.py b/src/sage/features/lrs.py index d7b15fbcdd6..501d1d371e7 100644 --- a/src/sage/features/lrs.py +++ b/src/sage/features/lrs.py @@ -3,6 +3,19 @@ Feature for testing the presence of ``lrslib`` """ +# ***************************************************************************** +# Copyright (C) 2016 Julian Rüth +# 2018 Jeroen Demeyer +# 2021-2022 Matthias Koeppe +# 2021 Kwankyu Lee +# 2022 Sébastien Labbé +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + import subprocess from . import Executable, FeatureTestResult diff --git a/src/sage/features/mcqd.py b/src/sage/features/mcqd.py index 131b175aabc..faf7ace441d 100644 --- a/src/sage/features/mcqd.py +++ b/src/sage/features/mcqd.py @@ -2,6 +2,15 @@ Features for testing the presence of ``mcqd`` """ +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import PythonModule from .join_feature import JoinFeature diff --git a/src/sage/features/meataxe.py b/src/sage/features/meataxe.py index d3f7fce200e..78de1bdfe03 100644 --- a/src/sage/features/meataxe.py +++ b/src/sage/features/meataxe.py @@ -2,6 +2,17 @@ Feature for testing the presence of ``meataxe`` """ +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# 2021 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + + from . import PythonModule from .join_feature import JoinFeature diff --git a/src/sage/features/mip_backends.py b/src/sage/features/mip_backends.py index 477d88efabf..d276e25abe8 100644 --- a/src/sage/features/mip_backends.py +++ b/src/sage/features/mip_backends.py @@ -2,6 +2,16 @@ Features for testing the presence of :class:`MixedIntegerLinearProgram` backends """ +# ***************************************************************************** +# Copyright (C) 2021-2022 Matthias Koeppe +# 2021 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import Feature, PythonModule, FeatureTestResult from .join_feature import JoinFeature diff --git a/src/sage/features/msolve.py b/src/sage/features/msolve.py index a7c7d5441b7..557632ff500 100644 --- a/src/sage/features/msolve.py +++ b/src/sage/features/msolve.py @@ -9,6 +9,15 @@ - :mod:`sage.rings.polynomial.msolve` """ +# ***************************************************************************** +# Copyright (C) 2022 Marc Mezzarobba +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + import subprocess from . import Executable from . import FeatureTestResult diff --git a/src/sage/features/nauty.py b/src/sage/features/nauty.py index 86683eb29df..f3df59f4a9c 100644 --- a/src/sage/features/nauty.py +++ b/src/sage/features/nauty.py @@ -2,6 +2,15 @@ Features for testing the presence of nauty executables """ +# ***************************************************************************** +# Copyright (C) 2022 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from sage.env import SAGE_NAUTY_BINS_PREFIX from . import Executable diff --git a/src/sage/features/normaliz.py b/src/sage/features/normaliz.py index 5c3ab4255f3..acf8c9242c1 100644 --- a/src/sage/features/normaliz.py +++ b/src/sage/features/normaliz.py @@ -1,6 +1,16 @@ r""" Feature for testing the presence of ``pynormaliz`` """ + +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import PythonModule from .join_feature import JoinFeature diff --git a/src/sage/features/pandoc.py b/src/sage/features/pandoc.py index 0a2bbcdacdb..a00a2b6f8e4 100644 --- a/src/sage/features/pandoc.py +++ b/src/sage/features/pandoc.py @@ -4,6 +4,7 @@ """ # **************************************************************************** # Copyright (C) 2018 Thierry Monteil +# 2021 Matthias Koeppe # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by diff --git a/src/sage/features/pdf2svg.py b/src/sage/features/pdf2svg.py index 9de3fb3cfd6..2b60574fe85 100644 --- a/src/sage/features/pdf2svg.py +++ b/src/sage/features/pdf2svg.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Feature for testing the presence of ``pdf2svg`` """ diff --git a/src/sage/features/phitigra.py b/src/sage/features/phitigra.py index f792d7b47cd..60ef8f46dde 100644 --- a/src/sage/features/phitigra.py +++ b/src/sage/features/phitigra.py @@ -1,6 +1,16 @@ r""" Check for phitigra """ + +# ***************************************************************************** +# Copyright (C) 2022 Jean-Florent Raymond +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import PythonModule diff --git a/src/sage/features/pkg_systems.py b/src/sage/features/pkg_systems.py index 5e31ec9f66d..334f8378050 100644 --- a/src/sage/features/pkg_systems.py +++ b/src/sage/features/pkg_systems.py @@ -1,6 +1,16 @@ r""" Features for testing the presence of package systems """ + +# ***************************************************************************** +# Copyright (C) 2021-2022 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import Feature diff --git a/src/sage/features/polymake.py b/src/sage/features/polymake.py index 579951c047f..6168c6c9978 100644 --- a/src/sage/features/polymake.py +++ b/src/sage/features/polymake.py @@ -1,6 +1,16 @@ r""" Feature for testing the presence of the Python interface to polymake """ + +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import PythonModule from .join_feature import JoinFeature diff --git a/src/sage/features/poppler.py b/src/sage/features/poppler.py index 1b0379bc0d2..b8f8586e7f5 100644 --- a/src/sage/features/poppler.py +++ b/src/sage/features/poppler.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Check for poppler features diff --git a/src/sage/features/rubiks.py b/src/sage/features/rubiks.py index b418c1571cb..b9bd2f126f7 100644 --- a/src/sage/features/rubiks.py +++ b/src/sage/features/rubiks.py @@ -1,14 +1,17 @@ -# -*- coding: utf-8 -*- r""" Features for testing the presence of ``rubiks`` """ # **************************************************************************** +# Copyright (C) 2020 John H. Palmieri +# 2021-2022 Matthias Koeppe +# # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # https://www.gnu.org/licenses/ # **************************************************************************** + from sage.env import RUBIKS_BINS_PREFIX from . import Executable diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index 3cb66162f07..7e545404de2 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -1,8 +1,20 @@ r""" Features for testing the presence of Python modules in the Sage library """ + +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# 2021 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import PythonModule, StaticFile from .join_feature import JoinFeature +from .singular import sage__libs__singular class sagemath_doc_html(StaticFile): @@ -144,6 +156,28 @@ def __init__(self): [PythonModule('sage.libs.pari.convert_sage')]) +class sage__modules(JoinFeature): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.modules`. + + EXAMPLES:: + + sage: from sage.features.sagemath import sage__modules + sage: sage__modules().is_present() # optional - sage.modules + FeatureTestResult('sage.modules', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__modules + sage: isinstance(sage__modules(), sage__modules) + True + """ + JoinFeature.__init__(self, 'sage.modules', + [PythonModule('sage.modules.free_module')]) + + class sage__plot(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.plot`. @@ -166,6 +200,52 @@ def __init__(self): [PythonModule('sage.plot.plot')]) +class sage__rings__finite_rings(JoinFeature): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.finite_rings`; + specifically, the element implementations using PARI. + + EXAMPLES:: + + sage: from sage.features.sagemath import sage__rings__finite_rings + sage: sage__rings__finite_rings().is_present() # optional - sage.rings.finite_rings + FeatureTestResult('sage.rings.finite_rings', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__rings__finite_rings + sage: isinstance(sage__rings__finite_rings(), sage__rings__finite_rings) + True + """ + JoinFeature.__init__(self, 'sage.rings.finite_rings', + [PythonModule('sage.rings.finite_rings.element_pari_ffelt')]) + + +class sage__rings__function_field(JoinFeature): + r""" + A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.function_field`. + + EXAMPLES:: + + sage: from sage.features.sagemath import sage__rings__function_field + sage: sage__rings__function_field().is_present() # optional - sage.rings.function_field + FeatureTestResult('sage.rings.function_field', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__rings__function_field + sage: isinstance(sage__rings__function_field(), sage__rings__function_field) + True + """ + JoinFeature.__init__(self, 'sage.rings.function_field', + [PythonModule('sage.rings.function_field.function_field_polymod'), + sage__libs__singular()]) + + class sage__rings__number_field(JoinFeature): r""" A :class:`~sage.features.Feature` describing the presence of :mod:`sage.rings.number_field`. @@ -303,7 +383,10 @@ def all_features(): sage__graphs(), sage__groups(), sage__libs__pari(), + sage__modules(), sage__plot(), + sage__rings__finite_rings(), + sage__rings__function_field(), sage__rings__number_field(), sage__rings__padics(), sage__rings__real_double(), diff --git a/src/sage/features/singular.py b/src/sage/features/singular.py index 610e195c641..c3bd3b6be03 100644 --- a/src/sage/features/singular.py +++ b/src/sage/features/singular.py @@ -1,7 +1,18 @@ r""" Features for testing the presence of Singular """ -from . import Executable + +# ***************************************************************************** +# Copyright (C) 2022-2023 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + +from . import Executable, PythonModule +from .join_feature import JoinFeature from sage.env import SINGULAR_BIN @@ -25,3 +36,35 @@ def __init__(self): """ Executable.__init__(self, "singular", SINGULAR_BIN, spkg='singular') + + +class sage__libs__singular(JoinFeature): + r""" + A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.singular` + (the library interface to Singular) and :mod:`sage.interfaces.singular` (the pexpect + interface to Singular). By design, we do not distinguish between these two, in order + to facilitate the conversion of code from the pexpect interface to the library + interface. + + EXAMPLES:: + + sage: from sage.features.singular import sage__libs__singular + sage: sage__libs__singular().is_present() # optional - sage.libs.singular + FeatureTestResult('sage.libs.singular', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.singular import sage__libs__singular + sage: isinstance(sage__libs__singular(), sage__libs__singular) + True + """ + JoinFeature.__init__(self, 'sage.libs.singular', + [PythonModule('sage.libs.singular.singular'), + PythonModule('sage.interfaces.singular')]) + + +def all_features(): + return [Singular(), + sage__libs__singular()] diff --git a/src/sage/features/sphinx.py b/src/sage/features/sphinx.py index d29de3f34f4..44283765fd1 100644 --- a/src/sage/features/sphinx.py +++ b/src/sage/features/sphinx.py @@ -1,6 +1,16 @@ r""" Check for Sphinx """ + +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import PythonModule diff --git a/src/sage/features/standard.py b/src/sage/features/standard.py new file mode 100644 index 00000000000..f48f47f2614 --- /dev/null +++ b/src/sage/features/standard.py @@ -0,0 +1,35 @@ +r""" +Check for various standard packages (for modularized distributions) + +These features are provided by standard packages in the Sage distribution. +""" + +# ***************************************************************************** +# Copyright (C) 2023 Matthias Koeppe +# +# 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + +from . import PythonModule +from .join_feature import JoinFeature + + +def all_features(): + return [PythonModule('cvxopt', spkg='cvxopt'), + PythonModule('fpylll', spkg='fpylll'), + JoinFeature('ipython', (PythonModule('IPython'),), spkg='ipython'), + JoinFeature('lrcalc_python', (PythonModule('lrcalc'),), spkg='lrcalc_python'), + PythonModule('mpmath', spkg='mpmath'), + PythonModule('networkx', spkg='networkx'), + PythonModule('numpy', spkg='numpy'), + PythonModule('pexpect', spkg='pexpect'), + JoinFeature('pillow', (PythonModule('PIL'),), spkg='pillow'), + JoinFeature('pplpy', (PythonModule('ppl'),), spkg='pplpy'), + PythonModule('primecountpy', spkg='primecountpy'), + PythonModule('ptyprocess', spkg='ptyprocess'), + PythonModule('requests', spkg='requests'), + PythonModule('scipy', spkg='scipy'), + PythonModule('sympy', spkg='sympy')] diff --git a/src/sage/features/tdlib.py b/src/sage/features/tdlib.py index ff38f6e23a5..afde40f7de7 100644 --- a/src/sage/features/tdlib.py +++ b/src/sage/features/tdlib.py @@ -2,6 +2,16 @@ Features for testing the presence of ``tdlib`` """ +# ***************************************************************************** +# Copyright (C) 2021 Matthias Koeppe +# 2021 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. +# https://www.gnu.org/licenses/ +# ***************************************************************************** + from . import PythonModule from .join_feature import JoinFeature diff --git a/src/sage/rings/derivation.py b/src/sage/rings/derivation.py index 29f3a18d662..4c8cb4662b9 100644 --- a/src/sage/rings/derivation.py +++ b/src/sage/rings/derivation.py @@ -196,7 +196,8 @@ from sage.rings.polynomial.polynomial_quotient_ring import PolynomialQuotientRing_generic from sage.rings.finite_rings.integer_mod_ring import IntegerModRing_generic from sage.rings.padics.padic_generic import pAdicGeneric -from sage.rings.function_field.function_field import FunctionField, RationalFunctionField +from sage.rings.function_field.function_field import FunctionField +from sage.rings.function_field.function_field_rational import RationalFunctionField from sage.categories.number_fields import NumberFields from sage.categories.finite_fields import FiniteFields from sage.categories.modules import Modules @@ -391,13 +392,13 @@ def __init__(self, domain, codomain, twist=None): constants, sharp = self._base_derivation._constants self._constants = (constants, False) # can we do better? elif isinstance(domain, RationalFunctionField): - from sage.rings.function_field.maps import FunctionFieldDerivation_rational + from sage.rings.function_field.derivations_rational import FunctionFieldDerivation_rational self.Element = FunctionFieldDerivation_rational self._gens = self._basis = [ None ] self._dual_basis = [ domain.gen() ] elif isinstance(domain, FunctionField): if domain.is_separable(): - from sage.rings.function_field.maps import FunctionFieldDerivation_separable + from sage.rings.function_field.derivations_polymod import FunctionFieldDerivation_separable self._base_derivation = RingDerivationModule(domain.base_ring(), defining_morphism) self.Element = FunctionFieldDerivation_separable try: @@ -410,7 +411,7 @@ def __init__(self, domain, codomain, twist=None): except NotImplementedError: pass else: - from sage.rings.function_field.maps import FunctionFieldDerivation_inseparable + from sage.rings.function_field.derivations_polymod import FunctionFieldDerivation_inseparable M, f, self._t = domain.separable_model() self._base_derivation = RingDerivationModule(M, defining_morphism * f) self._d = self._base_derivation(None) diff --git a/src/sage/rings/fraction_field.py b/src/sage/rings/fraction_field.py index 3f1964b13d2..02805d36142 100644 --- a/src/sage/rings/fraction_field.py +++ b/src/sage/rings/fraction_field.py @@ -1056,7 +1056,7 @@ def _coerce_map_from_(self, R): 1/t """ - from sage.rings.function_field.function_field import RationalFunctionField + from sage.rings.function_field.function_field_rational import RationalFunctionField if isinstance(R, RationalFunctionField) and self.variable_name() == R.variable_name() and self.base_ring() is R.constant_base_field(): from sage.categories.homset import Hom parent = Hom(R, self) diff --git a/src/sage/rings/function_field/constructor.py b/src/sage/rings/function_field/constructor.py index a459b9e51ac..f34024aa03b 100644 --- a/src/sage/rings/function_field/constructor.py +++ b/src/sage/rings/function_field/constructor.py @@ -25,9 +25,11 @@ """ #***************************************************************************** -# Copyright (C) 2010 William Stein -# Copyright (C) 2011 Maarten Derickx -# Copyright (C) 2011 Julian Rueth +# Copyright (C) 2010 William Stein +# 2011 Maarten Derickx +# 2011-2014 Julian Rueth +# 2012 Travis Scrimshaw +# 2017-2019 Kwankyu Lee # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -54,10 +56,10 @@ class FunctionFieldFactory(UniqueFactory): sage: K. = FunctionField(QQ); K Rational function field in x over Rational Field - sage: L. = FunctionField(GF(7)); L + sage: L. = FunctionField(GF(7)); L # optional - sage.rings.finite_rings Rational function field in y over Finite Field of size 7 - sage: R. = L[] - sage: M. = L.extension(z^7-z-y); M + sage: R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^7 - z - y); M # optional - sage.rings.finite_rings sage.rings.function_field Function field in z defined by z^7 + 6*z + 6*y TESTS:: @@ -66,8 +68,8 @@ class FunctionFieldFactory(UniqueFactory): sage: L. = FunctionField(QQ) sage: K is L True - sage: M. = FunctionField(GF(7)) - sage: K is M + sage: M. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: K is M # optional - sage.rings.finite_rings False sage: N. = FunctionField(QQ) sage: K is N @@ -99,13 +101,13 @@ def create_object(self, version, key,**extra_args): True """ if key[0].is_finite(): - from .function_field import RationalFunctionField_global + from .function_field_rational import RationalFunctionField_global return RationalFunctionField_global(key[0], names=key[1]) elif key[0].characteristic() == 0: - from .function_field import RationalFunctionField_char_zero + from .function_field_rational import RationalFunctionField_char_zero return RationalFunctionField_char_zero(key[0], names=key[1]) else: - from .function_field import RationalFunctionField + from .function_field_rational import RationalFunctionField return RationalFunctionField(key[0], names=key[1]) FunctionField=FunctionFieldFactory("sage.rings.function_field.constructor.FunctionField") @@ -133,9 +135,9 @@ class FunctionFieldExtensionFactory(UniqueFactory): sage: y2 = y*1 sage: y2 is y False - sage: L.=K.extension(x - y^2) - sage: M.=K.extension(x - y2^2) - sage: L is M + sage: L. = K.extension(x - y^2) # optional - sage.rings.function_field + sage: M. = K.extension(x - y2^2) # optional - sage.rings.function_field + sage: L is M # optional - sage.rings.function_field True """ def create_key(self,polynomial,names): @@ -147,7 +149,7 @@ def create_key(self,polynomial,names): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(x - y^2) # indirect doctest + sage: L. = K.extension(x - y^2) # indirect doctest # optional - sage.rings.function_field TESTS: @@ -155,12 +157,12 @@ def create_key(self,polynomial,names): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: R. = L[] - sage: M. = L.extension(z - 1) + sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: R. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z - 1) # optional - sage.rings.function_field sage: R. = K[] - sage: N. = K.extension(z - 1) - sage: M is N + sage: N. = K.extension(z - 1) # optional - sage.rings.function_field + sage: M is N # optional - sage.rings.function_field False """ @@ -179,32 +181,33 @@ def create_object(self,version,key,**extra_args): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(x - y^2) # indirect doctest - sage: y2 = y*1 - sage: M. = K.extension(x - y2^2) # indirect doctest - sage: L is M + sage: L. = K.extension(x - y^2) # indirect doctest # optional - sage.rings.function_field + sage: y2 = y*1 # optional - sage.rings.function_field + sage: M. = K.extension(x - y2^2) # indirect doctest # optional - sage.rings.function_field + sage: L is M # optional - sage.rings.function_field True """ - from . import function_field + from . import function_field_polymod, function_field_rational + f = key[0] names = key[1] base_field = f.base_ring() - if isinstance(base_field, function_field.RationalFunctionField): + if isinstance(base_field, function_field_rational.RationalFunctionField): k = base_field.constant_field() if k.is_finite(): # then we are in positive characteristic # irreducible and separable if f.is_irreducible() and not all(e % k.characteristic() == 0 for e in f.exponents()): # monic and integral if f.is_monic() and all(e in base_field.maximal_order() for e in f.coefficients()): - return function_field.FunctionField_global_integral(f, names) + return function_field_polymod.FunctionField_global_integral(f, names) else: - return function_field.FunctionField_global(f, names) + return function_field_polymod.FunctionField_global(f, names) elif k.characteristic() == 0: if f.is_irreducible() and f.is_monic() and all(e in base_field.maximal_order() for e in f.coefficients()): - return function_field.FunctionField_char_zero_integral(f, names) + return function_field_polymod.FunctionField_char_zero_integral(f, names) else: - return function_field.FunctionField_char_zero(f, names) - return function_field.FunctionField_polymod(f, names) + return function_field_polymod.FunctionField_char_zero(f, names) + return function_field_polymod.FunctionField_polymod(f, names) -FunctionFieldExtension=FunctionFieldExtensionFactory( +FunctionFieldExtension = FunctionFieldExtensionFactory( "sage.rings.function_field.constructor.FunctionFieldExtension") diff --git a/src/sage/rings/function_field/derivations.py b/src/sage/rings/function_field/derivations.py new file mode 100644 index 00000000000..e849a4e0016 --- /dev/null +++ b/src/sage/rings/function_field/derivations.py @@ -0,0 +1,101 @@ +r""" +Derivations of function fields + +For global function fields, which have positive characteristics, the higher +derivation is available:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: h = L.higher_derivation() # optional - sage.rings.finite_rings sage.rings.function_field + sage: h(y^2, 2) # optional - sage.rings.finite_rings sage.rings.function_field + ((x^7 + 1)/x^2)*y^2 + x^3*y + +AUTHORS: + +- William Stein (2010): initial version + +- Julian Rüth (2011-09-14, 2014-06-23, 2017-08-21): refactored class hierarchy; added + derivation classes; morphisms to/from fraction fields + +- Kwankyu Lee (2017-04-30): added higher derivations and completions + +""" +# **************************************************************************** +# Copyright (C) 2010 William Stein +# 2011-2017 Julian Rüth +# 2017 Alyson Deines +# 2017-2019 Kwankyu Lee +# 2018-2019 Travis Scrimshaw +# 2019 Brent Baccala +# 2022 Xavier Caruso +# 2022 Frédéric Chapoton +# +# 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. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.rings.derivation import RingDerivationWithoutTwist + + +class FunctionFieldDerivation(RingDerivationWithoutTwist): + r""" + Base class for derivations on function fields. + + A derivation on `R` is a map `R \to R` with + `D(\alpha+\beta)=D(\alpha)+D(\beta)` and `D(\alpha\beta)=\beta + D(\alpha)+\alpha D(\beta)` for all `\alpha,\beta\in R`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d + d/dx + """ + def __init__(self, parent): + r""" + Initialize a derivation. + + INPUT: + + - ``parent`` -- the differential module in which this + derivation lives + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: TestSuite(d).run() + """ + RingDerivationWithoutTwist.__init__(self, parent) + self.__field = parent.domain() + + def is_injective(self) -> bool: + r""" + Return ``False`` since a derivation is never injective. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d.is_injective() + False + """ + return False + + def _rmul_(self, factor): + """ + Return the product of this derivation by the scalar ``factor``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d + d/dx + sage: x * d + x*d/dx + """ + return self._lmul_(factor) diff --git a/src/sage/rings/function_field/derivations_polymod.py b/src/sage/rings/function_field/derivations_polymod.py new file mode 100644 index 00000000000..cc23d4c2461 --- /dev/null +++ b/src/sage/rings/function_field/derivations_polymod.py @@ -0,0 +1,911 @@ +r""" +Derivations of function fields: extension +""" + +# **************************************************************************** +# Copyright (C) 2010 William Stein +# 2011-2017 Julian Rüth +# 2017 Alyson Deines +# 2017-2019 Kwankyu Lee +# 2018-2019 Travis Scrimshaw +# 2019 Brent Baccala +# 2022 Xavier Caruso +# 2022 Frédéric Chapoton +# +# 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. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from sage.arith.misc import binomial +from sage.categories.homset import Hom +from sage.categories.map import Map +from sage.categories.sets_cat import Sets +from sage.rings.derivation import RingDerivationWithoutTwist + +from .derivations import FunctionFieldDerivation + + +class FunctionFieldDerivation_separable(FunctionFieldDerivation): + """ + Derivations of separable extensions. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: L.derivation() + d/dx + """ + def __init__(self, parent, d): + """ + Initialize a derivation. + + INPUT: + + - ``parent`` -- the parent of this derivation + + - ``d`` -- a variable name or a derivation over + the base field (or something capable to create + such a derivation) + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() + sage: TestSuite(d).run() + + sage: L.derivation(y) # d/dy + 2*y*d/dx + + sage: dK = K.derivation([x]); dK + x*d/dx + sage: L.derivation(dK) + x*d/dx + """ + FunctionFieldDerivation.__init__(self, parent) + L = parent.domain() + C = parent.codomain() + dm = parent._defining_morphism + u = L.gen() + if d == L.gen(): + d = parent._base_derivation(None) + f = L.polynomial().change_ring(L) + coeff = -f.derivative()(u) / f.map_coefficients(d)(u) + if dm is not None: + coeff = dm(coeff) + self._d = parent._base_derivation([coeff]) + self._gen_image = C.one() + else: + if isinstance(d, RingDerivationWithoutTwist) and d.domain() is L.base_ring(): + self._d = d + else: + self._d = d = parent._base_derivation(d) + f = L.polynomial() + if dm is None: + denom = f.derivative()(u) + else: + u = dm(u) + denom = f.derivative().map_coefficients(dm, new_base_ring=C)(u) + num = f.map_coefficients(d, new_base_ring=C)(u) + self._gen_image = -num / denom + + def _call_(self, x): + r""" + Evaluate the derivation on ``x``. + + INPUT: + + - ``x`` -- element of the function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() + sage: d(x) # indirect doctest + 1 + sage: d(y) + 1/2/x*y + sage: d(y^2) + 1 + """ + parent = self.parent() + if x.is_zero(): + return parent.codomain().zero() + x = x._x + y = parent.domain().gen() + dm = parent._defining_morphism + tmp1 = x.map_coefficients(self._d, new_base_ring=parent.codomain()) + tmp2 = x.derivative()(y) + if dm is not None: + tmp2 = dm(tmp2) + y = dm(y) + return tmp1(y) + tmp2 * self._gen_image + + def _add_(self, other): + """ + Return the sum of this derivation and ``other``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() + sage: d + d/dx + sage: d + d + 2*d/dx + """ + return type(self)(self.parent(), self._d + other._d) + + def _lmul_(self, factor): + """ + Return the product of this derivation by the scalar ``factor``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: d = L.derivation() + sage: d + d/dx + sage: y * d + y*d/dx + """ + return type(self)(self.parent(), factor * self._d) + + +class FunctionFieldDerivation_inseparable(FunctionFieldDerivation): + def __init__(self, parent, u=None): + r""" + Initialize this derivation. + + INPUT: + + - ``parent`` -- the parent of this derivation + + - ``u`` -- a parameter describing the derivation + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: d = L.derivation() # optional - sage.rings.finite_rings + + This also works for iterated non-monic extensions:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - 1/x) # optional - sage.rings.finite_rings + sage: R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^2*y - x^3) # optional - sage.rings.finite_rings + sage: M.derivation() # optional - sage.rings.finite_rings + d/dz + + We can also create a multiple of the canonical derivation:: + + sage: M.derivation([x]) # optional - sage.rings.finite_rings + x*d/dz + """ + FunctionFieldDerivation.__init__(self, parent) + if u is None: + self._u = parent.codomain().one() + elif u == 0 or isinstance(u, (list, tuple)): + if u == 0 or len(u) == 0: + self._u = parent.codomain().zero() + elif len(u) == 1: + self._u = parent.codomain()(u[0]) + else: + raise ValueError("the length does not match") + else: + raise ValueError("you must pass in either a name of a variable or a list of coefficients") + + def _call_(self, x): + r""" + Evaluate the derivation on ``x``. + + INPUT: + + - ``x`` -- an element of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: d = L.derivation() # optional - sage.rings.finite_rings + sage: d(x) # indirect doctest # optional - sage.rings.finite_rings + 0 + sage: d(y) # optional - sage.rings.finite_rings + 1 + sage: d(y^2) # optional - sage.rings.finite_rings + 0 + + """ + if x.is_zero(): + return self.codomain().zero() + parent = self.parent() + return self._u * parent._d(parent._t(x)) + + def _add_(self, other): + """ + Return the sum of this derivation and ``other``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 - x) # optional - sage.rings.finite_rings + sage: d = L.derivation() # optional - sage.rings.finite_rings + sage: d # optional - sage.rings.finite_rings + d/dy + sage: d + d # optional - sage.rings.finite_rings + 2*d/dy + """ + return type(self)(self.parent(), [self._u + other._u]) + + def _lmul_(self, factor): + """ + Return the product of this derivation by the scalar ``factor``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: d = L.derivation() # optional - sage.rings.finite_rings + sage: d # optional - sage.rings.finite_rings + d/dy + sage: y * d # optional - sage.rings.finite_rings + y*d/dy + """ + return type(self)(self.parent(), [factor * self._u]) + + +class FunctionFieldHigherDerivation(Map): + """ + Base class of higher derivations on function fields. + + INPUT: + + - ``field`` -- function field on which the derivation operates + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: F.higher_derivation() # optional - sage.rings.finite_rings + Higher derivation map: + From: Rational function field in x over Finite Field of size 2 + To: Rational function field in x over Finite Field of size 2 + """ + def __init__(self, field): + """ + Initialize. + + TESTS:: + + sage: F. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: TestSuite(h).run(skip='_test_category') # optional - sage.rings.finite_rings + """ + Map.__init__(self, Hom(field, field, Sets())) + self._field = field + # elements of a prime finite field do not have pth_root method + if field.constant_base_field().is_prime_field(): + self._pth_root_func = _pth_root_in_prime_field + else: + self._pth_root_func = _pth_root_in_finite_field + + def _repr_type(self) -> str: + """ + Return a string containing the type of the map. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: h # indirect doctest # optional - sage.rings.finite_rings + Higher derivation map: + From: Rational function field in x over Finite Field of size 2 + To: Rational function field in x over Finite Field of size 2 + """ + return 'Higher derivation' + + def __eq__(self, other) -> bool: + """ + Test if ``self`` equals ``other``. + + TESTS:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: loads(dumps(h)) == h # optional - sage.rings.finite_rings + True + """ + if isinstance(other, FunctionFieldHigherDerivation): + return self._field == other._field + return False + + +def _pth_root_in_prime_field(e): + """ + Return the `p`-th root of element ``e`` in a prime finite field. + + TESTS:: + + sage: from sage.rings.function_field.derivations_polymod import _pth_root_in_prime_field + sage: p = 5 + sage: F. = GF(p) # optional - sage.rings.finite_rings + sage: e = F.random_element() # optional - sage.rings.finite_rings + sage: _pth_root_in_prime_field(e)^p == e # optional - sage.rings.finite_rings + True + """ + return e + + +def _pth_root_in_finite_field(e): + """ + Return the `p`-th root of element ``e`` in a finite field. + + TESTS:: + + sage: from sage.rings.function_field.derivations_polymod import _pth_root_in_finite_field + sage: p = 3 + sage: F. = GF(p^2) # optional - sage.rings.finite_rings + sage: e = F.random_element() # optional - sage.rings.finite_rings + sage: _pth_root_in_finite_field(e)^p == e # optional - sage.rings.finite_rings + True + """ + return e.pth_root() + + +class RationalFunctionFieldHigherDerivation_global(FunctionFieldHigherDerivation): + """ + Higher derivations of rational function fields over finite fields. + + INPUT: + + - ``field`` -- function field on which the derivation operates + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: h # optional - sage.rings.finite_rings + Higher derivation map: + From: Rational function field in x over Finite Field of size 2 + To: Rational function field in x over Finite Field of size 2 + sage: h(x^2, 2) # optional - sage.rings.finite_rings + 1 + """ + def __init__(self, field): + """ + Initialize. + + TESTS:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: TestSuite(h).run(skip='_test_category') # optional - sage.rings.finite_rings + """ + FunctionFieldHigherDerivation.__init__(self, field) + + self._p = field.characteristic() + self._separating_element = field.gen() + + def _call_with_args(self, f, args=(), kwds={}): + """ + Call the derivation with args and kwds. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: h(x^2, 2) # indirect doctest # optional - sage.rings.finite_rings + 1 + """ + return self._derive(f, *args, **kwds) + + def _derive(self, f, i, separating_element=None): + """ + Return the `i`-th derivative of ``f`` with respect to the + separating element. + + This implements Hess' Algorithm 26 in [Hes2002b]_. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: h._derive(x^3, 0) # optional - sage.rings.finite_rings + x^3 + sage: h._derive(x^3, 1) # optional - sage.rings.finite_rings + x^2 + sage: h._derive(x^3, 2) # optional - sage.rings.finite_rings + x + sage: h._derive(x^3, 3) # optional - sage.rings.finite_rings + 1 + sage: h._derive(x^3, 4) # optional - sage.rings.finite_rings + 0 + """ + F = self._field + p = self._p + + if separating_element is None: + x = self._separating_element + + def derivative(f): + return f.derivative() + else: + x = separating_element + xderinv = ~(x.derivative()) + + def derivative(f): + return xderinv * f.derivative() + + prime_power_representation = self._prime_power_representation + + def derive(f, i): + # Step 1: zero-th derivative + if i == 0: + return f + # Step 2: + s = i % p + r = i // p + # Step 3: + e = f + while s > 0: + e = derivative(e) / F(s) + s -= 1 + # Step 4: + if r == 0: + return e + else: + # Step 5: + lambdas = prime_power_representation(e, x) + # Step 6 and 7: + der = 0 + for i in range(p): + mu = derive(lambdas[i], r) + der += mu**p * x**i + return der + + return derive(f, i) + + def _prime_power_representation(self, f, separating_element=None): + """ + Return `p`-th power representation of the element ``f``. + + Here `p` is the characteristic of the function field. + + This implements Hess' Algorithm 25. + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: h._prime_power_representation(x^2 + x + 1) # optional - sage.rings.finite_rings + [x + 1, 1] + sage: x^2 + x + 1 == _[0]^2 + _[1]^2 * x # optional - sage.rings.finite_rings + True + """ + F = self._field + p = self._p + + if separating_element is None: + x = self._separating_element + + def derivative(f): + return f.derivative() + else: + x = separating_element + xderinv = ~(x.derivative()) + + def derivative(f): + return xderinv * f.derivative() + + # Step 1: + a = [f] + aprev = f + j = 1 + while j < p: + aprev = derivative(aprev) / F(j) + a.append(aprev) + j += 1 + # Step 2: + b = a + j = p - 2 + while j >= 0: + b[j] -= sum(binomial(i, j) * b[i] * x**(i - j) + for i in range(j + 1, p)) + j -= 1 + # Step 3 + return [self._pth_root(c) for c in b] + + def _pth_root(self, c): + """ + Return the `p`-th root of the rational function ``c``. + + INPUT: + + - ``c`` -- rational function + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: h = F.higher_derivation() # optional - sage.rings.finite_rings + sage: h._pth_root((x^2+1)^2) # optional - sage.rings.finite_rings + x^2 + 1 + """ + K = self._field + p = self._p + + R = K._field.ring() + + poly = c.numerator() + num = R([self._pth_root_func(poly[i]) + for i in range(0, poly.degree() + 1, p)]) + poly = c.denominator() + den = R([self._pth_root_func(poly[i]) + for i in range(0, poly.degree() + 1, p)]) + return K.element_class(K, num / den) + + +class FunctionFieldHigherDerivation_global(FunctionFieldHigherDerivation): + """ + Higher derivations of global function fields. + + INPUT: + + - ``field`` -- function field on which the derivation operates + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings + sage: h = L.higher_derivation() # optional - sage.rings.finite_rings + sage: h # optional - sage.rings.finite_rings + Higher derivation map: + From: Function field in y defined by y^3 + x^3*y + x + To: Function field in y defined by y^3 + x^3*y + x + sage: h(y^2, 2) # optional - sage.rings.finite_rings + ((x^7 + 1)/x^2)*y^2 + x^3*y + """ + + def __init__(self, field): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings + sage: h = L.higher_derivation() # optional - sage.rings.finite_rings + sage: TestSuite(h).run(skip=['_test_category']) # optional - sage.rings.finite_rings + """ + from sage.matrix.constructor import matrix + + FunctionFieldHigherDerivation.__init__(self, field) + + self._p = field.characteristic() + self._separating_element = field(field.base_field().gen()) + + p = field.characteristic() + y = field.gen() + + # matrix for pth power map; used in _prime_power_representation method + self.__pth_root_matrix = matrix([(y**(i * p)).list() + for i in range(field.degree())]).transpose() + + # cache computed higher derivatives to speed up later computations + self._cache = {} + + def _call_with_args(self, f, args, kwds): + """ + Call the derivation with ``args`` and ``kwds``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings + sage: h = L.higher_derivation() # optional - sage.rings.finite_rings + sage: h(y^2, 2) # indirect doctest # optional - sage.rings.finite_rings + ((x^7 + 1)/x^2)*y^2 + x^3*y + """ + return self._derive(f, *args, **kwds) + + def _derive(self, f, i, separating_element=None): + """ + Return ``i``-th derivative of ``f`` with respect to the separating + element. + + This implements Hess' Algorithm 26 in [Hes2002b]_. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings + sage: h = L.higher_derivation() # optional - sage.rings.finite_rings + sage: y^3 # optional - sage.rings.finite_rings + x^3*y + x + sage: h._derive(y^3, 0) # optional - sage.rings.finite_rings + x^3*y + x + sage: h._derive(y^3, 1) # optional - sage.rings.finite_rings + x^4*y^2 + 1 + sage: h._derive(y^3, 2) # optional - sage.rings.finite_rings + x^10*y^2 + (x^8 + x)*y + sage: h._derive(y^3, 3) # optional - sage.rings.finite_rings + (x^9 + x^2)*y^2 + x^7*y + sage: h._derive(y^3, 4) # optional - sage.rings.finite_rings + (x^22 + x)*y^2 + ((x^21 + x^14 + x^7 + 1)/x)*y + """ + F = self._field + p = self._p + frob = F.frobenius_endomorphism() # p-th power map + + if separating_element is None: + x = self._separating_element + + def derivative(f): + return f.derivative() + else: + x = separating_element + xderinv = ~(x.derivative()) + + def derivative(f): + return xderinv * f.derivative() + + try: + cache = self._cache[separating_element] + except KeyError: + cache = self._cache[separating_element] = {} + + def derive(f, i): + # Step 1: zero-th derivative + if i == 0: + return f + + # Step 1.5: use cached result if available + try: + return cache[f, i] + except KeyError: + pass + + # Step 2: + s = i % p + r = i // p + # Step 3: + e = f + while s > 0: + e = derivative(e) / F(s) + s -= 1 + # Step 4: + if r == 0: + der = e + else: + # Step 5: inlined self._prime_power_representation + a = [e] + aprev = e + j = 1 + while j < p: + aprev = derivative(aprev) / F(j) + a.append(aprev) + j += 1 + b = a + j = p - 2 + while j >= 0: + b[j] -= sum(binomial(k, j) * b[k] * x**(k - j) + for k in range(j + 1, p)) + j -= 1 + lambdas = [self._pth_root(c) for c in b] + + # Step 6 and 7: + der = 0 + xpow = 1 + for k in range(p): + mu = derive(lambdas[k], r) + der += frob(mu) * xpow + xpow *= x + + cache[f, i] = der + return der + + return derive(f, i) + + def _prime_power_representation(self, f, separating_element=None): + """ + Return `p`-th power representation of the element ``f``. + + Here `p` is the characteristic of the function field. + + This implements Hess' Algorithm 25 in [Hes2002b]_. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings + sage: h = L.higher_derivation() # optional - sage.rings.finite_rings + sage: b = h._prime_power_representation(y) # optional - sage.rings.finite_rings + sage: y == b[0]^2 + b[1]^2 * x # optional - sage.rings.finite_rings + True + """ + F = self._field + p = self._p + + if separating_element is None: + x = self._separating_element + + def derivative(f): + return f.derivative() + else: + x = separating_element + xderinv = ~(x.derivative()) + + def derivative(f): + return xderinv * f.derivative() + + # Step 1: + a = [f] + aprev = f + j = 1 + while j < p: + aprev = derivative(aprev) / F(j) + a.append(aprev) + j += 1 + # Step 2: + b = a + j = p - 2 + while j >= 0: + b[j] -= sum(binomial(i, j) * b[i] * x**(i - j) + for i in range(j + 1, p)) + j -= 1 + # Step 3 + return [self._pth_root(c) for c in b] + + def _pth_root(self, c): + """ + Return the `p`-th root of function field element ``c``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings + sage: h = L.higher_derivation() # optional - sage.rings.finite_rings + sage: h._pth_root((x^2 + y^2)^2) # optional - sage.rings.finite_rings + y^2 + x^2 + """ + from sage.modules.free_module_element import vector + + K = self._field.base_field() # rational function field + p = self._p + + coeffs = [] + for d in self.__pth_root_matrix.solve_right(vector(c.list())): + poly = d.numerator() + num = K([self._pth_root_func(poly[i]) + for i in range(0, poly.degree() + 1, p)]) + poly = d.denominator() + den = K([self._pth_root_func(poly[i]) + for i in range(0, poly.degree() + 1, p)]) + coeffs.append(num / den) + return self._field(coeffs) + + +class FunctionFieldHigherDerivation_char_zero(FunctionFieldHigherDerivation): + """ + Higher derivations of function fields of characteristic zero. + + INPUT: + + - ``field`` -- function field on which the derivation operates + + EXAMPLES:: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: h + Higher derivation map: + From: Function field in y defined by y^3 + x^3*y + x + To: Function field in y defined by y^3 + x^3*y + x + sage: h(y,1) == -(3*x^2*y+1)/(3*y^2+x^3) + True + sage: h(y^2,1) == -2*y*(3*x^2*y+1)/(3*y^2+x^3) + True + sage: e = L.random_element() + sage: h(h(e,1),1) == 2*h(e,2) + True + sage: h(h(h(e,1),1),1) == 3*2*h(e,3) + True + """ + def __init__(self, field): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: TestSuite(h).run(skip=['_test_category']) + """ + FunctionFieldHigherDerivation.__init__(self, field) + + self._separating_element = field(field.base_field().gen()) + + # cache computed higher derivatives to speed up later computations + self._cache = {} + + def _call_with_args(self, f, args, kwds): + """ + Call the derivation with ``args`` and ``kwds``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: e = L.random_element() + sage: h(h(e,1),1) == 2*h(e,2) # indirect doctest + True + """ + return self._derive(f, *args, **kwds) + + def _derive(self, f, i, separating_element=None): + """ + Return ``i``-th derivative of ``f`` with respect to the separating + element. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^3 + x + x^3*Y) + sage: h = L.higher_derivation() + sage: y^3 + -x^3*y - x + sage: h._derive(y^3, 0) + -x^3*y - x + sage: h._derive(y^3, 1) + (-21/4*x^4/(x^7 + 27/4))*y^2 + ((-9/2*x^9 - 45/2*x^2)/(x^7 + 27/4))*y + (-9/2*x^7 - 27/4)/(x^7 + 27/4) + """ + F = self._field + + if separating_element is None: + x = self._separating_element + xderinv = 1 + else: + x = separating_element + xderinv = ~(x.derivative()) + + try: + cache = self._cache[separating_element] + except KeyError: + cache = self._cache[separating_element] = {} + + if i == 0: + return f + + try: + return cache[f, i] + except KeyError: + pass + + s = i + e = f + while s > 0: + e = xderinv * e.derivative() / F(s) + s -= 1 + + der = e + + cache[f, i] = der + return der diff --git a/src/sage/rings/function_field/derivations_rational.py b/src/sage/rings/function_field/derivations_rational.py new file mode 100644 index 00000000000..777f16d3e08 --- /dev/null +++ b/src/sage/rings/function_field/derivations_rational.py @@ -0,0 +1,131 @@ +r""" +Derivations of function fields: rational +""" + +# **************************************************************************** +# Copyright (C) 2010 William Stein +# 2011-2017 Julian Rüth +# 2017 Alyson Deines +# 2017-2019 Kwankyu Lee +# 2018-2019 Travis Scrimshaw +# 2019 Brent Baccala +# 2022 Xavier Caruso +# 2022 Frédéric Chapoton +# +# 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. +# https://www.gnu.org/licenses/ +# **************************************************************************** + +from .derivations import FunctionFieldDerivation + + +class FunctionFieldDerivation_rational(FunctionFieldDerivation): + """ + Derivations on rational function fields. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.derivation() + d/dx + """ + def __init__(self, parent, u=None): + """ + Initialize a derivation. + + INPUT: + + - ``parent`` -- the parent of this derivation + + - ``u`` -- a parameter describing the derivation + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: TestSuite(d).run() + + The parameter ``u`` can be the name of the variable:: + + sage: K.derivation(x) + d/dx + + or a list of length one whose unique element is the image + of the generator of the underlying function field:: + + sage: K.derivation([x^2]) + x^2*d/dx + """ + FunctionFieldDerivation.__init__(self, parent) + if u is None or u == parent.domain().gen(): + self._u = parent.codomain().one() + elif u == 0 or isinstance(u, (list, tuple)): + if u == 0 or len(u) == 0: + self._u = parent.codomain().zero() + elif len(u) == 1: + self._u = parent.codomain()(u[0]) + else: + raise ValueError("the length does not match") + else: + raise ValueError("you must pass in either a name of a variable or a list of coefficients") + + def _call_(self, x): + """ + Compute the derivation of ``x``. + + INPUT: + + - ``x`` -- element of the rational function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d(x) # indirect doctest + 1 + sage: d(x^3) + 3*x^2 + sage: d(1/x) + -1/x^2 + """ + f = x.numerator() + g = x.denominator() + numerator = f.derivative() * g - f * g.derivative() + if numerator.is_zero(): + return self.codomain().zero() + else: + v = numerator / g**2 + defining_morphism = self.parent()._defining_morphism + if defining_morphism is not None: + v = defining_morphism(v) + return self._u * v + + def _add_(self, other): + """ + Return the sum of this derivation and ``other``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d + d + 2*d/dx + """ + return type(self)(self.parent(), [self._u + other._u]) + + def _lmul_(self, factor): + """ + Return the product of this derivation by the scalar ``factor``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: d = K.derivation() + sage: d + d/dx + sage: x * d + x*d/dx + """ + return type(self)(self.parent(), [factor * self._u]) diff --git a/src/sage/rings/function_field/differential.py b/src/sage/rings/function_field/differential.py index e7d5f5a5c68..24ea2371040 100644 --- a/src/sage/rings/function_field/differential.py +++ b/src/sage/rings/function_field/differential.py @@ -1,3 +1,4 @@ +# sage.doctest: optional - sage.modules """ Differentials of function fields @@ -8,35 +9,35 @@ The module of differentials on a function field forms an one-dimensional vector space over the function field:: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: f = x + y - sage: g = 1 / y - sage: df = f.differential() - sage: dg = g.differential() - sage: dfdg = f.derivative() / g.derivative() - sage: df == dfdg * dg + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = x + y # optional - sage.rings.finite_rings sage.rings.function_field + sage: g = 1 / y # optional - sage.rings.finite_rings sage.rings.function_field + sage: df = f.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: dg = g.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: dfdg = f.derivative() / g.derivative() # optional - sage.rings.finite_rings sage.rings.function_field + sage: df == dfdg * dg # optional - sage.rings.finite_rings sage.rings.function_field True - sage: df + sage: df # optional - sage.rings.finite_rings sage.rings.function_field (x*y^2 + 1/x*y + 1) d(x) - sage: df.parent() + sage: df.parent() # optional - sage.rings.finite_rings sage.rings.function_field Space of differentials of Function field in y defined by y^3 + x^3*y + x We can compute a canonical divisor:: - sage: k = df.divisor() - sage: k.degree() + sage: k = df.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: k.degree() # optional - sage.rings.finite_rings sage.rings.function_field 4 - sage: k.degree() == 2 * L.genus() - 2 + sage: k.degree() == 2 * L.genus() - 2 # optional - sage.rings.finite_rings sage.rings.function_field True Exact differentials vanish and logarithmic differentials are stable under the Cartier operation:: - sage: df.cartier() + sage: df.cartier() # optional - sage.rings.finite_rings sage.rings.function_field 0 - sage: w = 1/f * df - sage: w.cartier() == w + sage: w = 1/f * df # optional - sage.rings.finite_rings sage.rings.function_field + sage: w.cartier() == w # optional - sage.rings.finite_rings sage.rings.function_field True AUTHORS: @@ -45,7 +46,9 @@ """ #***************************************************************************** -# Copyright (C) 2016 Kwankyu Lee +# Copyright (C) 2016-2019 Kwankyu Lee +# 2019 Brent Baccala +# 2019 Travis Scrimshaw # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -78,7 +81,7 @@ class FunctionFieldDifferential(ModuleElement): EXAMPLES:: - sage: F.=FunctionField(QQ) + sage: F. = FunctionField(QQ) sage: f = x/(x^2 + x + 1) sage: f.differential() ((-x^2 + 1)/(x^4 + 2*x^3 + 3*x^2 + 2*x + 1)) d(x) @@ -86,10 +89,10 @@ class FunctionFieldDifferential(ModuleElement): :: sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: L(x).differential() + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.function_field + sage: L(x).differential() # optional - sage.rings.function_field d(x) - sage: y.differential() + sage: y.differential() # optional - sage.rings.function_field ((21/4*x/(x^7 + 27/4))*y^2 + ((3/2*x^7 + 9/4)/(x^8 + 27/4*x))*y + 7/2*x^4/(x^7 + 27/4)) d(x) """ def __init__(self, parent, f, t=None): @@ -98,10 +101,10 @@ def __init__(self, parent, f, t=None): TESTS:: - sage: F. = FunctionField(GF(7)) - sage: f = x/(x^2 + x + 1) - sage: w = f.differential() - sage: TestSuite(w).run() + sage: F. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: f = x/(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: w = f.differential() # optional - sage.rings.finite_rings + sage: TestSuite(w).run() # optional - sage.rings.finite_rings """ ModuleElement.__init__(self, parent) @@ -116,12 +119,12 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: y.differential() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3+x+x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: y.differential() # optional - sage.rings.finite_rings sage.rings.function_field (x*y^2 + 1/x*y) d(x) - sage: F.=FunctionField(QQ) + sage: F. = FunctionField(QQ) sage: f = 1/x sage: f.differential() (-1/x^2) d(x) @@ -142,10 +145,10 @@ def _latex_(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: w = y.differential() - sage: latex(w) + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings + sage: w = y.differential() # optional - sage.rings.finite_rings + sage: latex(w) # optional - sage.rings.finite_rings \left( x y^{2} + \frac{1}{x} y \right)\, dx """ if self._f.is_zero(): # zero differential @@ -164,11 +167,11 @@ def __hash__(self): EXAMPLES:: - sage: K.=FunctionField(GF(2)); _. = K[] - sage: L.=K.extension(Y^3 + x + x^3*Y) - sage: {x.differential(): 1} + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: {x.differential(): 1} # optional - sage.rings.finite_rings sage.rings.function_field {d(x): 1} - sage: {y.differential(): 1} + sage: {y.differential(): 1} # optional - sage.rings.finite_rings sage.rings.function_field {(x*y^2 + 1/x*y) d(x): 1} """ return hash((self.parent(), self._f)) @@ -186,19 +189,19 @@ def _richcmp_(self, other, op): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: w1 = y.differential() - sage: w2 = L(x).differential() - sage: w3 = (x*y).differential() - sage: w1 < w2 + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w2 = L(x).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w3 = (x*y).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 < w2 # optional - sage.rings.finite_rings sage.rings.function_field False - sage: w2 < w1 + sage: w2 < w1 # optional - sage.rings.finite_rings sage.rings.function_field True - sage: w3 == x * w1 + y * w2 + sage: w3 == x * w1 + y * w2 # optional - sage.rings.finite_rings sage.rings.function_field True - sage: F.=FunctionField(QQ) + sage: F. = FunctionField(QQ) sage: w1 = ((x^2+x+1)^10).differential() sage: w2 = (x^2+x+1).differential() sage: w1 < w2 @@ -220,11 +223,11 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: w1 = y.differential() - sage: w2 = (1/y).differential() - sage: w1 + w2 + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w2 = (1/y).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 + w2 # optional - sage.rings.finite_rings sage.rings.function_field (((x^3 + 1)/x^2)*y^2 + 1/x*y) d(x) sage: F. = FunctionField(QQ) @@ -248,11 +251,11 @@ def _div_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: w1 = y.differential() - sage: w2 = (1/y).differential() - sage: w1 / w2 + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w2 = (1/y).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 / w2 # optional - sage.rings.finite_rings sage.rings.function_field y^2 sage: F. = FunctionField(QQ) @@ -272,11 +275,11 @@ def _neg_(self): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: w1 = y.differential() - sage: w2 = (-y).differential() - sage: -w1 == w2 + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w2 = (-y).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: -w1 == w2 # optional - sage.rings.finite_rings sage.rings.function_field True sage: F. = FunctionField(QQ) @@ -299,14 +302,14 @@ def _rmul_(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: w1 = (1/y).differential() - sage: w2 = (-1/y^2) * y.differential() - sage: w1 == w2 + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 = (1/y).differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w2 = (-1/y^2) * y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w1 == w2 # optional - sage.rings.finite_rings sage.rings.function_field True - sage: F.=FunctionField(QQ) + sage: F. = FunctionField(QQ) sage: w1 = (x^2*(x^2+x+1)).differential() sage: w2 = (x^2).differential() sage: w3 = (x^2+x+1).differential() @@ -328,26 +331,26 @@ def _acted_upon_(self, f, self_on_left): EXAMPLES:: - sage: K. = FunctionField(GF(31)); _. = K[] - sage: L. = K.extension(Y^2 - x); _. = L[] - sage: M. = L.extension(Z^2 - y) - sage: z.differential() + sage: K. = FunctionField(GF(31)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x); _. = L[] # optional - sage.rings.finite_rings sage.rings.function_field + sage: M. = L.extension(Z^2 - y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: z.differential() # optional - sage.rings.finite_rings sage.rings.function_field (8/x*z) d(x) - sage: 1/(2*z) * y.differential() + sage: 1/(2*z) * y.differential() # optional - sage.rings.finite_rings sage.rings.function_field (8/x*z) d(x) - sage: z * x.differential() + sage: z * x.differential() # optional - sage.rings.finite_rings sage.rings.function_field (z) d(x) - sage: z * (y^2).differential() + sage: z * (y^2).differential() # optional - sage.rings.finite_rings sage.rings.function_field (z) d(x) - sage: z * (z^4).differential() + sage: z * (z^4).differential() # optional - sage.rings.finite_rings sage.rings.function_field (z) d(x) :: - sage: K.=FunctionField(GF(4)); _. = K[] - sage: L.=K.extension(Y^3 + x + x^3*Y) - sage: y * x.differential() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: y * x.differential() # optional - sage.rings.finite_rings sage.rings.function_field (y) d(x) """ F = f.parent() @@ -363,10 +366,10 @@ def divisor(self): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: w = (1/y) * y.differential() - sage: w.divisor() + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = (1/y) * y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w.divisor() # optional - sage.rings.finite_rings sage.rings.function_field - 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) @@ -394,10 +397,10 @@ def valuation(self, place): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: w = (1/y) * y.differential() - sage: [w.valuation(p) for p in L.places()] + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = (1/y) * y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: [w.valuation(p) for p in L.places()] # optional - sage.rings.finite_rings sage.rings.function_field [-1, -1, -1, 0, 1, 0] """ F = self.parent().function_field() @@ -421,39 +424,39 @@ def residue(self, place): We verify the residue theorem in a rational function field:: - sage: F. = FunctionField(GF(4)) - sage: f = 0 - sage: while f == 0: + sage: F. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: f = 0 # optional - sage.rings.finite_rings + sage: while f == 0: # optional - sage.rings.finite_rings ....: f = F.random_element() - sage: w = 1/f * f.differential() - sage: d = f.divisor() - sage: s = d.support() - sage: sum([w.residue(p).trace() for p in s]) + sage: w = 1/f * f.differential() # optional - sage.rings.finite_rings + sage: d = f.divisor() # optional - sage.rings.finite_rings + sage: s = d.support() # optional - sage.rings.finite_rings + sage: sum([w.residue(p).trace() for p in s]) # optional - sage.rings.finite_rings 0 and in an extension field:: - sage: K. = FunctionField(GF(7)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: f = 0 - sage: while f == 0: + sage: K. = FunctionField(GF(7)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = 0 # optional - sage.rings.finite_rings sage.rings.function_field + sage: while f == 0: # optional - sage.rings.finite_rings sage.rings.function_field ....: f = L.random_element() - sage: w = 1/f * f.differential() - sage: d = f.divisor() - sage: s = d.support() - sage: sum([w.residue(p).trace() for p in s]) + sage: w = 1/f * f.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: d = f.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: s = d.support() # optional - sage.rings.finite_rings sage.rings.function_field + sage: sum([w.residue(p).trace() for p in s]) # optional - sage.rings.finite_rings sage.rings.function_field 0 and also in a function field of characteristic zero:: sage: R. = FunctionField(QQ) sage: L. = R[] - sage: F. = R.extension(Y^2 - x^4 - 4*x^3 - 2*x^2 - 1) - sage: a = 6*x^2 + 5*x + 7 - sage: b = 2*x^6 + 8*x^5 + 3*x^4 - 4*x^3 -1 - sage: w = y*a/b*x.differential() - sage: d = w.divisor() - sage: sum([QQ(w.residue(p)) for p in d.support()]) + sage: F. = R.extension(Y^2 - x^4 - 4*x^3 - 2*x^2 - 1) # optional - sage.rings.function_field + sage: a = 6*x^2 + 5*x + 7 # optional - sage.rings.function_field + sage: b = 2*x^6 + 8*x^5 + 3*x^4 - 4*x^3 - 1 # optional - sage.rings.function_field + sage: w = y*a/b*x.differential() # optional - sage.rings.function_field + sage: d = w.divisor() # optional - sage.rings.function_field + sage: sum([QQ(w.residue(p)) for p in d.support()]) # optional - sage.rings.function_field 0 """ @@ -481,12 +484,12 @@ def monomial_coefficients(self, copy=True): EXAMPLES:: - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: d = y.differential() - sage: d + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: d = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: d # optional - sage.rings.finite_rings sage.rings.function_field ((4*x/(x^7 + 3))*y^2 + ((4*x^7 + 1)/(x^8 + 3*x))*y + x^4/(x^7 + 3)) d(x) - sage: d.monomial_coefficients() + sage: d.monomial_coefficients() # optional - sage.rings.finite_rings sage.rings.function_field {0: (4*x/(x^7 + 3))*y^2 + ((4*x^7 + 1)/(x^8 + 3*x))*y + x^4/(x^7 + 3)} """ return {0: self._f} @@ -498,16 +501,16 @@ class FunctionFieldDifferential_global(FunctionFieldDifferential): EXAMPLES:: - sage: F.=FunctionField(GF(7)) - sage: f = x/(x^2 + x + 1) - sage: f.differential() + sage: F. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: f = x/(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: f.differential() # optional - sage.rings.finite_rings ((6*x^2 + 1)/(x^4 + 2*x^3 + 3*x^2 + 2*x + 1)) d(x) :: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: y.differential() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: y.differential() # optional - sage.rings.finite_rings sage.rings.function_field (x*y^2 + 1/x*y) d(x) """ def cartier(self): @@ -527,19 +530,19 @@ def cartier(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: f = x/y - sage: w = 1/f*f.differential() - sage: w.cartier() == w + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = x/y # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = 1/f*f.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w.cartier() == w # optional - sage.rings.finite_rings sage.rings.function_field True :: - sage: F. = FunctionField(GF(4)) - sage: f = x/(x^2 + x + 1) - sage: w = 1/f*f.differential() - sage: w.cartier() == w + sage: F. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: f = x/(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: w = 1/f*f.differential() # optional - sage.rings.finite_rings + sage: w.cartier() == w # optional - sage.rings.finite_rings True """ W = self.parent() @@ -559,9 +562,9 @@ class DifferentialsSpace(UniqueRepresentation, Parent): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: L.space_of_differentials() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field Space of differentials of Function field in y defined by y^3 + x^3*y + x The space of differentials is a one-dimensional module over the function @@ -571,14 +574,14 @@ class DifferentialsSpace(UniqueRepresentation, Parent): element is automatically found and used to generate the base differential relative to which other differentials are denoted:: - sage: K. = FunctionField(GF(5)) - sage: R. = K[] - sage: L. = K.extension(y^5 - 1/x) - sage: L(x).differential() + sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^5 - 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L(x).differential() # optional - sage.rings.finite_rings sage.rings.function_field 0 - sage: y.differential() + sage: y.differential() # optional - sage.rings.finite_rings sage.rings.function_field d(y) - sage: (y^2).differential() + sage: (y^2).differential() # optional - sage.rings.finite_rings sage.rings.function_field (2*y) d(y) """ Element = FunctionFieldDifferential @@ -589,10 +592,10 @@ def __init__(self, field, category=None): TESTS:: - sage: K.=FunctionField(GF(4)); _.=K[] - sage: L.=K.extension(Y^3+x+x^3*Y) - sage: W = L.space_of_differentials() - sage: TestSuite(W).run() + sage: K. = FunctionField(GF(4)); _.=K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: W = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(W).run() # optional - sage.rings.finite_rings sage.rings.function_field """ Parent.__init__(self, base=field, category=Modules(field).FiniteDimensional().WithBasis().or_subcategory(category)) @@ -615,10 +618,10 @@ def _repr_(self): EXAMPLES:: - sage: K.=FunctionField(GF(4)); _.=K[] - sage: L.=K.extension(Y^3+x+x^3*Y) - sage: w = y.differential() - sage: w.parent() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = y.differential() # optional - sage.rings.finite_rings sage.rings.function_field + sage: w.parent() # optional - sage.rings.finite_rings sage.rings.function_field Space of differentials of Function field in y defined by y^3 + x^3*y + x """ return "Space of differentials of {}".format(self.base()) @@ -633,14 +636,14 @@ def _element_constructor_(self, f): EXAMPLES:: - sage: K.=FunctionField(GF(4)); _.=K[] - sage: L.=K.extension(Y^3+x+x^3*Y) - sage: S = L.space_of_differentials() - sage: S(y) + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: S = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field + sage: S(y) # optional - sage.rings.finite_rings sage.rings.function_field (x*y^2 + 1/x*y) d(x) - sage: S(y) in S + sage: S(y) in S # optional - sage.rings.finite_rings sage.rings.function_field True - sage: S(1) + sage: S(1) # optional - sage.rings.finite_rings sage.rings.function_field 0 """ if f in self.base(): @@ -658,8 +661,8 @@ def _coerce_map_from_(self, S): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: L.space_of_differentials().coerce_map_from(K.space_of_differentials()) + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: L.space_of_differentials().coerce_map_from(K.space_of_differentials()) # optional - sage.rings.function_field Inclusion morphism: From: Space of differentials of Rational function field in x over Rational Field To: Space of differentials of Function field in y defined by y^2 - x*y + 4*x^3 @@ -675,10 +678,10 @@ def function_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: S = L.space_of_differentials() - sage: S.function_field() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: S = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field + sage: S.function_field() # optional - sage.rings.finite_rings sage.rings.function_field Function field in y defined by y^3 + x^3*y + x """ return self.base() @@ -689,10 +692,10 @@ def _an_element_(self): EXAMPLES:: - sage: K.=FunctionField(GF(4)); _.=K[] - sage: L.=K.extension(Y^3+x+x^3*Y) - sage: S = L.space_of_differentials() - sage: S.an_element() # random + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: S = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field + sage: S.an_element() # random # optional - sage.rings.finite_rings sage.rings.function_field (x*y^2 + 1/x*y) d(x) """ F = self.base() @@ -704,10 +707,10 @@ def basis(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: S = L.space_of_differentials() - sage: S.basis() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: S = L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field + sage: S.basis() # optional - sage.rings.finite_rings sage.rings.function_field Family (d(x),) """ return Family([self.element_class(self, self.base().one())]) @@ -723,9 +726,9 @@ class DifferentialsSpace_global(DifferentialsSpace): EXAMPLES:: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: L.space_of_differentials() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.space_of_differentials() # optional - sage.rings.finite_rings sage.rings.function_field Space of differentials of Function field in y defined by y^3 + x^3*y + x """ Element = FunctionFieldDifferential_global @@ -738,10 +741,10 @@ class DifferentialsSpaceInclusion(Morphism): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: OK = K.space_of_differentials() - sage: OL = L.space_of_differentials() - sage: OL.coerce_map_from(OK) + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: OK = K.space_of_differentials() # optional - sage.rings.function_field + sage: OL = L.space_of_differentials() # optional - sage.rings.function_field + sage: OL.coerce_map_from(OK) # optional - sage.rings.function_field Inclusion morphism: From: Space of differentials of Rational function field in x over Rational Field To: Space of differentials of Function field in y defined by y^2 - x*y + 4*x^3 @@ -754,10 +757,10 @@ def _repr_(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: OK = K.space_of_differentials() - sage: OL = L.space_of_differentials() - sage: OL.coerce_map_from(OK) + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: OK = K.space_of_differentials() # optional - sage.rings.function_field + sage: OL = L.space_of_differentials() # optional - sage.rings.function_field + sage: OL.coerce_map_from(OK) # optional - sage.rings.function_field Inclusion morphism: From: Space of differentials of Rational function field in x over Rational Field To: Space of differentials of Function field in y defined by y^2 - x*y + 4*x^3 @@ -774,10 +777,10 @@ def is_injective(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: OK = K.space_of_differentials() - sage: OL = L.space_of_differentials() - sage: OL.coerce_map_from(OK).is_injective() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: OK = K.space_of_differentials() # optional - sage.rings.function_field + sage: OL = L.space_of_differentials() # optional - sage.rings.function_field + sage: OL.coerce_map_from(OK).is_injective() # optional - sage.rings.function_field True """ return True @@ -789,15 +792,15 @@ def is_surjective(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: OK = K.space_of_differentials() - sage: OL = L.space_of_differentials() - sage: OL.coerce_map_from(OK).is_surjective() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: OK = K.space_of_differentials() # optional - sage.rings.function_field + sage: OL = L.space_of_differentials() # optional - sage.rings.function_field + sage: OL.coerce_map_from(OK).is_surjective() # optional - sage.rings.function_field False - sage: S. = L[] - sage: M. = L.extension(z - 1) - sage: OM = M.space_of_differentials() - sage: OM.coerce_map_from(OL).is_surjective() + sage: S. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z - 1) # optional - sage.rings.function_field + sage: OM = M.space_of_differentials() # optional - sage.rings.function_field + sage: OM.coerce_map_from(OL).is_surjective() # optional - sage.rings.function_field True """ K = self.domain().function_field() @@ -814,12 +817,12 @@ def _call_(self, v): EXAMPLES:: - sage: K. = FunctionField(QQbar); _. = K[] - sage: L. = K.extension(Y^2 - x*Y + 4*x^3) - sage: OK = K.space_of_differentials() - sage: OL = L.space_of_differentials() - sage: mor = OL.coerce_map_from(OK) - sage: mor(x.differential()).parent() + sage: K. = FunctionField(QQbar); _. = K[] # optional - sage.rings.number_field + sage: L. = K.extension(Y^2 - x*Y + 4*x^3) # optional - sage.rings.function_field sage.rings.number_field + sage: OK = K.space_of_differentials() # optional - sage.rings.function_field sage.rings.number_field + sage: OL = L.space_of_differentials() # optional - sage.rings.function_field sage.rings.number_field + sage: mor = OL.coerce_map_from(OK) # optional - sage.rings.function_field sage.rings.number_field + sage: mor(x.differential()).parent() # optional - sage.rings.function_field sage.rings.number_field Space of differentials of Function field in y defined by y^2 - x*y + 4*x^3 """ domain = self.domain() diff --git a/src/sage/rings/function_field/divisor.py b/src/sage/rings/function_field/divisor.py index 10e06c248cd..4646491c7e0 100644 --- a/src/sage/rings/function_field/divisor.py +++ b/src/sage/rings/function_field/divisor.py @@ -1,3 +1,5 @@ +# sage.doctest: optional - sage.rings.finite_rings (because all doctests use finite fields) +# sage.doctest: optional - sage.rings.function_field (because almost all doctests use function field extensions) """ Divisors of function fields @@ -38,7 +40,8 @@ """ #***************************************************************************** -# Copyright (C) 2016 Kwankyu Lee +# Copyright (C) 2016-2022 Kwankyu Lee +# 2019 Brent Baccala # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/src/sage/rings/function_field/element.pxd b/src/sage/rings/function_field/element.pxd new file mode 100644 index 00000000000..f0418634f82 --- /dev/null +++ b/src/sage/rings/function_field/element.pxd @@ -0,0 +1,10 @@ +from sage.structure.element cimport FieldElement + + +cdef class FunctionFieldElement(FieldElement): + cdef readonly object _x + cdef readonly object _matrix + + cdef FunctionFieldElement _new_c(self) + cpdef bint is_nth_power(self, n) + cpdef FunctionFieldElement nth_root(self, n) diff --git a/src/sage/rings/function_field/element.pyx b/src/sage/rings/function_field/element.pyx index 6de39e743b2..2533611cf52 100644 --- a/src/sage/rings/function_field/element.pyx +++ b/src/sage/rings/function_field/element.pyx @@ -20,16 +20,16 @@ Arithmetic with rational functions:: Derivatives of elements in separable extensions:: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: (y^3 + x).derivative() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: (y^3 + x).derivative() # optional - sage.rings.finite_rings sage.rings.function_field ((x^2 + 1)/x^2)*y + (x^4 + x^3 + 1)/x^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() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: y.divisor() # optional - sage.rings.finite_rings sage.rings.function_field - Place (1/x, 1/x*y) - Place (x, x*y) + 2*Place (x + 1, x*y) @@ -48,10 +48,16 @@ AUTHORS: """ # **************************************************************************** -# Copyright (C) 2010 William Stein -# Copyright (C) 2010 Robert Bradshaw -# Copyright (C) 2011-2020 Julian Rueth -# Copyright (C) 2011 Maarten Derickx +# Copyright (C) 2010 William Stein +# 2010 Robert Bradshaw +# 2011-2020 Julian Rueth +# 2011 Maarten Derickx +# 2015 Nils Bruin +# 2016 Frédéric Chapoton +# 2017-2019 Kwankyu Lee +# 2018-2020 Travis Scrimshaw +# 2019 Brent Baccala +# 2021 Saher Amasha # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -104,9 +110,6 @@ cdef class FunctionFieldElement(FieldElement): sage: isinstance(t, sage.rings.function_field.element.FunctionFieldElement) True """ - cdef readonly object _x - cdef readonly object _matrix - def __reduce__(self): """ EXAMPLES:: @@ -138,8 +141,8 @@ cdef class FunctionFieldElement(FieldElement): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(b^2-a) - sage: b.__pari__() + sage: L. = K.extension(b^2 - a) # optional - sage.rings.function_field + sage: b.__pari__() # optional - sage.rings.function_field Traceback (most recent call last): ... NotImplementedError: PARI does not support general function field elements. @@ -177,19 +180,19 @@ cdef class FunctionFieldElement(FieldElement): A rational function field:: sage: K. = FunctionField(QQ) - sage: t.matrix() + sage: t.matrix() # optional - sage.modules [t] - sage: (1/(t+1)).matrix() + sage: (1/(t+1)).matrix() # optional - sage.modules [1/(t + 1)] Now an example in a nontrivial extension of a rational function field:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: y.matrix() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: y.matrix() # optional - sage.modules sage.rings.function_field [ 0 1] [-4*x^3 x] - sage: y.matrix().charpoly('Z') + sage: y.matrix().charpoly('Z') # optional - sage.modules sage.rings.function_field Z^2 - x*Z + 4*x^3 An example in a relative extension, where neither function @@ -197,21 +200,21 @@ cdef class FunctionFieldElement(FieldElement): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: M. = L[] - sage: Z. = L.extension(T^3 - y^2*T + x) - sage: alpha.matrix() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: M. = L[] # optional - sage.rings.function_field + sage: Z. = L.extension(T^3 - y^2*T + x) # optional - sage.rings.function_field + sage: alpha.matrix() # optional - sage.modules sage.rings.function_field [ 0 1 0] [ 0 0 1] [ -x x*y - 4*x^3 0] - sage: alpha.matrix(K) + sage: alpha.matrix(K) # optional - sage.modules sage.rings.function_field [ 0 0 1 0 0 0] [ 0 0 0 1 0 0] [ 0 0 0 0 1 0] [ 0 0 0 0 0 1] [ -x 0 -4*x^3 x 0 0] [ 0 -x -4*x^4 -4*x^3 + x^2 0 0] - sage: alpha.matrix(Z) + sage: alpha.matrix(Z) # optional - sage.modules sage.rings.function_field [alpha] We show that this matrix does indeed work as expected when making a @@ -219,13 +222,13 @@ cdef class FunctionFieldElement(FieldElement): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: V, from_V, to_V = L.vector_space() - sage: y5 = to_V(y^5); y5 + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # optional - sage.rings.function_field + sage: V, from_V, to_V = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: y5 = to_V(y^5); y5 # optional - sage.modules sage.rings.function_field ((x^4 + 1)/x, 2*x, 0, 0, 0) - sage: y4y = to_V(y^4) * y.matrix(); y4y + sage: y4y = to_V(y^4) * y.matrix(); y4y # optional - sage.modules sage.rings.function_field ((x^4 + 1)/x, 2*x, 0, 0, 0) - sage: y5 == y4y + sage: y5 == y4y # optional - sage.modules sage.rings.function_field True """ # multiply each element of the vector space isomorphic to the parent @@ -247,8 +250,8 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: y.trace() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: y.trace() # optional - sage.modules sage.rings.function_field x """ return self.matrix().trace() @@ -260,18 +263,18 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: y.norm() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: y.norm() # optional - sage.modules sage.rings.function_field 4*x^3 The norm is relative:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] - sage: M. = L.extension(z^3 - y^2*z + x) - sage: z.norm() + sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z^3 - y^2*z + x) # optional - sage.rings.function_field + sage: z.norm() # optional - sage.modules sage.rings.function_field -x - sage: z.norm().parent() + sage: z.norm().parent() # optional - sage.modules sage.rings.function_field Function field in y defined by y^2 - x*y + 4*x^3 """ return self.matrix().determinant() @@ -320,13 +323,13 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] - sage: M. = L.extension(z^3 - y^2*z + x) - sage: x.characteristic_polynomial('W') + sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z^3 - y^2*z + x) # optional - sage.rings.function_field + sage: x.characteristic_polynomial('W') # optional - sage.modules sage.rings.function_field W - x - sage: y.characteristic_polynomial('W') + sage: y.characteristic_polynomial('W') # optional - sage.modules sage.rings.function_field W^2 - x*W + 4*x^3 - sage: z.characteristic_polynomial('W') + sage: z.characteristic_polynomial('W') # optional - sage.modules sage.rings.function_field W^3 + (-x*y + 4*x^3)*W + x """ return self.matrix().characteristic_polynomial(*args, **kwds) @@ -341,13 +344,13 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] - sage: M. = L.extension(z^3 - y^2*z + x) - sage: x.minimal_polynomial('W') + sage: L. = K.extension(y^2 - x*y + 4*x^3); R. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z^3 - y^2*z + x) # optional - sage.rings.function_field + sage: x.minimal_polynomial('W') # optional - sage.modules sage.rings.function_field W - x - sage: y.minimal_polynomial('W') + sage: y.minimal_polynomial('W') # optional - sage.modules sage.rings.function_field W^2 - x*W + 4*x^3 - sage: z.minimal_polynomial('W') + sage: z.minimal_polynomial('W') # optional - sage.modules sage.rings.function_field W^3 + (-x*y + 4*x^3)*W + x """ return self.matrix().minimal_polynomial(*args, **kwds) @@ -361,16 +364,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: y.is_integral() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: y.is_integral() # optional - sage.rings.function_field True - sage: (y/x).is_integral() + sage: (y/x).is_integral() # optional - sage.modules sage.rings.function_field True - sage: (y/x)^2 - (y/x) + 4*x + sage: (y/x)^2 - (y/x) + 4*x # optional - sage.modules sage.rings.function_field 0 - sage: (y/x^2).is_integral() + sage: (y/x^2).is_integral() # optional - sage.modules sage.rings.function_field False - sage: (y/x).minimal_polynomial('W') + sage: (y/x).minimal_polynomial('W') # optional - sage.modules sage.rings.function_field W^2 - W + 4*x """ R = self.parent().base_field().maximal_order() @@ -384,29 +387,29 @@ cdef class FunctionFieldElement(FieldElement): sage: K. = FunctionField(QQ) sage: f = 1 / t - sage: f.differential() + sage: f.differential() # optional - sage.modules (-1/t^2) d(t) - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x +1/x) - sage: (y^3 + x).differential() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x +1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: (y^3 + x).differential() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field (((x^2 + 1)/x^2)*y + (x^4 + x^3 + 1)/x^3) d(x) TESTS: Verify that :trac:`27712` is resolved:: - sage: K. = FunctionField(GF(31)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: R. = L[] - sage: M. = L.extension(z^2 - y) + sage: K. = FunctionField(GF(31)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: R. = L[] # optional - sage.rings.finite_rings sage.rings.function_field + sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings sage.rings.function_field - sage: x.differential() + sage: x.differential() # optional - sage.rings.finite_rings sage.modules d(x) - sage: y.differential() + sage: y.differential() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field (16/x*y) d(x) - sage: z.differential() + sage: z.differential() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field (8/x*z) d(x) """ F = self.parent() @@ -424,12 +427,12 @@ cdef class FunctionFieldElement(FieldElement): sage: K. = FunctionField(QQ) sage: f = (t + 1) / (t^2 - 1/3) - sage: f.derivative() + sage: f.derivative() # optional - sage.modules (-t^2 - 2*t - 1/3)/(t^4 - 2/3*t^2 + 1/9) - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: (y^3 + x).derivative() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: (y^3 + x).derivative() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field ((x^2 + 1)/x^2)*y + (x^4 + x^3 + 1)/x^3 """ D = self.parent().derivation() @@ -449,16 +452,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: f = t^2 - sage: f.higher_derivative(2) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: f = t^2 # optional - sage.rings.finite_rings + sage: f.higher_derivative(2) # optional - sage.rings.finite_rings sage.modules 1 :: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: (y^3 + x).higher_derivative(2) + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: (y^3 + x).higher_derivative(2) # optional - sage.rings.finite_rings sage.modules sage.rings.function_field 1/x^3*y + (x^6 + x^4 + x^3 + x^2 + x + 1)/x^5 """ D = self.parent().higher_derivation() @@ -471,18 +474,18 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: f = 1/(x^3 + x^2 + x) - sage: f.divisor() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings + sage: f.divisor() # optional - sage.rings.finite_rings sage.modules 3*Place (1/x) - Place (x) - Place (x^2 + x + 1) :: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: y.divisor() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: y.divisor() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field - Place (1/x, 1/x*y) - Place (x, x*y) + 2*Place (x + 1, x*y) @@ -501,16 +504,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: f = 1/(x^3 + x^2 + x) - sage: f.divisor_of_zeros() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings + sage: f.divisor_of_zeros() # optional - sage.rings.finite_rings sage.modules 3*Place (1/x) :: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: (x/y).divisor_of_zeros() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: (x/y).divisor_of_zeros() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field 3*Place (x, x*y) """ if self.is_zero(): @@ -527,17 +530,17 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: f = 1/(x^3 + x^2 + x) - sage: f.divisor_of_poles() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings + sage: f.divisor_of_poles() # optional - sage.rings.finite_rings sage.modules Place (x) + Place (x^2 + x + 1) :: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: (x/y).divisor_of_poles() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: (x/y).divisor_of_poles() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field Place (1/x, 1/x*y) + 2*Place (x + 1, x*y) """ if self.is_zero(): @@ -554,16 +557,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: f = 1/(x^3 + x^2 + x) - sage: f.zeros() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings + sage: f.zeros() # optional - sage.rings.finite_rings sage.modules [Place (1/x)] :: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: (x/y).zeros() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: (x/y).zeros() # optional - sage.rings.finite_rings sage.modules [Place (x, x*y)] """ return self.divisor_of_zeros().support() @@ -574,16 +577,16 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: f = 1/(x^3 + x^2 + x) - sage: f.poles() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: f = 1/(x^3 + x^2 + x) # optional - sage.rings.finite_rings + sage: f.poles() # optional - sage.rings.finite_rings sage.modules [Place (x), Place (x^2 + x + 1)] :: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: (x/y).poles() + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: (x/y).poles() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field [Place (1/x, 1/x*y), Place (x + 1, x*y)] """ return self.divisor_of_poles().support() @@ -598,19 +601,19 @@ cdef class FunctionFieldElement(FieldElement): 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) + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_infinite()[0] # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: y.valuation(p) # optional - sage.rings.finite_rings sage.modules sage.rings.function_field -1 :: sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: p = O.ideal(x-1).place() - sage: y.valuation(p) + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.function_field sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.function_field sage.rings.function_field + sage: p = O.ideal(x - 1).place() # optional - sage.rings.function_field sage.rings.function_field + sage: y.valuation(p) # optional - sage.rings.function_field sage.rings.function_field 0 """ prime = place.prime_ideal() @@ -633,23 +636,23 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(5)) - sage: p = K.place_infinite() - sage: f = 1/t^2 + 3 - sage: f.evaluate(p) + sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: p = K.place_infinite() # optional - sage.rings.finite_rings + sage: f = 1/t^2 + 3 # optional - sage.rings.finite_rings + sage: f.evaluate(p) # optional - sage.rings.finite_rings 3 :: - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p, = L.places_infinite() - sage: p, = L.places_infinite() - sage: (y + x).evaluate(p) + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p, = L.places_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: p, = L.places_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: (y + x).evaluate(p) # optional - sage.rings.finite_rings sage.rings.function_field Traceback (most recent call last): ... ValueError: has a pole at the place - sage: (y/x + 1).evaluate(p) + sage: (y/x + 1).evaluate(p) # optional - sage.rings.finite_rings sage.rings.function_field 1 """ R, fr_R, to_R = place._residue_field() @@ -682,9 +685,9 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(3)) - sage: f = (x+1)/(x-1) - sage: f.is_nth_power(2) + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: f = (x+1)/(x-1) # optional - sage.rings.finite_rings + sage: f.is_nth_power(2) # optional - sage.rings.finite_rings False """ raise NotImplementedError("is_nth_power() not implemented for generic elements") @@ -708,873 +711,10 @@ cdef class FunctionFieldElement(FieldElement): EXAMPLES:: - sage: K. = FunctionField(GF(3)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: L(y^27).nth_root(27) + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L(y^27).nth_root(27) # optional - sage.rings.finite_rings sage.rings.function_field y """ raise NotImplementedError("nth_root() not implemented for generic elements") - -cdef class FunctionFieldElement_polymod(FunctionFieldElement): - """ - Elements of a finite extension of a function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: x*y + 1/x^3 - x*y + 1/x^3 - """ - def __init__(self, parent, x, reduce=True): - """ - Initialize. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: TestSuite(x*y + 1/x^3).run() - """ - FieldElement.__init__(self, parent) - if reduce: - self._x = x % self._parent.polynomial() - else: - self._x = x - - def element(self): - """ - Return the underlying polynomial that represents the element. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(T^2 - x*T + 4*x^3) - sage: f = y/x^2 + x/(x^2+1); f - 1/x^2*y + x/(x^2 + 1) - sage: f.element() - 1/x^2*y + x/(x^2 + 1) - """ - return self._x - - def _repr_(self): - """ - Return the string representation of the element. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: y._repr_() - 'y' - """ - return self._x._repr(name=self.parent().variable_name()) - - def __bool__(self): - """ - Return True if the element is not zero. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: bool(y) - True - sage: bool(L(0)) - False - sage: bool(L.coerce(L.polynomial())) - False - """ - return not not self._x - - def __hash__(self): - """ - Return the hash of the element. - - TESTS:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: len({hash(y^i+x^j) for i in [-2..2] for j in [-2..2]}) >= 24 - True - """ - return hash(self._x) - - cpdef _richcmp_(self, other, int op): - """ - Do rich comparison with the other element with respect to ``op`` - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: L(0) == 0 - True - sage: y != L(2) - True - """ - cdef FunctionFieldElement left = self - cdef FunctionFieldElement right = other - return richcmp(left._x, right._x, op) - - cpdef _add_(self, right): - """ - Add the element with the other element. - - INPUT: - - - ``right`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: (2*y + x/(1+x^3)) + (3*y + 5*x*y) # indirect doctest - (5*x + 5)*y + x/(x^3 + 1) - sage: (y^2 - x*y + 4*x^3)==0 # indirect doctest - True - sage: -y+y - 0 - """ - cdef FunctionFieldElement res = self._new_c() - res._x = self._x + (right)._x - return res - - cpdef _sub_(self, right): - """ - Subtract the other element from the element. - - INPUT: - - - ``right`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: (2*y + x/(1+x^3)) - (3*y + 5*x*y) # indirect doctest - (-5*x - 1)*y + x/(x^3 + 1) - sage: y-y - 0 - """ - cdef FunctionFieldElement res = self._new_c() - res._x = self._x - (right)._x - return res - - cpdef _mul_(self, right): - """ - Multiply the element with the other element. - - INPUT: - - - ``right`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: y * (3*y + 5*x*y) # indirect doctest - (5*x^2 + 3*x)*y - 20*x^4 - 12*x^3 - """ - cdef FunctionFieldElement res = self._new_c() - res._x = (self._x * (right)._x) % self._parent.polynomial() - return res - - cpdef _div_(self, right): - """ - Divide the element with the other element. - - INPUT: - - - ``right`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: (2*y + x/(1+x^3)) / (2*y + x/(1+x^3)) # indirect doctest - 1 - sage: 1 / (y^2 - x*y + 4*x^3) # indirect doctest - Traceback (most recent call last): - ... - ZeroDivisionError: Cannot invert 0 - """ - return self * ~right - - def __invert__(self): - """ - Return the multiplicative inverse of the element. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: a = ~(2*y + 1/x); a # indirect doctest - (-1/8*x^2/(x^5 + 1/8*x^2 + 1/16))*y + (1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16) - sage: a*(2*y + 1/x) - 1 - """ - if self.is_zero(): - raise ZeroDivisionError("Cannot invert 0") - P = self._parent - return P(self._x.xgcd(P._polynomial)[1]) - - cpdef list list(self): - """ - Return the list of the coefficients representing the element. - - If the function field is `K[y]/(f(y))`, then return the coefficients of - the reduced presentation of the element as a polynomial in `K[y]`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: a = ~(2*y + 1/x); a - (-1/8*x^2/(x^5 + 1/8*x^2 + 1/16))*y + (1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16) - sage: a.list() - [(1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16), -1/8*x^2/(x^5 + 1/8*x^2 + 1/16)] - sage: (x*y).list() - [0, x] - """ - return self._x.padded_list(self._parent.degree()) - - cpdef FunctionFieldElement nth_root(self, n): - r""" - Return an ``n``-th root of this element in the function field. - - INPUT: - - - ``n`` -- an integer - - OUTPUT: - - Returns an element ``a`` in the function field such that this element - equals `a^n`. Raises an error if no such element exists. - - ALGORITHM: - - If ``n`` is a power of the characteristic of the field and the constant - base field is perfect, then this uses the algorithm described in - Proposition 12 of [GiTr1996]_. - - .. SEEALSO:: - - :meth:`is_nth_power` - - EXAMPLES:: - - sage: K. = FunctionField(GF(3)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: L(y^3).nth_root(3) - y - sage: L(y^9).nth_root(-9) - 1/x*y - - This also works for inseparable extensions:: - - sage: K. = FunctionField(GF(3)) - sage: R. = K[] - sage: L. = K.extension(y^3 - x^2) - sage: L(x).nth_root(3)^3 - x - sage: L(x^9).nth_root(-27)^-27 - x^9 - - """ - if n == 1: - return self - if n < 0: - return (~self).nth_root(-n) - if n == 0: - if not self.is_one(): - raise ValueError("element is not a 0-th power") - return self - - # reduce to the separable case - poly = self._parent._polynomial - if not poly.gcd(poly.derivative()).is_one(): - L, from_L, to_L = self._parent.separable_model(('t', 'w')) - return from_L(to_L(self).nth_root(n)) - - constant_base_field = self._parent.constant_base_field() - p = constant_base_field.characteristic() - if p.divides(n) and constant_base_field.is_perfect(): - return self._pth_root().nth_root(n//p) - - raise NotImplementedError("nth_root() not implemented for this n") - - cpdef bint is_nth_power(self, n): - r""" - Return whether this element is an ``n``-th power in the function field. - - INPUT: - - - ``n`` -- an integer - - ALGORITHM: - - If ``n`` is a power of the characteristic of the field and the constant - base field is perfect, then this uses the algorithm described in - Proposition 12 of [GiTr1996]_. - - .. SEEALSO:: - - :meth:`nth_root` - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: y.is_nth_power(2) - False - sage: L(x).is_nth_power(2) - True - - """ - if n == 0: - return self.is_one() - if n == 1: - return True - if n < 0: - return self.is_unit() and (~self).is_nth_power(-n) - - # reduce to the separable case - poly = self._parent._polynomial - if not poly.gcd(poly.derivative()).is_one(): - L, from_L, to_L = self._parent.separable_model(('t', 'w')) - return to_L(self).is_nth_power(n) - - constant_base_field = self._parent.constant_base_field() - p = constant_base_field.characteristic() - if p.divides(n) and constant_base_field.is_perfect(): - return self._parent.derivation()(self).is_zero() and self._pth_root().is_nth_power(n//p) - - raise NotImplementedError("is_nth_power() not implemented for this n") - - cdef FunctionFieldElement _pth_root(self): - r""" - Helper method for :meth:`nth_root` and :meth:`is_nth_power` which - computes a `p`-th root if the characteristic is `p` and the constant - base field is perfect. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: (y^3).nth_root(3) # indirect doctest - y - """ - cdef Py_ssize_t deg = self._parent.degree() - if deg == 1: - return self._parent(self._x[0].nth_root(self._parent.characteristic())) - - from .function_field import RationalFunctionField - if not isinstance(self.base_ring(), RationalFunctionField): - raise NotImplementedError("only implemented for simple extensions of function fields") - # compute a representation of the generator y of the field in terms of powers of y^p - cdef Py_ssize_t i - cdef list v = [] - char = self._parent.characteristic() - cdef FunctionFieldElement_polymod yp = self._parent.gen() ** char - val = self._parent.one()._x - poly = self._parent.polynomial() - for i in range(deg): - v += val.padded_list(deg) - val = (val * yp._x) % poly - from sage.matrix.matrix_space import MatrixSpace - MS = MatrixSpace(self._parent._base, deg) - M = MS(v) - y = self._parent._base.polynomial_ring()(M.solve_left(MS.column_space()([0,1]+[0]*(deg-2))).list()) - - f = self._x(y).map_coefficients(lambda c: c.nth_root(char)) - return self._parent(f) - - -cdef class FunctionFieldElement_rational(FunctionFieldElement): - """ - Elements of a rational function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); K - Rational function field in t over Rational Field - sage: t^2 + 3/2*t - t^2 + 3/2*t - sage: FunctionField(QQ,'t').gen()^3 - t^3 - """ - def __init__(self, parent, x, reduce=True): - """ - Initialize. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: x = t^3 - sage: TestSuite(x).run() - """ - FieldElement.__init__(self, parent) - self._x = x - - def __pari__(self): - r""" - Coerce the element to PARI. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: ((a+1)/(a-1)).__pari__() - (a + 1)/(a - 1) - - """ - return self.element().__pari__() - - def element(self): - """ - Return the underlying fraction field element that represents the element. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)) - sage: t.element() - t - sage: type(t.element()) - <... 'sage.rings.fraction_field_FpT.FpTElement'> - - sage: K. = FunctionField(GF(131101)) - sage: t.element() - t - sage: type(t.element()) - <... 'sage.rings.fraction_field_element.FractionFieldElement_1poly_field'> - """ - return self._x - - cpdef list list(self): - """ - Return a list with just the element. - - The list represents the element when the rational function field is - viewed as a (one-dimensional) vector space over itself. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: t.list() - [t] - """ - return [self] - - def _repr_(self): - """ - Return the string representation of the element. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: t._repr_() - 't' - """ - return repr(self._x) - - def __bool__(self): - """ - Return True if the element is not zero. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: bool(t) - True - sage: bool(K(0)) - False - sage: bool(K(1)) - True - """ - return not not self._x - - def __hash__(self): - """ - Return the hash of the element. - - TESTS: - - It would be nice if the following would produce a list of - 15 distinct hashes:: - - sage: K. = FunctionField(QQ) - sage: len({hash(t^i+t^j) for i in [-2..2] for j in [i..2]}) >= 10 - True - """ - return hash(self._x) - - cpdef _richcmp_(self, other, int op): - """ - Compare the element with the other element with respect to ``op`` - - INPUT: - - - ``other`` -- element - - - ``op`` -- comparison operator - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: t > 0 - True - sage: t < t^2 - True - """ - cdef FunctionFieldElement left - cdef FunctionFieldElement right - try: - left = self - right = other - lp = left._parent - rp = right._parent - if lp != rp: - return richcmp_not_equal(lp, rp, op) - return richcmp(left._x, right._x, op) - except TypeError: - return NotImplemented - - cpdef _add_(self, right): - """ - Add the element with the other element. - - INPUT: - - - ``right`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: t + (3*t^3) # indirect doctest - 3*t^3 + t - """ - cdef FunctionFieldElement res = self._new_c() - res._x = self._x + (right)._x - return res - - cpdef _sub_(self, right): - """ - Subtract the other element from the element. - - INPUT: - - - ``right`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: t - (3*t^3) # indirect doctest - -3*t^3 + t - """ - cdef FunctionFieldElement res = self._new_c() - res._x = self._x - (right)._x - return res - - cpdef _mul_(self, right): - """ - Multiply the element with the other element - - INPUT: - - - ``right`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: (t+1) * (t^2-1) # indirect doctest - t^3 + t^2 - t - 1 - """ - cdef FunctionFieldElement res = self._new_c() - res._x = self._x * (right)._x - return res - - cpdef _div_(self, right): - """ - Divide the element with the other element - - INPUT: - - - ``right`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: (t+1) / (t^2 - 1) # indirect doctest - 1/(t - 1) - """ - cdef FunctionFieldElement res = self._new_c() - res._parent = self._parent.fraction_field() - res._x = self._x / (right)._x - return res - - def numerator(self): - """ - Return the numerator of the rational function. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: f = (t+1) / (t^2 - 1/3); f - (t + 1)/(t^2 - 1/3) - sage: f.numerator() - t + 1 - """ - return self._x.numerator() - - def denominator(self): - """ - Return the denominator of the rational function. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: f = (t+1) / (t^2 - 1/3); f - (t + 1)/(t^2 - 1/3) - sage: f.denominator() - t^2 - 1/3 - """ - return self._x.denominator() - - def valuation(self, place): - """ - Return the valuation of the rational function at the place. - - Rational function field places are associated with irreducible - polynomials. - - INPUT: - - - ``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) - 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 - """ - 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): - """ - Return whether the element is a square. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: t.is_square() - False - sage: (t^2/4).is_square() - True - sage: f = 9 * (t+1)^6 / (t^2 - 2*t + 1); f.is_square() - True - - sage: K. = FunctionField(GF(5)) - sage: (-t^2).is_square() - True - sage: (-t^2).sqrt() - 2*t - """ - return self._x.is_square() - - def sqrt(self, all=False): - """ - Return the square root of the rational function. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: f = t^2 - 2 + 1/t^2; f.sqrt() - (t^2 - 1)/t - sage: f = t^2; f.sqrt(all=True) - [t, -t] - - TESTS:: - - sage: K(4/9).sqrt() - 2/3 - sage: K(0).sqrt(all=True) - [0] - """ - if all: - return [self._parent(r) for r in self._x.sqrt(all=True)] - else: - return self._parent(self._x.sqrt()) - - cpdef bint is_nth_power(self, n): - r""" - Return whether this element is an ``n``-th power in the rational - function field. - - INPUT: - - - ``n`` -- an integer - - OUTPUT: - - Returns ``True`` if there is an element `a` in the function field such - that this element equals `a^n`. - - ALGORITHM: - - If ``n`` is a power of the characteristic of the field and the constant - base field is perfect, then this uses the algorithm described in Lemma - 3 of [GiTr1996]_. - - .. SEEALSO:: - - :meth:`nth_root` - - EXAMPLES:: - - sage: K. = FunctionField(GF(3)) - sage: f = (x+1)/(x-1) - sage: f.is_nth_power(1) - True - sage: f.is_nth_power(3) - False - sage: (f^3).is_nth_power(3) - True - sage: (f^9).is_nth_power(-9) - True - """ - if n == 1: - return True - if n < 0: - return (~self).is_nth_power(-n) - - p = self._parent.characteristic() - if n == p: - return self._parent.derivation()(self).is_zero() - if p.divides(n): - return self.is_nth_power(p) and self.nth_root(p).is_nth_power(n//p) - if n == 2: - return self.is_square() - - raise NotImplementedError("is_nth_power() not implemented for the given n") - - cpdef FunctionFieldElement nth_root(self, n): - r""" - Return an ``n``-th root of this element in the function field. - - INPUT: - - - ``n`` -- an integer - - OUTPUT: - - Returns an element ``a`` in the rational function field such that this - element equals `a^n`. Raises an error if no such element exists. - - ALGORITHM: - - If ``n`` is a power of the characteristic of the field and the constant - base field is perfect, then this uses the algorithm described in - Corollary 3 of [GiTr1996]_. - - .. SEEALSO:: - - :meth:`is_nth_power` - - EXAMPLES:: - - sage: K. = FunctionField(GF(3)) - sage: f = (x+1)/(x+2) - sage: f.nth_root(1) - (x + 1)/(x + 2) - sage: f.nth_root(3) - Traceback (most recent call last): - ... - ValueError: element is not an n-th power - sage: (f^3).nth_root(3) - (x + 1)/(x + 2) - sage: (f^9).nth_root(-9) - (x + 2)/(x + 1) - """ - if n == 0: - if not self.is_one(): - raise ValueError("element is not a 0-th power") - return self - if n == 1: - return self - if n < 0: - return (~self).nth_root(-n) - p = self._parent.characteristic() - if p.divides(n): - if not self.is_nth_power(p): - raise ValueError("element is not an n-th power") - return self._parent(self.numerator().nth_root(p) / self.denominator().nth_root(p)).nth_root(n//p) - if n == 2: - return self.sqrt() - - raise NotImplementedError("nth_root() not implemented for {}".format(n)) - - def factor(self): - """ - Factor the rational function. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: f = (t+1) / (t^2 - 1/3) - sage: f.factor() - (t + 1) * (t^2 - 1/3)^-1 - sage: (7*f).factor() - (7) * (t + 1) * (t^2 - 1/3)^-1 - sage: ((7*f).factor()).unit() - 7 - sage: (f^3).factor() - (t + 1)^3 * (t^2 - 1/3)^-3 - """ - P = self.parent() - F = self._x.factor() - from sage.structure.factorization import Factorization - return Factorization([(P(a),e) for a,e in F], unit=F.unit()) - - def inverse_mod(self, I): - """ - Return an inverse of the element modulo the integral ideal `I`, if `I` - and the element together generate the unit ideal. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order(); I = O.ideal(x^2+1) - sage: t = O(x+1).inverse_mod(I); t - -1/2*x + 1/2 - sage: (t*(x+1) - 1) in I - True - - """ - assert len(I.gens()) == 1 - f = I.gens()[0]._x - assert f.denominator() == 1 - assert self._x.denominator() == 1 - return self.parent()(self._x.numerator().inverse_mod(f.numerator())) diff --git a/src/sage/rings/function_field/element_polymod.pyx b/src/sage/rings/function_field/element_polymod.pyx new file mode 100644 index 00000000000..2c858f8568c --- /dev/null +++ b/src/sage/rings/function_field/element_polymod.pyx @@ -0,0 +1,398 @@ +# sage.doctest: optional - sage.rings.function_field +r""" +Elements of function fields: extension +""" + +#***************************************************************************** +# Copyright (C) 2023 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 sage.misc.cachefunc import cached_method +from sage.structure.richcmp cimport richcmp, richcmp_not_equal +from sage.structure.element cimport FieldElement, RingElement, ModuleElement, Element + +from sage.rings.function_field.element cimport FunctionFieldElement + + +cdef class FunctionFieldElement_polymod(FunctionFieldElement): + """ + Elements of a finite extension of a function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: x*y + 1/x^3 + x*y + 1/x^3 + """ + def __init__(self, parent, x, reduce=True): + """ + Initialize. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: TestSuite(x*y + 1/x^3).run() + """ + FieldElement.__init__(self, parent) + if reduce: + self._x = x % self._parent.polynomial() + else: + self._x = x + + def element(self): + """ + Return the underlying polynomial that represents the element. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(T^2 - x*T + 4*x^3) + sage: f = y/x^2 + x/(x^2+1); f + 1/x^2*y + x/(x^2 + 1) + sage: f.element() + 1/x^2*y + x/(x^2 + 1) + """ + return self._x + + def _repr_(self): + """ + Return the string representation of the element. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: y._repr_() + 'y' + """ + return self._x._repr(name=self.parent().variable_name()) + + def __bool__(self): + """ + Return True if the element is not zero. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: bool(y) + True + sage: bool(L(0)) + False + sage: bool(L.coerce(L.polynomial())) + False + """ + return not not self._x + + def __hash__(self): + """ + Return the hash of the element. + + TESTS:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: len({hash(y^i+x^j) for i in [-2..2] for j in [-2..2]}) >= 24 + True + """ + return hash(self._x) + + cpdef _richcmp_(self, other, int op): + """ + Do rich comparison with the other element with respect to ``op`` + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: L(0) == 0 + True + sage: y != L(2) + True + """ + cdef FunctionFieldElement left = self + cdef FunctionFieldElement right = other + return richcmp(left._x, right._x, op) + + cpdef _add_(self, right): + """ + Add the element with the other element. + + INPUT: + + - ``right`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: (2*y + x/(1+x^3)) + (3*y + 5*x*y) # indirect doctest + (5*x + 5)*y + x/(x^3 + 1) + sage: (y^2 - x*y + 4*x^3)==0 # indirect doctest + True + sage: -y + y + 0 + """ + cdef FunctionFieldElement res = self._new_c() + res._x = self._x + (right)._x + return res + + cpdef _sub_(self, right): + """ + Subtract the other element from the element. + + INPUT: + + - ``right`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: (2*y + x/(1+x^3)) - (3*y + 5*x*y) # indirect doctest + (-5*x - 1)*y + x/(x^3 + 1) + sage: y - y + 0 + """ + cdef FunctionFieldElement res = self._new_c() + res._x = self._x - (right)._x + return res + + cpdef _mul_(self, right): + """ + Multiply the element with the other element. + + INPUT: + + - ``right`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: y * (3*y + 5*x*y) # indirect doctest + (5*x^2 + 3*x)*y - 20*x^4 - 12*x^3 + """ + cdef FunctionFieldElement res = self._new_c() + res._x = (self._x * (right)._x) % self._parent.polynomial() + return res + + cpdef _div_(self, right): + """ + Divide the element with the other element. + + INPUT: + + - ``right`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: (2*y + x/(1+x^3)) / (2*y + x/(1+x^3)) # indirect doctest + 1 + sage: 1 / (y^2 - x*y + 4*x^3) # indirect doctest + Traceback (most recent call last): + ... + ZeroDivisionError: Cannot invert 0 + """ + return self * ~right + + def __invert__(self): + """ + Return the multiplicative inverse of the element. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: a = ~(2*y + 1/x); a # indirect doctest + (-1/8*x^2/(x^5 + 1/8*x^2 + 1/16))*y + (1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16) + sage: a*(2*y + 1/x) + 1 + """ + if self.is_zero(): + raise ZeroDivisionError("Cannot invert 0") + P = self._parent + return P(self._x.xgcd(P._polynomial)[1]) + + cpdef list list(self): + """ + Return the list of the coefficients representing the element. + + If the function field is `K[y]/(f(y))`, then return the coefficients of + the reduced presentation of the element as a polynomial in `K[y]`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x*y + 4*x^3) + sage: a = ~(2*y + 1/x); a + (-1/8*x^2/(x^5 + 1/8*x^2 + 1/16))*y + (1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16) + sage: a.list() + [(1/8*x^3 + 1/16*x)/(x^5 + 1/8*x^2 + 1/16), -1/8*x^2/(x^5 + 1/8*x^2 + 1/16)] + sage: (x*y).list() + [0, x] + """ + return self._x.padded_list(self._parent.degree()) + + cpdef FunctionFieldElement nth_root(self, n): + r""" + Return an ``n``-th root of this element in the function field. + + INPUT: + + - ``n`` -- an integer + + OUTPUT: + + Returns an element ``a`` in the function field such that this element + equals `a^n`. Raises an error if no such element exists. + + ALGORITHM: + + If ``n`` is a power of the characteristic of the field and the constant + base field is perfect, then this uses the algorithm described in + Proposition 12 of [GiTr1996]_. + + .. SEEALSO:: + + :meth:`is_nth_power` + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: L(y^3).nth_root(3) # optional - sage.rings.finite_rings + y + sage: L(y^9).nth_root(-9) # optional - sage.rings.finite_rings + 1/x*y + + This also works for inseparable extensions:: + + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 - x^2) # optional - sage.rings.finite_rings + sage: L(x).nth_root(3)^3 # optional - sage.rings.finite_rings + x + sage: L(x^9).nth_root(-27)^-27 # optional - sage.rings.finite_rings + x^9 + + """ + if n == 1: + return self + if n < 0: + return (~self).nth_root(-n) + if n == 0: + if not self.is_one(): + raise ValueError("element is not a 0-th power") + return self + + # reduce to the separable case + poly = self._parent._polynomial + if not poly.gcd(poly.derivative()).is_one(): + L, from_L, to_L = self._parent.separable_model(('t', 'w')) + return from_L(to_L(self).nth_root(n)) + + constant_base_field = self._parent.constant_base_field() + p = constant_base_field.characteristic() + if p.divides(n) and constant_base_field.is_perfect(): + return self._pth_root().nth_root(n//p) + + raise NotImplementedError("nth_root() not implemented for this n") + + cpdef bint is_nth_power(self, n): + r""" + Return whether this element is an ``n``-th power in the function field. + + INPUT: + + - ``n`` -- an integer + + ALGORITHM: + + If ``n`` is a power of the characteristic of the field and the constant + base field is perfect, then this uses the algorithm described in + Proposition 12 of [GiTr1996]_. + + .. SEEALSO:: + + :meth:`nth_root` + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: y.is_nth_power(2) # optional - sage.rings.finite_rings + False + sage: L(x).is_nth_power(2) # optional - sage.rings.finite_rings + True + + """ + if n == 0: + return self.is_one() + if n == 1: + return True + if n < 0: + return self.is_unit() and (~self).is_nth_power(-n) + + # reduce to the separable case + poly = self._parent._polynomial + if not poly.gcd(poly.derivative()).is_one(): + L, from_L, to_L = self._parent.separable_model(('t', 'w')) + return to_L(self).is_nth_power(n) + + constant_base_field = self._parent.constant_base_field() + p = constant_base_field.characteristic() + if p.divides(n) and constant_base_field.is_perfect(): + return self._parent.derivation()(self).is_zero() and self._pth_root().is_nth_power(n//p) + + raise NotImplementedError("is_nth_power() not implemented for this n") + + cdef FunctionFieldElement _pth_root(self): + r""" + Helper method for :meth:`nth_root` and :meth:`is_nth_power` which + computes a `p`-th root if the characteristic is `p` and the constant + base field is perfect. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: (y^3).nth_root(3) # indirect doctest # optional - sage.rings.finite_rings + y + """ + cdef Py_ssize_t deg = self._parent.degree() + if deg == 1: + return self._parent(self._x[0].nth_root(self._parent.characteristic())) + + from .function_field_rational import RationalFunctionField + if not isinstance(self.base_ring(), RationalFunctionField): + raise NotImplementedError("only implemented for simple extensions of function fields") + # compute a representation of the generator y of the field in terms of powers of y^p + cdef Py_ssize_t i + cdef list v = [] + char = self._parent.characteristic() + cdef FunctionFieldElement_polymod yp = self._parent.gen() ** char + val = self._parent.one()._x + poly = self._parent.polynomial() + for i in range(deg): + v += val.padded_list(deg) + val = (val * yp._x) % poly + from sage.matrix.matrix_space import MatrixSpace + MS = MatrixSpace(self._parent._base, deg) + M = MS(v) + y = self._parent._base.polynomial_ring()(M.solve_left(MS.column_space()([0,1]+[0]*(deg-2))).list()) + + f = self._x(y).map_coefficients(lambda c: c.nth_root(char)) + return self._parent(f) diff --git a/src/sage/rings/function_field/element_rational.pyx b/src/sage/rings/function_field/element_rational.pyx new file mode 100644 index 00000000000..76656e1735a --- /dev/null +++ b/src/sage/rings/function_field/element_rational.pyx @@ -0,0 +1,503 @@ +r""" +Elements of function fields: rational +""" + +#***************************************************************************** +# Copyright (C) 2023 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 sage.misc.cachefunc import cached_method +from sage.structure.richcmp cimport richcmp, richcmp_not_equal +from sage.structure.element cimport FieldElement, RingElement, ModuleElement, Element + +from sage.rings.function_field.element cimport FunctionFieldElement + + +cdef class FunctionFieldElement_rational(FunctionFieldElement): + """ + Elements of a rational function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); K + Rational function field in t over Rational Field + sage: t^2 + 3/2*t + t^2 + 3/2*t + sage: FunctionField(QQ,'t').gen()^3 + t^3 + """ + def __init__(self, parent, x, reduce=True): + """ + Initialize. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: x = t^3 + sage: TestSuite(x).run() + """ + FieldElement.__init__(self, parent) + self._x = x + + def __pari__(self): + r""" + Coerce the element to PARI. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: ((a+1)/(a-1)).__pari__() # optional - sage.rings.finite_rings + (a + 1)/(a - 1) + + """ + return self.element().__pari__() + + def element(self): + """ + Return the underlying fraction field element that represents the element. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: t.element() # optional - sage.rings.finite_rings + t + sage: type(t.element()) # optional - sage.rings.finite_rings + <... 'sage.rings.fraction_field_FpT.FpTElement'> + + sage: K. = FunctionField(GF(131101)) # optional - sage.rings.finite_rings + sage: t.element() # optional - sage.rings.finite_rings + t + sage: type(t.element()) # optional - sage.rings.finite_rings + <... 'sage.rings.fraction_field_element.FractionFieldElement_1poly_field'> + """ + return self._x + + cpdef list list(self): + """ + Return a list with just the element. + + The list represents the element when the rational function field is + viewed as a (one-dimensional) vector space over itself. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: t.list() + [t] + """ + return [self] + + def _repr_(self): + """ + Return the string representation of the element. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: t._repr_() + 't' + """ + return repr(self._x) + + def __bool__(self): + """ + Return True if the element is not zero. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: bool(t) + True + sage: bool(K(0)) + False + sage: bool(K(1)) + True + """ + return not not self._x + + def __hash__(self): + """ + Return the hash of the element. + + TESTS: + + It would be nice if the following would produce a list of + 15 distinct hashes:: + + sage: K. = FunctionField(QQ) + sage: len({hash(t^i+t^j) for i in [-2..2] for j in [i..2]}) >= 10 + True + """ + return hash(self._x) + + cpdef _richcmp_(self, other, int op): + """ + Compare the element with the other element with respect to ``op`` + + INPUT: + + - ``other`` -- element + + - ``op`` -- comparison operator + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: t > 0 + True + sage: t < t^2 + True + """ + cdef FunctionFieldElement left + cdef FunctionFieldElement right + try: + left = self + right = other + lp = left._parent + rp = right._parent + if lp != rp: + return richcmp_not_equal(lp, rp, op) + return richcmp(left._x, right._x, op) + except TypeError: + return NotImplemented + + cpdef _add_(self, right): + """ + Add the element with the other element. + + INPUT: + + - ``right`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: t + (3*t^3) # indirect doctest + 3*t^3 + t + """ + cdef FunctionFieldElement res = self._new_c() + res._x = self._x + (right)._x + return res + + cpdef _sub_(self, right): + """ + Subtract the other element from the element. + + INPUT: + + - ``right`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: t - (3*t^3) # indirect doctest + -3*t^3 + t + """ + cdef FunctionFieldElement res = self._new_c() + res._x = self._x - (right)._x + return res + + cpdef _mul_(self, right): + """ + Multiply the element with the other element + + INPUT: + + - ``right`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: (t+1) * (t^2-1) # indirect doctest + t^3 + t^2 - t - 1 + """ + cdef FunctionFieldElement res = self._new_c() + res._x = self._x * (right)._x + return res + + cpdef _div_(self, right): + """ + Divide the element with the other element + + INPUT: + + - ``right`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: (t+1) / (t^2 - 1) # indirect doctest + 1/(t - 1) + """ + cdef FunctionFieldElement res = self._new_c() + res._parent = self._parent.fraction_field() + res._x = self._x / (right)._x + return res + + def numerator(self): + """ + Return the numerator of the rational function. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: f = (t+1) / (t^2 - 1/3); f + (t + 1)/(t^2 - 1/3) + sage: f.numerator() + t + 1 + """ + return self._x.numerator() + + def denominator(self): + """ + Return the denominator of the rational function. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: f = (t+1) / (t^2 - 1/3); f + (t + 1)/(t^2 - 1/3) + sage: f.denominator() + t^2 - 1/3 + """ + return self._x.denominator() + + def valuation(self, place): + """ + Return the valuation of the rational function at the place. + + Rational function field places are associated with irreducible + polynomials. + + INPUT: + + - ``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) + 2 + sage: f.valuation(t) + 0 + sage: f.valuation(t^2 - 1/3) + -3 + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: p = K.places_finite()[0] # optional - sage.rings.finite_rings + sage: (1/x^2).valuation(p) # optional - sage.rings.finite_rings + -2 + """ + 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): + """ + Return whether the element is a square. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: t.is_square() + False + sage: (t^2/4).is_square() + True + sage: f = 9 * (t+1)^6 / (t^2 - 2*t + 1); f.is_square() + True + + sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: (-t^2).is_square() # optional - sage.rings.finite_rings + True + sage: (-t^2).sqrt() # optional - sage.rings.finite_rings + 2*t + """ + return self._x.is_square() + + def sqrt(self, all=False): + """ + Return the square root of the rational function. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: f = t^2 - 2 + 1/t^2; f.sqrt() + (t^2 - 1)/t + sage: f = t^2; f.sqrt(all=True) + [t, -t] + + TESTS:: + + sage: K(4/9).sqrt() + 2/3 + sage: K(0).sqrt(all=True) + [0] + """ + if all: + return [self._parent(r) for r in self._x.sqrt(all=True)] + else: + return self._parent(self._x.sqrt()) + + cpdef bint is_nth_power(self, n): + r""" + Return whether this element is an ``n``-th power in the rational + function field. + + INPUT: + + - ``n`` -- an integer + + OUTPUT: + + Returns ``True`` if there is an element `a` in the function field such + that this element equals `a^n`. + + ALGORITHM: + + If ``n`` is a power of the characteristic of the field and the constant + base field is perfect, then this uses the algorithm described in Lemma + 3 of [GiTr1996]_. + + .. SEEALSO:: + + :meth:`nth_root` + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: f = (x+1)/(x-1) # optional - sage.rings.finite_rings + sage: f.is_nth_power(1) # optional - sage.rings.finite_rings + True + sage: f.is_nth_power(3) # optional - sage.rings.finite_rings + False + sage: (f^3).is_nth_power(3) # optional - sage.rings.finite_rings + True + sage: (f^9).is_nth_power(-9) # optional - sage.rings.finite_rings + True + """ + if n == 1: + return True + if n < 0: + return (~self).is_nth_power(-n) + + p = self._parent.characteristic() + if n == p: + return self._parent.derivation()(self).is_zero() + if p.divides(n): + return self.is_nth_power(p) and self.nth_root(p).is_nth_power(n//p) + if n == 2: + return self.is_square() + + raise NotImplementedError("is_nth_power() not implemented for the given n") + + cpdef FunctionFieldElement nth_root(self, n): + r""" + Return an ``n``-th root of this element in the function field. + + INPUT: + + - ``n`` -- an integer + + OUTPUT: + + Returns an element ``a`` in the rational function field such that this + element equals `a^n`. Raises an error if no such element exists. + + ALGORITHM: + + If ``n`` is a power of the characteristic of the field and the constant + base field is perfect, then this uses the algorithm described in + Corollary 3 of [GiTr1996]_. + + .. SEEALSO:: + + :meth:`is_nth_power` + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: f = (x+1)/(x+2) # optional - sage.rings.finite_rings + sage: f.nth_root(1) # optional - sage.rings.finite_rings + (x + 1)/(x + 2) + sage: f.nth_root(3) # optional - sage.rings.finite_rings + Traceback (most recent call last): + ... + ValueError: element is not an n-th power + sage: (f^3).nth_root(3) # optional - sage.rings.finite_rings + (x + 1)/(x + 2) + sage: (f^9).nth_root(-9) # optional - sage.rings.finite_rings + (x + 2)/(x + 1) + """ + if n == 0: + if not self.is_one(): + raise ValueError("element is not a 0-th power") + return self + if n == 1: + return self + if n < 0: + return (~self).nth_root(-n) + p = self._parent.characteristic() + if p.divides(n): + if not self.is_nth_power(p): + raise ValueError("element is not an n-th power") + return self._parent(self.numerator().nth_root(p) / self.denominator().nth_root(p)).nth_root(n//p) + if n == 2: + return self.sqrt() + + raise NotImplementedError("nth_root() not implemented for {}".format(n)) + + def factor(self): + """ + Factor the rational function. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: f = (t+1) / (t^2 - 1/3) + sage: f.factor() # optional - sage.rings.finite_rings + (t + 1) * (t^2 - 1/3)^-1 + sage: (7*f).factor() # optional - sage.rings.finite_rings + (7) * (t + 1) * (t^2 - 1/3)^-1 + sage: ((7*f).factor()).unit() # optional - sage.rings.finite_rings + 7 + sage: (f^3).factor() # optional - sage.rings.finite_rings + (t + 1)^3 * (t^2 - 1/3)^-3 + """ + P = self.parent() + F = self._x.factor() + from sage.structure.factorization import Factorization + return Factorization([(P(a),e) for a,e in F], unit=F.unit()) + + def inverse_mod(self, I): + """ + Return an inverse of the element modulo the integral ideal `I`, if `I` + and the element together generate the unit ideal. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order(); I = O.ideal(x^2 + 1) + sage: t = O(x + 1).inverse_mod(I); t + -1/2*x + 1/2 + sage: (t*(x+1) - 1) in I + True + + """ + assert len(I.gens()) == 1 + f = I.gens()[0]._x + assert f.denominator() == 1 + assert self._x.denominator() == 1 + return self.parent()(self._x.numerator().inverse_mod(f.numerator())) diff --git a/src/sage/rings/function_field/extensions.py b/src/sage/rings/function_field/extensions.py index 0abe0346f72..10a81cc3c1e 100644 --- a/src/sage/rings/function_field/extensions.py +++ b/src/sage/rings/function_field/extensions.py @@ -11,36 +11,36 @@ Constant field extension of the rational function field over rational numbers:: sage: K. = FunctionField(QQ) - sage: N. = QuadraticField(2) - sage: L = K.extension_constant_field(N) - sage: L + sage: N. = QuadraticField(2) # optional - sage.rings.number_field + sage: L = K.extension_constant_field(N) # optional - sage.rings.number_field + sage: L # optional - sage.rings.number_field Rational function field in x over Number Field in a with defining polynomial x^2 - 2 with a = 1.4142... over its base - sage: d = (x^2 - 2).divisor() - sage: d + sage: d = (x^2 - 2).divisor() # optional - sage.rings.number_field + sage: d # optional - sage.rings.number_field -2*Place (1/x) + Place (x^2 - 2) - sage: L.conorm_divisor(d) + sage: L.conorm_divisor(d) # optional - sage.rings.number_field -2*Place (1/x) + Place (x - a) + Place (x + a) Constant field extension of a function field over a finite field:: - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: E = F.extension_constant_field(GF(2^3)) - sage: E + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E # optional - sage.rings.finite_rings sage.rings.function_field Function field in y defined by y^3 + x^6 + x^4 + x^2 over its base - sage: p = F.get_place(3) - sage: E.conorm_place(p) # random + sage: p = F.get_place(3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E.conorm_place(p) # random # optional - sage.rings.finite_rings sage.rings.function_field Place (x + z3, y + z3^2 + z3) + Place (x + z3^2, y + z3) + Place (x + z3^2 + z3, y + z3^2) - sage: q = F.get_place(2) - sage: E.conorm_place(q) # random + sage: q = F.get_place(2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E.conorm_place(q) # random # optional - sage.rings.finite_rings sage.rings.function_field Place (x + 1, y^2 + y + 1) - sage: E.conorm_divisor(p + q) # random + sage: E.conorm_divisor(p + q) # random # optional - sage.rings.finite_rings sage.rings.function_field Place (x + 1, y^2 + y + 1) + Place (x + z3, y + z3^2 + z3) + Place (x + z3^2, y + z3) @@ -52,6 +52,15 @@ """ +# **************************************************************************** +# Copyright (C) 2021-2022 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. +# https://www.gnu.org/licenses/ +# **************************************************************************** + from sage.rings.ring_extension import RingExtension_generic from .constructor import FunctionField @@ -81,10 +90,10 @@ def __init__(self, F, k_ext): TESTS:: - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: E = F.extension_constant_field(GF(2^3)) - sage: TestSuite(E).run(skip=['_test_elements', '_test_pickling']) + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(E).run(skip=['_test_elements', '_test_pickling']) # optional - sage.rings.finite_rings sage.rings.function_field """ k = F.constant_base_field() F_base = F.base_field() @@ -120,10 +129,10 @@ def top(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: E = F.extension_constant_field(GF(2^3)) - sage: E.top() + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E.top() # optional - sage.rings.finite_rings sage.rings.function_field Function field in y defined by y^3 + x^6 + x^4 + x^2 """ return self._F_ext @@ -136,10 +145,10 @@ def defining_morphism(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: E = F.extension_constant_field(GF(2^3)) - sage: E.defining_morphism() + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E.defining_morphism() # optional - sage.rings.finite_rings sage.rings.function_field Function Field morphism: From: Function field in y defined by y^3 + x^6 + x^4 + x^2 To: Function field in y defined by y^3 + x^6 + x^4 + x^2 @@ -161,16 +170,16 @@ def conorm_place(self, p): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: E = F.extension_constant_field(GF(2^3)) - sage: p = F.get_place(3) - sage: d = E.conorm_place(p) - sage: [pl.degree() for pl in d.support()] + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = F.get_place(3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: d = E.conorm_place(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: [pl.degree() for pl in d.support()] # optional - sage.rings.finite_rings sage.rings.function_field [1, 1, 1] - sage: p = F.get_place(2) - sage: d = E.conorm_place(p) - sage: [pl.degree() for pl in d.support()] + sage: p = F.get_place(2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: d = E.conorm_place(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: [pl.degree() for pl in d.support()] # optional - sage.rings.finite_rings sage.rings.function_field [2] """ embedF = self.defining_morphism() @@ -197,15 +206,15 @@ def conorm_divisor(self, d): EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: E = F.extension_constant_field(GF(2^3)) - sage: p1 = F.get_place(3) - sage: p2 = F.get_place(2) - sage: c = E.conorm_divisor(2*p1+ 3*p2) - sage: c1 = E.conorm_place(p1) - sage: c2 = E.conorm_place(p2) - sage: c == 2*c1 + 3*c2 + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E = F.extension_constant_field(GF(2^3)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1 = F.get_place(3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p2 = F.get_place(2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: c = E.conorm_divisor(2*p1 + 3*p2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: c1 = E.conorm_place(p1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: c2 = E.conorm_place(p2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: c == 2*c1 + 3*c2 # optional - sage.rings.finite_rings sage.rings.function_field True """ div_top = self.divisor_group() diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 60b834221ba..e89ea118106 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Function Fields @@ -10,122 +9,122 @@ We create a rational function field:: - sage: K. = FunctionField(GF(5^2,'a')); K + sage: K. = FunctionField(GF(5^2,'a')); K # optional - sage.rings.finite_rings Rational function field in x over Finite Field in a of size 5^2 - sage: K.genus() + sage: K.genus() # optional - sage.rings.finite_rings 0 - sage: f = (x^2 + x + 1) / (x^3 + 1) - sage: f + sage: f = (x^2 + x + 1) / (x^3 + 1) # optional - sage.rings.finite_rings + sage: f # optional - sage.rings.finite_rings (x^2 + x + 1)/(x^3 + 1) - sage: f^3 + sage: f^3 # optional - sage.rings.finite_rings (x^6 + 3*x^5 + x^4 + 2*x^3 + x^2 + 3*x + 1)/(x^9 + 3*x^6 + 3*x^3 + 1) Then we create an extension of the rational function field, and do some simple arithmetic in it:: - sage: R. = K[] - sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)); L + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)); L # optional - sage.rings.finite_rings sage.rings.function_field Function field in y defined by y^3 + 3*x*y + (4*x^4 + 4)/x - sage: y^2 + sage: y^2 # optional - sage.rings.finite_rings sage.rings.function_field y^2 - sage: y^3 + sage: y^3 # optional - sage.rings.finite_rings sage.rings.function_field 2*x*y + (x^4 + 1)/x - sage: a = 1/y; a + sage: a = 1/y; a # optional - sage.rings.finite_rings sage.rings.function_field (x/(x^4 + 1))*y^2 + 3*x^2/(x^4 + 1) - sage: a * y + sage: a * y # optional - sage.rings.finite_rings sage.rings.function_field 1 We next make an extension of the above function field, illustrating that arithmetic with a tower of three fields is fully supported:: - sage: S. = L[] - sage: M. = L.extension(t^2 - x*y) - sage: M + sage: S. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(t^2 - x*y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: M # optional - sage.rings.finite_rings sage.rings.function_field Function field in t defined by t^2 + 4*x*y - sage: t^2 + sage: t^2 # optional - sage.rings.finite_rings sage.rings.function_field x*y - sage: 1/t + sage: 1/t # optional - sage.rings.finite_rings sage.rings.function_field ((1/(x^4 + 1))*y^2 + 3*x/(x^4 + 1))*t - sage: M.base_field() + sage: M.base_field() # optional - sage.rings.finite_rings sage.rings.function_field Function field in y defined by y^3 + 3*x*y + (4*x^4 + 4)/x - sage: M.base_field().base_field() + sage: M.base_field().base_field() # optional - sage.rings.finite_rings sage.rings.function_field Rational function field in x over Finite Field in a of size 5^2 It is also possible to construct function fields over an imperfect base field:: - sage: N. = FunctionField(K) + sage: N. = FunctionField(K) # optional - sage.rings.finite_rings and inseparable extension function fields:: - sage: J. = FunctionField(GF(5)); J + sage: J. = FunctionField(GF(5)); J # optional - sage.rings.finite_rings Rational function field in x over Finite Field of size 5 - sage: T. = J[] - sage: O. = J.extension(v^5 - x); O + sage: T. = J[] # optional - sage.rings.finite_rings + sage: O. = J.extension(v^5 - x); O # optional - sage.rings.finite_rings sage.rings.function_field Function field in v defined by v^5 + 4*x Function fields over the rational field are supported:: sage: F. = FunctionField(QQ) sage: R. = F[] - sage: L. = F.extension(Y^2 - x^8 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal(x, y - 1) - sage: P = I.place() - sage: D = P.divisor() - sage: D.basis_function_space() + sage: L. = F.extension(Y^2 - x^8 - 1) # optional - sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.function_field + sage: I = O.ideal(x, y - 1) # optional - sage.rings.function_field + sage: P = I.place() # optional - sage.rings.function_field + sage: D = P.divisor() # optional - sage.rings.function_field + sage: D.basis_function_space() # optional - sage.rings.function_field [1] - sage: (2*D).basis_function_space() + sage: (2*D).basis_function_space() # optional - sage.rings.function_field [1] - sage: (3*D).basis_function_space() + sage: (3*D).basis_function_space() # optional - sage.rings.function_field [1] - sage: (4*D).basis_function_space() + sage: (4*D).basis_function_space() # optional - sage.rings.function_field [1, 1/x^4*y + 1/x^4] sage: K. = FunctionField(QQ); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: O = F.maximal_order() - sage: I = O.ideal(y) - sage: I.divisor() + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: I.divisor() # optional - sage.rings.function_field 2*Place (x, y, (1/(x^3 + x^2 + x))*y^2) + 2*Place (x^2 + x + 1, y, (1/(x^3 + x^2 + x))*y^2) sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: I.divisor() + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: I.divisor() # optional - sage.rings.function_field - Place (x, x*y) + Place (x^2 + 1, x*y) Function fields over the algebraic field are supported:: - sage: K. = FunctionField(QQbar); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: I.divisor() + sage: K. = FunctionField(QQbar); _. = K[] # optional - sage.rings.number_field + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.function_field sage.rings.number_field + sage: O = L.maximal_order() # optional - sage.rings.function_field sage.rings.number_field + sage: I = O.ideal(y) # optional - sage.rings.function_field sage.rings.number_field + sage: I.divisor() # optional - sage.rings.function_field sage.rings.number_field Place (x - I, x*y) - Place (x, x*y) + Place (x + I, x*y) - sage: pl = I.divisor().support()[0] - sage: m = L.completion(pl, prec=5) - sage: m(x) + sage: pl = I.divisor().support()[0] # optional - sage.rings.function_field sage.rings.number_field + sage: m = L.completion(pl, prec=5) # optional - sage.rings.function_field sage.rings.number_field + sage: m(x) # optional - sage.rings.function_field sage.rings.number_field I + s + O(s^5) - sage: m(y) # long time (4s) + sage: m(y) # long time (4s) # optional - sage.rings.function_field sage.rings.number_field -2*s + (-4 - I)*s^2 + (-15 - 4*I)*s^3 + (-75 - 23*I)*s^4 + (-413 - 154*I)*s^5 + O(s^6) - sage: m(y)^2 + m(y) + m(x) + 1/m(x) # long time (8s) + sage: m(y)^2 + m(y) + m(x) + 1/m(x) # long time (8s) # optional - sage.rings.function_field sage.rings.number_field O(s^5) TESTS:: - sage: TestSuite(J).run() - sage: TestSuite(K).run(max_runs=256) # long time (10s) - sage: TestSuite(L).run(max_runs=8) # long time (25s) + sage: TestSuite(J).run() # optional - sage.rings.finite_rings + sage: TestSuite(K).run(max_runs=256) # long time (10s) # optional - sage.rings.number_field + sage: TestSuite(L).run(max_runs=8) # long time (25s) # optional - sage.rings.function_field sage.rings.number_field sage: TestSuite(M).run(max_runs=8) # long time (35s) sage: TestSuite(N).run(max_runs=8, skip = '_test_derivation') # long time (15s) - sage: TestSuite(O).run() + sage: TestSuite(O).run() # optional - sage.rings.function_field sage.rings.number_field sage: TestSuite(R).run() - sage: TestSuite(S).run() # long time (4s) + sage: TestSuite(S).run() # long time (4s) # optional - sage.rings.finite_rings sage.rings.function_field Global function fields ---------------------- @@ -140,31 +139,31 @@ of its maximal order and maximal infinite order, and then do arithmetic with ideals of those maximal orders:: - sage: K. = FunctionField(GF(3)); _. = K[] - sage: L. = K.extension(t^4 + t - x^5) - sage: O = L.maximal_order() - sage: O.basis() + sage: K. = FunctionField(GF(3)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.basis() # optional - sage.rings.finite_rings sage.rings.function_field (1, y, 1/x*y^2 + 1/x*y, 1/x^3*y^3 + 2/x^3*y^2 + 1/x^3*y) - sage: I = O.ideal(x,y); I + sage: I = O.ideal(x,y); I # optional - sage.rings.finite_rings sage.rings.function_field Ideal (x, y) of Maximal order of Function field in y defined by y^4 + y + 2*x^5 - sage: J = I^-1 - sage: J.basis_matrix() + sage: J = I^-1 # optional - sage.rings.finite_rings sage.rings.function_field + sage: J.basis_matrix() # optional - sage.rings.finite_rings sage.rings.function_field [ 1 0 0 0] [1/x 1/x 0 0] [ 0 0 1 0] [ 0 0 0 1] - sage: L.maximal_order_infinite().basis() + sage: L.maximal_order_infinite().basis() # optional - sage.rings.finite_rings sage.rings.function_field (1, 1/x^2*y, 1/x^3*y^2, 1/x^4*y^3) As an example of the most sophisticated computations that Sage can do with a global function field, we compute all the Weierstrass places of the Klein quartic over `\GF{2}` and gap numbers for ordinary places:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: L.genus() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.genus() # optional - sage.rings.finite_rings sage.rings.function_field 3 - sage: L.weierstrass_places() + sage: L.weierstrass_places() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field [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), @@ -175,17 +174,17 @@ Place (x^3 + x^2 + 1, y + x), Place (x^3 + x^2 + 1, y + x^2 + 1), Place (x^3 + x^2 + 1, y + x^2 + x + 1)] - sage: L.gaps() + sage: L.gaps() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field [1, 2, 3] The gap numbers for Weierstrass places are of course not ordinary:: - sage: p1,p2,p3 = L.weierstrass_places()[:3] - sage: p1.gaps() + sage: p1,p2,p3 = L.weierstrass_places()[:3] # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + sage: p1.gaps() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field [1, 2, 4] - sage: p2.gaps() + sage: p2.gaps() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field [1, 2, 4] - sage: p3.gaps() + sage: p3.gaps() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field [1, 2, 4] AUTHORS: @@ -210,40 +209,37 @@ - Brent Baccala (2019-12-20): added function fields over number fields and QQbar """ + # **************************************************************************** -# Copyright (C) 2010 William Stein -# Copyright (C) 2010 Robert Bradshaw -# Copyright (C) 2011-2018 Julian Rüth -# Copyright (C) 2011 Maarten Derickx +# Copyright (C) 2010 William Stein +# 2010 Robert Bradshaw +# 2011-2018 Julian Rüth +# 2011 Maarten Derickx +# 2011 Syed Ahmad Lavasani +# 2013-2014 Simon King +# 2017 Dean Bisogno +# 2017 Alyson Deines +# 2017-2019 David Roe +# 2017-2022 Kwankyu Lee +# 2018 Marc Mezzarobba +# 2018 Wilfried Luebbe +# 2019 Brent Baccala +# 2022 Frédéric Chapoton +# 2022 Gonzalo Tornaría # # 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. # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.misc.cachefunc import cached_method - -from sage.interfaces.singular import singular - -from sage.arith.functions import lcm -from sage.rings.integer import Integer +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_import import LazyImport from sage.rings.ring import Field -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.qqbar_decorators import handle_AA_and_QQbar - -from sage.modules.free_module_element import vector - from sage.categories.homset import Hom from sage.categories.function_fields import FunctionFields from sage.structure.category_object import CategoryObject -from .differential import DifferentialsSpace, DifferentialsSpace_global - -from .element import ( - FunctionFieldElement, - FunctionFieldElement_rational, - FunctionFieldElement_polymod) def is_FunctionField(x): """ @@ -278,7 +274,7 @@ class FunctionField(Field): sage: K Rational function field in x over Rational Field """ - _differentials_space = DifferentialsSpace + _differentials_space = LazyImport('sage.rings.function_field.differential', 'DifferentialsSpace') def __init__(self, base_field, names, category=FunctionFields()): """ @@ -307,7 +303,7 @@ def is_perfect(self): sage: FunctionField(QQ, 'x').is_perfect() True - sage: FunctionField(GF(2), 'x').is_perfect() + sage: FunctionField(GF(2), 'x').is_perfect() # optional - sage.rings.finite_rings False """ return self.characteristic() == 0 @@ -335,8 +331,8 @@ def some_elements(self): :: sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: L.some_elements() + sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: L.some_elements() # optional - sage.rings.function_field [1, y, 1/x*y, @@ -370,15 +366,15 @@ def characteristic(self): sage: K. = FunctionField(QQ) sage: K.characteristic() 0 - sage: K. = FunctionField(QQbar) - sage: K.characteristic() + sage: K. = FunctionField(QQbar) # optional - sage.rings.number_field + sage: K.characteristic() # optional - sage.rings.number_field 0 - sage: K. = FunctionField(GF(7)) - sage: K.characteristic() + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: K.characteristic() # optional - sage.rings.finite_rings 7 - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: L.characteristic() + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.characteristic() # optional - sage.rings.finite_rings sage.rings.function_field 7 """ return self.constant_base_field().characteristic() @@ -392,8 +388,8 @@ def is_finite(self): sage: R. = FunctionField(QQ) sage: R.is_finite() False - sage: R. = FunctionField(GF(7)) - sage: R.is_finite() + sage: R. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: R.is_finite() # optional - sage.rings.finite_rings False """ return False @@ -408,11 +404,11 @@ def is_global(self): sage: R. = FunctionField(QQ) sage: R.is_global() False - sage: R. = FunctionField(QQbar) - sage: R.is_global() + sage: R. = FunctionField(QQbar) # optional - sage.rings.number_field + sage: R.is_global() # optional - sage.rings.number_field False - sage: R. = FunctionField(GF(7)) - sage: R.is_global() + sage: R. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: R.is_global() # optional - sage.rings.finite_rings True """ return self.constant_base_field().is_finite() @@ -435,18 +431,18 @@ def extension(self, f, names=None): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: K.extension(y^5 - x^3 - 3*x + x*y) + sage: K.extension(y^5 - x^3 - 3*x + x*y) # optional - sage.rings.function_field Function field in y defined by y^5 + x*y - x^3 - 3*x A nonintegral defining polynomial:: sage: K. = FunctionField(QQ); R. = K[] - sage: K.extension(y^3 + (1/t)*y + t^3/(t+1), 'z') + sage: K.extension(y^3 + (1/t)*y + t^3/(t+1), 'z') # optional - sage.rings.function_field Function field in z defined by z^3 + 1/t*z + t^3/(t + 1) The defining polynomial need not be monic or integral:: - sage: K.extension(t*y^3 + (1/t)*y + t^3/(t+1)) + sage: K.extension(t*y^3 + (1/t)*y + t^3/(t+1)) # optional - sage.rings.function_field Function field in y defined by t*y^3 + 1/t*y + t^3/(t + 1) """ from . import constructor @@ -471,34 +467,35 @@ def order_with_basis(self, basis, check=True): EXAMPLES:: - sage: K. = FunctionField(QQ); R. = K[]; L. = K.extension(y^3 + x^3 + 4*x + 1) - sage: O = L.order_with_basis([1, y, y^2]); O + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field + sage: O = L.order_with_basis([1, y, y^2]); O # optional - sage.rings.function_field Order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() + sage: O.basis() # optional - sage.rings.function_field (1, y, y^2) Note that 1 does not need to be an element of the basis, as long it is in the module spanned by it:: - sage: O = L.order_with_basis([1+y, y, y^2]); O + sage: O = L.order_with_basis([1+y, y, y^2]); O # optional - sage.rings.function_field Order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() + sage: O.basis() # optional - sage.rings.function_field (y + 1, y, y^2) The following error is raised when the module spanned by the basis is not closed under multiplication:: - sage: O = L.order_with_basis([1, x^2 + x*y, (2/3)*y^2]); O + sage: O = L.order_with_basis([1, x^2 + x*y, (2/3)*y^2]); O # optional - sage.rings.function_field Traceback (most recent call last): ... ValueError: the module generated by basis (1, x*y + x^2, 2/3*y^2) must be closed under multiplication and this happens when the identity is not in the module spanned by the basis:: - sage: O = L.order_with_basis([x, x^2 + x*y, (2/3)*y^2]) + sage: O = L.order_with_basis([x, x^2 + x*y, (2/3)*y^2]) # optional - sage.rings.function_field Traceback (most recent call last): ... ValueError: the identity element must be in the module spanned by basis (x, x*y + x^2, 2/3*y^2) """ - from .order import FunctionFieldOrder_basis + from .order_basis import FunctionFieldOrder_basis return FunctionFieldOrder_basis(tuple([self(a) for a in basis]), check=check) def order(self, x, check=True): @@ -514,20 +511,20 @@ def order(self, x, check=True): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) - sage: O = L.order(y); O + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field + sage: O = L.order(y); O # optional - sage.modules sage.rings.function_field Order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() + sage: O.basis() # optional - sage.modules sage.rings.function_field (1, y, y^2) - sage: Z = K.order(x); Z + sage: Z = K.order(x); Z # optional - sage.modules Order in Rational function field in x over Rational Field - sage: Z.basis() + sage: Z.basis() # optional - sage.modules (1,) Orders with multiple generators are not yet supported:: - sage: Z = K.order([x,x^2]); Z + sage: Z = K.order([x, x^2]); Z # optional - sage.modules Traceback (most recent call last): ... NotImplementedError @@ -559,24 +556,24 @@ def order_infinite_with_basis(self, basis, check=True): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) - sage: O = L.order_infinite_with_basis([1, 1/x*y, 1/x^2*y^2]); O + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field + sage: O = L.order_infinite_with_basis([1, 1/x*y, 1/x^2*y^2]); O # optional - sage.rings.function_field Infinite order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() + sage: O.basis() # optional - sage.rings.function_field (1, 1/x*y, 1/x^2*y^2) Note that 1 does not need to be an element of the basis, as long it is in the module spanned by it:: - sage: O = L.order_infinite_with_basis([1+1/x*y,1/x*y, 1/x^2*y^2]); O + sage: O = L.order_infinite_with_basis([1+1/x*y,1/x*y, 1/x^2*y^2]); O # optional - sage.rings.function_field Infinite order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: O.basis() + sage: O.basis() # optional - sage.rings.function_field (1/x*y + 1, 1/x*y, 1/x^2*y^2) The following error is raised when the module spanned by the basis is not closed under multiplication:: - sage: O = L.order_infinite_with_basis([1,y, 1/x^2*y^2]); O + sage: O = L.order_infinite_with_basis([1,y, 1/x^2*y^2]); O # optional - sage.rings.function_field Traceback (most recent call last): ... ValueError: the module generated by basis (1, y, 1/x^2*y^2) must be closed under multiplication @@ -584,12 +581,12 @@ def order_infinite_with_basis(self, basis, check=True): and this happens when the identity is not in the module spanned by the basis:: - sage: O = L.order_infinite_with_basis([1/x,1/x*y, 1/x^2*y^2]) + sage: O = L.order_infinite_with_basis([1/x,1/x*y, 1/x^2*y^2]) # optional - sage.rings.function_field Traceback (most recent call last): ... ValueError: the identity element must be in the module spanned by basis (1/x, 1/x*y, 1/x^2*y^2) """ - from .order import FunctionFieldOrderInfinite_basis + from .order_basis import FunctionFieldOrderInfinite_basis return FunctionFieldOrderInfinite_basis(tuple([self(g) for g in basis]), check=check) def order_infinite(self, x, check=True): @@ -605,17 +602,17 @@ def order_infinite(self, x, check=True): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) - sage: L.order_infinite(y) # todo: not implemented + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field + sage: L.order_infinite(y) # todo: not implemented # optional - sage.modules sage.rings.function_field - sage: Z = K.order(x); Z + sage: Z = K.order(x); Z # optional - sage.modules Order in Rational function field in x over Rational Field - sage: Z.basis() + sage: Z.basis() # optional - sage.modules (1,) Orders with multiple generators, not yet supported:: - sage: Z = K.order_infinite([x,x^2]); Z + sage: Z = K.order_infinite([x, x^2]); Z # optional - sage.modules Traceback (most recent call last): ... NotImplementedError @@ -641,34 +638,35 @@ def _coerce_map_from_(self, source): EXAMPLES:: - sage: K. = FunctionField(QQ); R. = K[]; L. = K.extension(y^3 + x^3 + 4*x + 1) - sage: L.equation_order() + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field + sage: L.equation_order() # optional - sage.rings.function_field Order in Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: L._coerce_map_from_(L.equation_order()) + sage: L._coerce_map_from_(L.equation_order()) # optional - sage.rings.function_field Conversion map: From: Order in Function field in y defined by y^3 + x^3 + 4*x + 1 To: Function field in y defined by y^3 + x^3 + 4*x + 1 - sage: L._coerce_map_from_(GF(7)) + sage: L._coerce_map_from_(GF(7)) # optional - sage.rings.finite_rings sage.rings.function_field sage: K. = FunctionField(QQ) - sage: L. = FunctionField(GaussianIntegers().fraction_field()) - sage: L.has_coerce_map_from(K) + sage: L. = FunctionField(GaussianIntegers().fraction_field()) # optional - sage.rings.number_field + sage: L.has_coerce_map_from(K) # optional - sage.rings.number_field True sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^3 + 1) - sage: K. = FunctionField(GaussianIntegers().fraction_field()) - sage: R. = K[] - sage: M. = K.extension(y^3 + 1) - sage: M.has_coerce_map_from(L) # not tested (the constant field including into a function field is not yet known to be injective) + sage: L. = K.extension(y^3 + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: K. = FunctionField(GaussianIntegers().fraction_field()) # optional - sage.rings.number_field + sage: R. = K[] # optional - sage.rings.number_field + sage: M. = K.extension(y^3 + 1) # optional - sage.rings.function_field + sage: M.has_coerce_map_from(L) # not tested (the constant field including into a function field is not yet known to be injective) # optional - sage.rings.function_field True sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(I^2 + 1) - sage: M. = FunctionField(GaussianIntegers().fraction_field()) - sage: M.has_coerce_map_from(L) + sage: L. = K.extension(I^2 + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: M. = FunctionField(GaussianIntegers().fraction_field()) # optional - sage.rings.number_field + sage: M.has_coerce_map_from(L) # optional - sage.rings.finite_rings sage.rings.function_field sage.rings.number_field True Check that :trac:`31072` is fixed:: @@ -766,10 +764,14 @@ def _convert_map_from_(self, R): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^3 + x^3 + 4*x + 1) - sage: K(L(x)) # indirect doctest + sage: L. = K.extension(y^3 + x^3 + 4*x + 1) # optional - sage.rings.function_field + sage: K(L(x)) # indirect doctest # optional - sage.rings.function_field x """ + try: + from .function_field_polymod import FunctionField_polymod + except ImportError: + FunctionField_polymod = () if isinstance(R, FunctionField_polymod): base_conversion = self.convert_map_from(R.base_field()) if base_conversion is not None: @@ -797,25 +799,28 @@ def _intermediate_fields(self, base): [Rational function field in x over Rational Field] sage: R. = K[] - sage: L. = K.extension(y^2-x) - sage: L._intermediate_fields(K) - [Function field in y defined by y^2 - x, Rational function field in x over Rational Field] - - sage: R. = L[] - sage: M. = L.extension(z^2-y) - sage: M._intermediate_fields(L) - [Function field in z defined by z^2 - y, Function field in y defined by y^2 - x] - sage: M._intermediate_fields(K) - [Function field in z defined by z^2 - y, Function field in y defined by y^2 - x, + sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: L._intermediate_fields(K) # optional - sage.rings.function_field + [Function field in y defined by y^2 - x, + Rational function field in x over Rational Field] + + sage: R. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z^2 - y) # optional - sage.rings.function_field + sage: M._intermediate_fields(L) # optional - sage.rings.function_field + [Function field in z defined by z^2 - y, + Function field in y defined by y^2 - x] + sage: M._intermediate_fields(K) # optional - sage.rings.function_field + [Function field in z defined by z^2 - y, + Function field in y defined by y^2 - x, Rational function field in x over Rational Field] TESTS:: - sage: K._intermediate_fields(M) + sage: K._intermediate_fields(M) # optional - sage.rings.function_field Traceback (most recent call last): ... ValueError: field has not been constructed as a finite extension of base - sage: K._intermediate_fields(QQ) + sage: K._intermediate_fields(QQ) # optional - sage.rings.function_field Traceback (most recent call last): ... TypeError: base must be a function field @@ -842,15 +847,17 @@ def rational_function_field(self): Rational function field in x over Rational Field sage: R. = K[] - sage: L. = K.extension(y^2-x) - sage: L.rational_function_field() + sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: L.rational_function_field() # optional - sage.rings.function_field Rational function field in x over Rational Field - sage: R. = L[] - sage: M. = L.extension(z^2-y) - sage: M.rational_function_field() + sage: R. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z^2 - y) # optional - sage.rings.function_field + sage: M.rational_function_field() # optional - sage.rings.function_field Rational function field in x over Rational Field """ + from .function_field_rational import RationalFunctionField + return self if isinstance(self, RationalFunctionField) else self.base_field().rational_function_field() def valuation(self, prime): @@ -870,48 +877,49 @@ def valuation(self, prime): function field:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(1); v + sage: v = K.valuation(1); v # optional - sage.rings.function_field (x - 1)-adic valuation - sage: v(x) + sage: v(x) # optional - sage.rings.function_field 0 - sage: v(x - 1) + sage: v(x - 1) # optional - sage.rings.function_field 1 A place can also be specified with an irreducible polynomial:: - sage: v = K.valuation(x - 1); v + sage: v = K.valuation(x - 1); v # optional - sage.rings.function_field (x - 1)-adic valuation Similarly, for a finite non-rational place:: - sage: v = K.valuation(x^2 + 1); v + sage: v = K.valuation(x^2 + 1); v # optional - sage.rings.function_field (x^2 + 1)-adic valuation - sage: v(x^2 + 1) + sage: v(x^2 + 1) # optional - sage.rings.function_field 1 - sage: v(x) + sage: v(x) # optional - sage.rings.function_field 0 Or for the infinite place:: - sage: v = K.valuation(1/x); v + sage: v = K.valuation(1/x); v # optional - sage.rings.function_field Valuation at the infinite place - sage: v(x) + sage: v(x) # optional - sage.rings.function_field -1 Instead of specifying a generator of a place, we can define a valuation on a rational function field by giving a discrete valuation on the underlying polynomial ring:: - sage: R. = QQ[] - sage: w = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x - 1, 1) - sage: v = K.valuation(w); v + sage: R. = QQ[] # optional - sage.rings.function_field + sage: u = valuations.GaussValuation(R, valuations.TrivialValuation(QQ)) # optional - sage.rings.function_field + sage: w = u.augmentation(x - 1, 1) # optional - sage.rings.function_field + sage: v = K.valuation(w); v # optional - sage.rings.function_field (x - 1)-adic valuation Note that this allows us to specify valuations which do not correspond to a place of the function field:: - sage: w = valuations.GaussValuation(R, QQ.valuation(2)) - sage: v = K.valuation(w); v + sage: w = valuations.GaussValuation(R, QQ.valuation(2)) # optional - sage.rings.function_field + sage: v = K.valuation(w); v # optional - sage.rings.function_field 2-adic valuation The same is possible for valuations with `v(1/x) > 0` by passing in an @@ -921,18 +929,20 @@ def valuation(self, prime): applying the substitution `x \mapsto 1/x` (here, the inverse map is also `x \mapsto 1/x`):: - sage: w = valuations.GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) - sage: w = K.valuation(w) - sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v - Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) + sage: w = valuations.GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) # optional - sage.rings.function_field + sage: w = K.valuation(w) # optional - sage.rings.function_field + sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v # optional - sage.rings.function_field + Valuation on rational function field + induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] + (in Rational function field in x over Rational Field after x |--> 1/x) Note that classical valuations at finite places or the infinite place are always normalized such that the uniformizing element has valuation 1:: - sage: K. = FunctionField(GF(3)) - sage: M. = FunctionField(K) - sage: v = M.valuation(x^3 - t) - sage: v(x^3 - t) + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: M. = FunctionField(K) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = M.valuation(x^3 - t) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v(x^3 - t) # optional - sage.rings.finite_rings sage.rings.function_field 1 However, if such a valuation comes out of a base change of the ground @@ -940,16 +950,16 @@ def valuation(self, prime): extension of ``v`` to ``L`` still has valuation 1 on `x^3 - t` but it has valuation ``1/3`` on its uniformizing element `x - w`:: - sage: R. = K[] - sage: L. = K.extension(w^3 - t) - sage: N. = FunctionField(L) - sage: w = v.extension(N) # missing factorization, :trac:`16572` + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(w^3 - t) # optional - sage.rings.finite_rings sage.rings.function_field + sage: N. = FunctionField(L) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = v.extension(N) # missing factorization, :trac:`16572` # optional - sage.rings.finite_rings sage.rings.function_field Traceback (most recent call last): ... NotImplementedError - sage: w(x^3 - t) # not tested + sage: w(x^3 - t) # not tested # optional - sage.rings.finite_rings sage.rings.function_field 1 - sage: w(x - w) # not tested + sage: w(x - w) # not tested # optional - sage.rings.finite_rings sage.rings.function_field 1/3 There are several ways to create valuations on extensions of rational @@ -957,16 +967,16 @@ def valuation(self, prime): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x); L + sage: L. = K.extension(y^2 - x); L # optional - sage.rings.function_field Function field in y defined by y^2 - x A place that has a unique extension can just be defined downstairs:: - sage: v = L.valuation(x); v + sage: v = L.valuation(x); v # optional - sage.rings.function_field (x)-adic valuation """ - from sage.rings.function_field.function_field_valuation import FunctionFieldValuation + from sage.rings.function_field.valuation import FunctionFieldValuation return FunctionFieldValuation(self, prime) def space_of_differentials(self): @@ -976,13 +986,14 @@ def space_of_differentials(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.space_of_differentials() + sage: K.space_of_differentials() # optional - sage.modules Space of differentials of Rational function field in t over Rational Field - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L.space_of_differentials() - Space of differentials of Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.space_of_differentials() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + Space of differentials of Function field in y + defined by y^3 + (4*x^3 + 1)/(x^3 + 3) """ return self._differentials_space(self) @@ -993,7 +1004,7 @@ def space_of_holomorphic_differentials(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.space_of_holomorphic_differentials() + sage: K.space_of_holomorphic_differentials() # optional - sage.modules (Vector space of dimension 0 over Rational Field, Linear map: From: Vector space of dimension 0 over Rational Field @@ -1002,15 +1013,17 @@ def space_of_holomorphic_differentials(self): From: Space of differentials of Rational function field in t over Rational Field To: Vector space of dimension 0 over Rational Field) - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L.space_of_holomorphic_differentials() + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.space_of_holomorphic_differentials() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field (Vector space of dimension 4 over Finite Field of size 5, Linear map: From: Vector space of dimension 4 over Finite Field of size 5 - To: Space of differentials of Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3), + To: Space of differentials of Function field in y + defined by y^3 + (4*x^3 + 1)/(x^3 + 3), Section of linear map: - From: Space of differentials of Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) + From: Space of differentials of Function field in y + defined by y^3 + (4*x^3 + 1)/(x^3 + 3) To: Vector space of dimension 4 over Finite Field of size 5) """ return self.divisor_group().zero().differential_space() @@ -1024,12 +1037,12 @@ def basis_of_holomorphic_differentials(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.basis_of_holomorphic_differentials() + sage: K.basis_of_holomorphic_differentials() # optional - sage.modules [] - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L.basis_of_holomorphic_differentials() + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.basis_of_holomorphic_differentials() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field [((x/(x^3 + 4))*y) d(x), ((1/(x^3 + 4))*y) d(x), ((x/(x^3 + 4))*y^2) d(x), @@ -1046,17 +1059,17 @@ def divisor_group(self): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: K.divisor_group() + sage: K.divisor_group() # optional - sage.modules Divisor group of Rational function field in t over Rational Field sage: _. = K[] - sage: L. = K.extension(Y^3 - (t^3 - 1)/(t^3 - 2)) - sage: L.divisor_group() + sage: L. = K.extension(Y^3 - (t^3 - 1)/(t^3 - 2)) # optional - sage.rings.function_field + sage: L.divisor_group() # optional - sage.modules sage.rings.function_field Divisor group of Function field in y defined by y^3 + (-t^3 + 1)/(t^3 - 2) - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L.divisor_group() + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.divisor_group() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field Divisor group of Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) """ from .divisor import DivisorGroup @@ -1068,17 +1081,17 @@ def place_set(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) - sage: K.place_set() + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: K.place_set() # optional - sage.rings.finite_rings Set of places of Rational function field in t over Finite Field of size 7 sage: K. = FunctionField(QQ) sage: K.place_set() Set of places of Rational function field in t over Rational Field - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: L.place_set() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field Set of places of Function field in y defined by y^2 + y + (x^2 + 1)/x """ from .place import PlaceSet @@ -1102,82 +1115,83 @@ def completion(self, place, name=None, prec=None, gen_name=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p); m + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p); m # optional - sage.rings.finite_rings sage.rings.function_field Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(x,10) + sage: m(x, 10) # optional - sage.rings.finite_rings sage.rings.function_field s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + s^9 + s^10 + O(s^12) - sage: m(y,10) + sage: m(y, 10) # optional - sage.rings.finite_rings sage.rings.function_field s^-1 + 1 + s^3 + s^5 + s^7 + O(s^9) - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p); m + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p); m # optional - sage.rings.finite_rings sage.rings.function_field Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(x,10) + sage: m(x, 10) # optional - sage.rings.finite_rings sage.rings.function_field s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + s^9 + s^10 + O(s^12) - sage: m(y,10) + sage: m(y, 10) # optional - sage.rings.finite_rings sage.rings.function_field s^-1 + 1 + s^3 + s^5 + s^7 + O(s^9) - sage: K. = FunctionField(GF(2)) - sage: p = K.places_finite()[0]; p + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: p = K.places_finite()[0]; p # optional - sage.rings.finite_rings Place (x) - sage: m = K.completion(p); m + sage: m = K.completion(p); m # optional - sage.rings.finite_rings Completion map: From: Rational function field in x over Finite Field of size 2 To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(1/(x+1)) + sage: m(1/(x+1)) # optional - sage.rings.finite_rings 1 + s + s^2 + s^3 + s^4 + s^5 + s^6 + s^7 + s^8 + s^9 + s^10 + s^11 + s^12 + s^13 + s^14 + s^15 + s^16 + s^17 + s^18 + s^19 + O(s^20) - sage: p = K.place_infinite(); p + sage: p = K.place_infinite(); p # optional - sage.rings.finite_rings Place (1/x) - sage: m = K.completion(p); m + sage: m = K.completion(p); m # optional - sage.rings.finite_rings Completion map: From: Rational function field in x over Finite Field of size 2 To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(x) + sage: m(x) # optional - sage.rings.finite_rings s^-1 + O(s^19) - sage: m = K.completion(p, prec=infinity); m + sage: m = K.completion(p, prec=infinity); m # optional - sage.rings.finite_rings Completion map: From: Rational function field in x over Finite Field of size 2 To: Lazy Laurent Series Ring in s over Finite Field of size 2 - sage: f = m(x); f + sage: f = m(x); f # optional - sage.rings.finite_rings s^-1 + ... - sage: f.coefficient(100) + sage: f.coefficient(100) # optional - sage.rings.finite_rings 0 sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 - x) - sage: O = L.maximal_order() - sage: decomp = O.decomposition(K.maximal_order().ideal(x - 1)) - sage: pls = (decomp[0][0].place(), decomp[1][0].place()) - sage: m = L.completion(pls[0]); m + sage: L. = K.extension(Y^2 - x) # optional - sage.rings.function_field sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.function_field sage.rings.function_field + sage: decomp = O.decomposition(K.maximal_order().ideal(x - 1)) # optional - sage.rings.function_field sage.rings.function_field + sage: pls = (decomp[0][0].place(), decomp[1][0].place()) # optional - sage.rings.function_field sage.rings.function_field + sage: m = L.completion(pls[0]); m # optional - sage.rings.function_field sage.rings.function_field Completion map: From: Function field in y defined by y^2 - x To: Laurent Series Ring in s over Rational Field - sage: xe = m(x) - sage: ye = m(y) - sage: ye^2 - xe == 0 + sage: xe = m(x) # optional - sage.rings.function_field sage.rings.function_field + sage: ye = m(y) # optional - sage.rings.function_field sage.rings.function_field + sage: ye^2 - xe == 0 # optional - sage.rings.function_field sage.rings.function_field True - sage: decomp2 = O.decomposition(K.maximal_order().ideal(x^2 + 1)) - sage: pls2 = decomp2[0][0].place() - sage: m = L.completion(pls2); m + sage: decomp2 = O.decomposition(K.maximal_order().ideal(x^2 + 1)) # optional - sage.rings.function_field sage.rings.function_field + sage: pls2 = decomp2[0][0].place() # optional - sage.rings.function_field sage.rings.function_field + sage: m = L.completion(pls2); m # optional - sage.rings.function_field sage.rings.function_field Completion map: From: Function field in y defined by y^2 - x - To: Laurent Series Ring in s over Number Field in a with defining polynomial x^4 + 2*x^2 + 4*x + 2 - sage: xe = m(x) - sage: ye = m(y) - sage: ye^2 - xe == 0 + To: Laurent Series Ring in s over + Number Field in a with defining polynomial x^4 + 2*x^2 + 4*x + 2 + sage: xe = m(x) # optional - sage.rings.function_field sage.rings.function_field + sage: ye = m(y) # optional - sage.rings.function_field sage.rings.function_field + sage: ye^2 - xe == 0 # optional - sage.rings.function_field sage.rings.function_field True """ from .maps import FunctionFieldCompletion @@ -1193,3488 +1207,13 @@ def extension_constant_field(self, k): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(Y^2 + Y + x + 1/x) - sage: E = F.extension_constant_field(GF(2^4)) - sage: E + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E = F.extension_constant_field(GF(2^4)) # optional - sage.rings.finite_rings sage.rings.function_field + sage: E # optional - sage.rings.finite_rings sage.rings.function_field Function field in y defined by y^2 + y + (x^2 + 1)/x over its base - sage: E.constant_base_field() + sage: E.constant_base_field() # optional - sage.rings.finite_rings sage.rings.function_field Finite Field in z4 of size 2^4 """ from .extensions import ConstantFieldExtension return ConstantFieldExtension(self, k) - - -class FunctionField_polymod(FunctionField): - """ - Function fields defined by a univariate polynomial, as an extension of the - base field. - - INPUT: - - - ``polynomial`` -- univariate polynomial over a function field - - - ``names`` -- tuple of length 1 or string; variable names - - - ``category`` -- category (default: category of function fields) - - EXAMPLES: - - We make a function field defined by a degree 5 polynomial over the - rational function field over the rational numbers:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L - Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - - We next make a function field over the above nontrivial function - field L:: - - sage: S. = L[] - sage: M. = L.extension(z^2 + y*z + y); M - Function field in z defined by z^2 + y*z + y - sage: 1/z - ((-x/(x^4 + 1))*y^4 + 2*x^2/(x^4 + 1))*z - 1 - sage: z * (1/z) - 1 - - We drill down the tower of function fields:: - - sage: M.base_field() - Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - sage: M.base_field().base_field() - Rational function field in x over Rational Field - sage: M.base_field().base_field().constant_field() - Rational Field - sage: M.constant_base_field() - Rational Field - - .. WARNING:: - - It is not checked if the polynomial used to define the function field is irreducible - Hence it is not guaranteed that this object really is a field! - This is illustrated below. - - :: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(x^2 - y^2) - sage: (y - x)*(y + x) - 0 - sage: 1/(y - x) - 1 - sage: y - x == 0; y + x == 0 - False - False - """ - 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. - - EXAMPLES: - - We create an extension of a function field:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L = K.extension(y^5 - x^3 - 3*x + x*y); L - Function field in y defined by y^5 + x*y - x^3 - 3*x - sage: TestSuite(L).run(max_runs=512) # long time (15s) - - We can set the variable name, which doesn't have to be y:: - - sage: L. = K.extension(y^5 - x^3 - 3*x + x*y); L - Function field in w defined by w^5 + x*w - x^3 - 3*x - - TESTS: - - Test that :trac:`17033` is fixed:: - - sage: K. = FunctionField(QQ) - sage: R. = QQ[] - sage: M. = K.extension(x^7-x-t) - sage: M(x) - z - sage: M('z') - z - sage: M('x') - Traceback (most recent call last): - ... - TypeError: unable to evaluate 'x' in Fraction Field of Univariate - Polynomial Ring in t over Rational Field - """ - from sage.rings.polynomial.polynomial_element import Polynomial - if polynomial.parent().ngens()>1 or not isinstance(polynomial, Polynomial): - raise TypeError("polynomial must be univariate a polynomial") - if names is None: - names = (polynomial.variable_name(), ) - elif names != polynomial.variable_name(): - polynomial = polynomial.change_variable_name(names) - if polynomial.degree() <= 0: - raise ValueError("polynomial must have positive degree") - base_field = polynomial.base_ring() - if not isinstance(base_field, FunctionField): - raise TypeError("polynomial must be over a FunctionField") - - self._base_field = base_field - self._polynomial = polynomial - - FunctionField.__init__(self, base_field, names=names, - category=FunctionFields().or_subcategory(category)) - - from .place import FunctionFieldPlace_polymod - self._place_class = FunctionFieldPlace_polymod - - 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()) - - def __hash__(self): - """ - Return hash of the function field. - - The hash value is equal to the hash of the defining polynomial. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L = K.extension(y^5 - x^3 - 3*x + x*y) - sage: hash(L) == hash(L.polynomial()) - True - """ - return self._hash - - def _element_constructor_(self, x): - r""" - Make ``x`` into an element of the function field, possibly not canonically. - - INPUT: - - - ``x`` -- element - - TESTS:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L._element_constructor_(L.polynomial_ring().gen()) - y - """ - if isinstance(x, FunctionFieldElement): - return self.element_class(self, self._ring(x.element())) - return self.element_class(self, self._ring(x)) - - def gen(self, n=0): - """ - Return the `n`-th generator of the function field. By default, `n` is 0; any other - value of `n` leads to an error. The generator is the class of `y`, if we view - the function field as being presented as `K[y]/(f(y))`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L.gen() - y - sage: L.gen(1) - Traceback (most recent call last): - ... - IndexError: there is only one generator - """ - if n != 0: - raise IndexError("there is only one generator") - return self._gen - - def ngens(self): - """ - Return the number of generators of the function field over its base - field. This is by definition 1. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L.ngens() - 1 - """ - return 1 - - def _to_base_field(self, f): - r""" - Return ``f`` as an element of the :meth:`base_field`. - - INPUT: - - - ``f`` -- element of the function field which lies in the base - field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: L._to_base_field(L(x)) - x - sage: L._to_base_field(y) - Traceback (most recent call last): - ... - ValueError: y is not an element of the base field - - TESTS: - - Verify that :trac:`21872` has been resolved:: - - sage: R. = L[] - sage: M. = L.extension(z^2 - y) - - sage: M(1) in QQ - True - sage: M(y) in L - True - sage: M(x) in K - True - sage: z in K - False - """ - K = self.base_field() - if f.element().is_constant(): - return K(f.element()) - raise ValueError("%r is not an element of the base field"%(f,)) - - def _to_constant_base_field(self, f): - """ - Return ``f`` as an element of the :meth:`constant_base_field`. - - INPUT: - - - ``f`` -- element of the rational function field which is a - constant - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: L._to_constant_base_field(L(1)) - 1 - sage: L._to_constant_base_field(y) - Traceback (most recent call last): - ... - ValueError: y is not an element of the base field - - TESTS: - - Verify that :trac:`21872` has been resolved:: - - sage: L(1) in QQ - True - sage: y in QQ - False - """ - return self.base_field()._to_constant_base_field(self._to_base_field(f)) - - def monic_integral_model(self, names=None): - """ - Return a function field isomorphic to this field but which is an - extension of a rational function field with defining polynomial that is - monic and integral over the constant base field. - - INPUT: - - - ``names`` -- a string or a tuple of up to two strings (default: - ``None``), the name of the generator of the field, and the name of - the generator of the underlying rational function field (if a tuple); - if not given, then the names are chosen automatically. - - OUTPUT: - - A triple ``(F,f,t)`` where ``F`` is a function field, ``f`` is an - isomorphism from ``F`` to this field, and ``t`` is the inverse of - ``f``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(x^2*y^5 - 1/x); L - Function field in y defined by x^2*y^5 - 1/x - sage: A, from_A, to_A = L.monic_integral_model('z') - sage: A - Function field in z defined by z^5 - x^12 - sage: from_A - Function Field morphism: - From: Function field in z defined by z^5 - x^12 - To: Function field in y defined by x^2*y^5 - 1/x - Defn: z |--> x^3*y - x |--> x - sage: to_A - Function Field morphism: - From: Function field in y defined by x^2*y^5 - 1/x - To: Function field in z defined by z^5 - x^12 - Defn: y |--> 1/x^3*z - x |--> x - sage: to_A(y) - 1/x^3*z - sage: from_A(to_A(y)) - y - sage: from_A(to_A(1/y)) - x^3*y^4 - sage: from_A(to_A(1/y)) == 1/y - True - - This also works for towers of function fields:: - - sage: R. = L[] - sage: M. = L.extension(z^2*y - 1/x) - sage: M.monic_integral_model() - (Function field in z_ defined by z_^10 - x^18, Function Field morphism: - From: Function field in z_ defined by z_^10 - x^18 - To: Function field in z defined by y*z^2 - 1/x - Defn: z_ |--> x^2*z - x |--> x, Function Field morphism: - From: Function field in z defined by y*z^2 - 1/x - To: Function field in z_ defined by z_^10 - x^18 - Defn: z |--> 1/x^2*z_ - y |--> 1/x^15*z_^8 - x |--> x) - - TESTS: - - If the field is already a monic integral extension, then it is returned - unchanged:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2-x) - sage: L.monic_integral_model() - (Function field in y defined by y^2 - x, Function Field endomorphism of Function field in y defined by y^2 - x - Defn: y |--> y - x |--> x, Function Field endomorphism of Function field in y defined by y^2 - x - Defn: y |--> y - x |--> x) - - unless ``names`` does not match with the current names:: - - sage: L.monic_integral_model(names=('yy','xx')) - (Function field in yy defined by yy^2 - xx, Function Field morphism: - From: Function field in yy defined by yy^2 - xx - To: Function field in y defined by y^2 - x - Defn: yy |--> y - xx |--> x, Function Field morphism: - From: Function field in y defined by y^2 - x - To: Function field in yy defined by yy^2 - xx - Defn: y |--> yy - x |--> xx) - - """ - if names: - if not isinstance(names, tuple): - names = (names,) - if len(names) > 2: - raise ValueError("names must contain at most 2 entries") - - if self.base_field() is not self.rational_function_field(): - L,from_L,to_L = self.simple_model() - ret,ret_to_L,L_to_ret = L.monic_integral_model(names) - from_ret = ret.hom( [from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))] ) - to_ret = self.hom( [L_to_ret(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())] ) - return ret, from_ret, to_ret - else: - if self.polynomial().is_monic() and all(c.denominator().is_one() for c in self.polynomial()): - # self is already monic and integral - if names is None or names == (): - names = (self.variable_name(),) - return self.change_variable_name(names) - else: - if not names: - names = (self.variable_name()+"_",) - if len(names) == 1: - names = (names[0], self.rational_function_field().variable_name()) - - g, d = self._make_monic_integral(self.polynomial()) - K,from_K,to_K = self.base_field().change_variable_name(names[1]) - g = g.map_coefficients(to_K) - ret = K.extension(g, names=names[0]) - from_ret = ret.hom([self.gen() * d, self.base_field().gen()]) - to_ret = self.hom([ret.gen() / d, ret.base_field().gen()]) - return ret, from_ret, to_ret - - def _make_monic_integral(self, f): - """ - Return a monic integral polynomial `g` and an element `d` of the base - field such that `g(y*d)=0` where `y` is a root of `f`. - - INPUT: - - - ``f`` -- polynomial - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(x^2*y^5 - 1/x) - sage: g, d = L._make_monic_integral(L.polynomial()); g,d - (y^5 - x^12, x^3) - sage: (y*d).is_integral() - True - sage: g.is_monic() - True - sage: g(y*d) - 0 - """ - R = f.base_ring() - if not isinstance(R, RationalFunctionField): - raise NotImplementedError - - # make f monic - n = f.degree() - c = f.leading_coefficient() - if c != 1: - f = f / c - - # find lcm of denominators - # would be good to replace this by minimal... - d = lcm([b.denominator() for b in f.list() if b]) - if d != 1: - x = f.parent().gen() - g = (d**n) * f(x/d) - else: - g = f - return g, d - - def constant_field(self): - """ - Return the algebraic closure of the constant field of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y^5 - x) - sage: L.constant_field() - Traceback (most recent call last): - ... - NotImplementedError - """ - raise NotImplementedError - - def constant_base_field(self): - """ - Return the base constant field of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L - Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - sage: L.constant_base_field() - Rational Field - sage: S. = L[] - sage: M. = L.extension(z^2 - y) - sage: M.constant_base_field() - Rational Field - """ - return self.base_field().constant_base_field() - - @cached_method(key=lambda self, base: self.base_field() if base is None else base) - def degree(self, base=None): - """ - Return the degree of the function field over the function field ``base``. - - INPUT: - - - ``base`` -- a function field (default: ``None``), a function field - from which this field has been constructed as a finite extension. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L - Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - sage: L.degree() - 5 - sage: L.degree(L) - 1 - - sage: R. = L[] - sage: M. = L.extension(z^2 - y) - sage: M.degree(L) - 2 - sage: M.degree(K) - 10 - - TESTS:: - - sage: L.degree(M) - Traceback (most recent call last): - ... - ValueError: base must be the rational function field itself - - """ - if base is None: - base = self.base_field() - if base is self: - from sage.rings.integer_ring import ZZ - return ZZ(1) - return self._polynomial.degree() * self.base_field().degree(base) - - def _repr_(self): - """ - Return the string representation of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L._repr_() - 'Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x' - """ - return "Function field in %s defined by %s"%(self.variable_name(), self._polynomial) - - def base_field(self): - """ - Return the base field of the function field. This function field is - presented as `L = K[y]/(f(y))`, and the base field is by definition the - field `K`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L.base_field() - Rational function field in x over Rational Field - """ - return self._base_field - - def random_element(self, *args, **kwds): - """ - Create a random element of the function field. Parameters are passed - onto the random_element method of the base_field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x)) - sage: L.random_element() # random - ((x^2 - x + 2/3)/(x^2 + 1/3*x - 1))*y^2 + ((-1/4*x^2 + 1/2*x - 1)/(-5/2*x + 2/3))*y - + (-1/2*x^2 - 4)/(-12*x^2 + 1/2*x - 1/95) - """ - return self(self._ring.random_element(degree=self.degree(), *args, **kwds)) - - def polynomial(self): - """ - Return the univariate polynomial that defines the function field, that - is, the polynomial `f(y)` so that the function field is of the form - `K[y]/(f(y))`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L.polynomial() - y^5 - 2*x*y + (-x^4 - 1)/x - """ - return self._polynomial - - def is_separable(self, base=None): - r""" - Return whether this is a separable extension of ``base``. - - INPUT: - - - ``base`` -- a function field from which this field has been created - as an extension or ``None`` (default: ``None``); if ``None``, then - return whether this is a separable extension over its base field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: L.is_separable() - False - sage: R. = L[] - sage: M. = L.extension(z^3 - y) - sage: M.is_separable() - True - sage: M.is_separable(K) - False - - sage: K. = FunctionField(GF(5)) - sage: R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L.is_separable() - True - - sage: K. = FunctionField(GF(5)) - sage: R. = K[] - sage: L. = K.extension(y^5 - 1) - sage: L.is_separable() - False - - """ - if base is None: - base = self.base_field() - for k in self._intermediate_fields(base)[:-1]: - f = k.polynomial() - g = f.derivative() - if f.gcd(g).degree() != 0: - return False - return True - - def polynomial_ring(self): - """ - Return the polynomial ring used to represent elements of the - function field. If we view the function field as being presented - as `K[y]/(f(y))`, then this function returns the ring `K[y]`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L.polynomial_ring() - Univariate Polynomial Ring in y over Rational function field in x over Rational Field - """ - return self._ring - - @cached_method(key=lambda self, base, basis, map: (self.base_field() if base is None else base, basis, map)) - def free_module(self, base=None, basis=None, map=True): - """ - Return a vector space and isomorphisms from the field to and from the - vector space. - - This function allows us to identify the elements of this field with - elements of a vector space over the base field, which is useful for - representation and arithmetic with orders, ideals, etc. - - INPUT: - - - ``base`` -- a function field (default: ``None``), the returned vector - space is over this subfield `R`, which defaults to the base field of this - function field. - - - ``basis`` -- a basis for this field over the base. - - - ``maps`` -- boolean (default ``True``), whether to return - `R`-linear maps to and from `V`. - - OUTPUT: - - - a vector space over the base function field - - - an isomorphism from the vector space to the field (if requested) - - - an isomorphism from the field to the vector space (if requested) - - EXAMPLES: - - We define a function field:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L - Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - - We get the vector spaces, and maps back and forth:: - - sage: V, from_V, to_V = L.free_module() - sage: V - Vector space of dimension 5 over Rational function field in x over Rational Field - sage: from_V - Isomorphism: - From: Vector space of dimension 5 over Rational function field in x over Rational Field - To: Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - sage: to_V - Isomorphism: - From: Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - To: Vector space of dimension 5 over Rational function field in x over Rational Field - - We convert an element of the vector space back to the function field:: - - sage: from_V(V.1) - y - - We define an interesting element of the function field:: - - sage: a = 1/L.0; a - (x/(x^4 + 1))*y^4 - 2*x^2/(x^4 + 1) - - We convert it to the vector space, and get a vector over the base field:: - - sage: to_V(a) - (-2*x^2/(x^4 + 1), 0, 0, 0, x/(x^4 + 1)) - - We convert to and back, and get the same element:: - - sage: from_V(to_V(a)) == a - True - - In the other direction:: - - sage: v = x*V.0 + (1/x)*V.1 - sage: to_V(from_V(v)) == v - True - - And we show how it works over an extension of an extension field:: - - sage: R2. = L[]; M. = L.extension(z^2 -y) - sage: M.free_module() - (Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x, Isomorphism: - From: Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - To: Function field in z defined by z^2 - y, Isomorphism: - From: Function field in z defined by z^2 - y - To: Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x) - - We can also get the vector space of ``M`` over ``K``:: - - sage: M.free_module(K) - (Vector space of dimension 10 over Rational function field in x over Rational Field, Isomorphism: - From: Vector space of dimension 10 over Rational function field in x over Rational Field - To: Function field in z defined by z^2 - y, Isomorphism: - From: Function field in z defined by z^2 - y - To: Vector space of dimension 10 over Rational function field in x over Rational Field) - - """ - if basis is not None: - raise NotImplementedError - from .maps import MapVectorSpaceToFunctionField, MapFunctionFieldToVectorSpace - if base is None: - base = self.base_field() - degree = self.degree(base) - V = base**degree - if not map: - return V - from_V = MapVectorSpaceToFunctionField(V, self) - to_V = MapFunctionFieldToVectorSpace(self, V) - return (V, from_V, to_V) - - def maximal_order(self): - """ - Return the maximal order of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L.maximal_order() - Maximal order of Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - """ - from .order import FunctionFieldMaximalOrder_polymod - return FunctionFieldMaximalOrder_polymod(self) - - def maximal_order_infinite(self): - """ - Return the maximal infinite order of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: L.maximal_order_infinite() - Maximal infinite order of Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: F.maximal_order_infinite() - Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: L.maximal_order_infinite() - Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x - """ - from .order import FunctionFieldMaximalOrderInfinite_polymod - return FunctionFieldMaximalOrderInfinite_polymod(self) - - def different(self): - """ - Return the different of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(Y^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 equation_order(self): - """ - Return the equation order of the function field. - - If we view the function field as being presented as `K[y]/(f(y))`, then - the order generated by the class of `y` is returned. If `f` - is not monic, then :meth:`_make_monic_integral` is called, and instead - we get the order generated by some integral multiple of a root of `f`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: O = L.equation_order() - sage: O.basis() - (1, x*y, x^2*y^2, x^3*y^3, x^4*y^4) - - We try an example, in which the defining polynomial is not - monic and is not integral:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(x^2*y^5 - 1/x); L - Function field in y defined by x^2*y^5 - 1/x - sage: O = L.equation_order() - sage: O.basis() - (1, x^3*y, x^6*y^2, x^9*y^3, x^12*y^4) - """ - d = self._make_monic_integral(self.polynomial())[1] - return self.order(d*self.gen(), check=False) - - def hom(self, im_gens, base_morphism=None): - """ - Create a homomorphism from the function field to another function field. - - INPUT: - - - ``im_gens`` -- list of images of the generators of the function field - and of successive base rings. - - - ``base_morphism`` -- homomorphism of the base ring, after the - ``im_gens`` are used. Thus if ``im_gens`` has length 2, then - ``base_morphism`` should be a morphism from the base ring of the base - ring of the function field. - - EXAMPLES: - - We create a rational function field, and a quadratic extension of it:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - - We make the field automorphism that sends y to -y:: - - sage: f = L.hom(-y); f - Function Field endomorphism of Function field in y defined by y^2 - x^3 - 1 - Defn: y |--> -y - - Evaluation works:: - - sage: f(y*x - 1/x) - -x*y - 1/x - - We try to define an invalid morphism:: - - sage: f = L.hom(y+1) - Traceback (most recent call last): - ... - ValueError: invalid morphism - - We make a morphism of the base rational function field:: - - sage: phi = K.hom(x+1); phi - Function Field endomorphism of Rational function field in x over Rational Field - Defn: x |--> x + 1 - sage: phi(x^3 - 3) - x^3 + 3*x^2 + 3*x - 2 - sage: (x+1)^3-3 - x^3 + 3*x^2 + 3*x - 2 - - We make a morphism by specifying where the generators and the - base generators go:: - - sage: L.hom([-y, x]) - Function Field endomorphism of Function field in y defined by y^2 - x^3 - 1 - Defn: y |--> -y - x |--> x - - You can also specify a morphism on the base:: - - sage: R1. = K[] - sage: L1. = K.extension(q^2 - (x+1)^3 - 1) - sage: L.hom(q, base_morphism=phi) - Function Field morphism: - From: Function field in y defined by y^2 - x^3 - 1 - To: Function field in q defined by q^2 - x^3 - 3*x^2 - 3*x - 2 - Defn: y |--> q - x |--> x + 1 - - We make another extension of a rational function field:: - - sage: K2. = FunctionField(QQ); R2. = K2[] - sage: L2. = K2.extension((4*w)^2 - (t+1)^3 - 1) - - We define a morphism, by giving the images of generators:: - - sage: f = L.hom([4*w, t+1]); f - Function Field morphism: - From: Function field in y defined by y^2 - x^3 - 1 - To: Function field in w defined by 16*w^2 - t^3 - 3*t^2 - 3*t - 2 - Defn: y |--> 4*w - x |--> t + 1 - - Evaluation works, as expected:: - - sage: f(y+x) - 4*w + t + 1 - sage: f(x*y + x/(x^2+1)) - (4*t + 4)*w + (t + 1)/(t^2 + 2*t + 2) - - We make another extension of a rational function field:: - - sage: K3. = FunctionField(QQ); R3. = K3[] - sage: L3. = K3.extension(yy^2 - xx^3 - 1) - - This is the function field L with the generators exchanged. We define a morphism to L:: - - sage: g = L3.hom([x,y]); g - Function Field morphism: - From: Function field in xx defined by -xx^3 + yy^2 - 1 - To: Function field in y defined by y^2 - x^3 - 1 - Defn: xx |--> x - yy |--> y - - """ - if not isinstance(im_gens, (list,tuple)): - im_gens = [im_gens] - if len(im_gens) == 0: - raise ValueError("no images specified") - - if len(im_gens) > 1: - base_morphism = self.base_field().hom(im_gens[1:], base_morphism) - - # the codomain of this morphism is the field containing all the im_gens - codomain = im_gens[0].parent() - if base_morphism is not None: - from sage.categories.pushout import pushout - codomain = pushout(codomain, base_morphism.codomain()) - - from .maps import FunctionFieldMorphism_polymod - return FunctionFieldMorphism_polymod(self.Hom(codomain), im_gens[0], base_morphism) - - @cached_method - def genus(self): - """ - Return the genus of the function field. - - For now, the genus is computed using Singular. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) - sage: L.genus() - 3 - """ - # Unfortunately Singular can not compute the genus with the - # polynomial_ring()._singular_ object because genus method only accepts - # a ring of transcendental degree 2 over a prime field not a ring of - # transcendental degree 1 over a rational function field of one variable - - if (isinstance(self._base_field, RationalFunctionField) and - self._base_field.constant_field().is_prime_field()): - - # making the auxiliary ring which only has polynomials - # with integral coefficients. - tmpAuxRing = PolynomialRing(self._base_field.constant_field(), - str(self._base_field.gen())+','+str(self._ring.gen())) - intMinPoly, d = self._make_monic_integral(self._polynomial) - curveIdeal = tmpAuxRing.ideal(intMinPoly) - - singular.lib('normal.lib') #loading genus method in Singular - return int(curveIdeal._singular_().genus()) - - else: - raise NotImplementedError("computation of genus over non-prime " - "constant fields not implemented yet") - - def _simple_model(self, name='v'): - r""" - Return a finite extension `N/K(x)` isomorphic to the tower of - extensions `M/L/K(x)` with `K` perfect. - - Helper method for :meth:`simple_model`. - - INPUT: - - - ``name`` -- a string, the name of the generator of `N` - - ALGORITHM: - - Since `K` is perfect, the extension `M/K(x)` is simple, i.e., generated - by a single element [BM1940]_. Therefore, there are only finitely many - intermediate fields (Exercise 3.6.7 in [Bo2009]_). - Let `a` be a generator of `M/L` and let `b` be a generator of `L/K(x)`. - For some `i` the field `N_i=K(x)(a+x^ib)` is isomorphic to `M` and so - it is enough to test for all terms of the form `a+x^ib` whether they - generate a field of the right degree. - Indeed, suppose for contradiction that for all `i` we had `N_i\neq M`. - Then `N_i=N_j` for some `i,j`. Thus `(a+x^ib)-(a+x^jb)=b(x^i-x^j)\in - N_j` and so `b\in N_j`. Similarly, - `a+x^ib-x^{i-j}(a+x^jb)=a(1+x^{i-j})\in N_j` and so `a\in N_j`. - Therefore, `N_j=M`. - - TESTS:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2-x) - sage: R. = L[] - sage: M. = L.extension(z^2-y) - sage: M._simple_model() - (Function field in v defined by v^4 - x, - Function Field morphism: - From: Function field in v defined by v^4 - x - To: Function field in z defined by z^2 - y - Defn: v |--> z, - Function Field morphism: - From: Function field in z defined by z^2 - y - To: Function field in v defined by v^4 - x - Defn: z |--> v - y |--> v^2) - - Check that this also works for inseparable extensions:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2-x) - sage: R. = L[] - sage: M. = L.extension(z^2-y) - sage: M._simple_model() - (Function field in v defined by v^4 + x, - Function Field morphism: - From: Function field in v defined by v^4 + x - To: Function field in z defined by z^2 + y - Defn: v |--> z, - Function Field morphism: - From: Function field in z defined by z^2 + y - To: Function field in v defined by v^4 + x - Defn: z |--> v - y |--> v^2) - - An example where the generator of the last extension does not generate - the extension of the rational function field:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2-x) - sage: R. = L[] - sage: M. = L.extension(z^3-1) - sage: M._simple_model() - (Function field in v defined by v^6 + x*v^4 + x^2*v^2 + x^3 + 1, - Function Field morphism: - From: Function field in v defined by v^6 + x*v^4 + x^2*v^2 + x^3 + 1 - To: Function field in z defined by z^3 + 1 - Defn: v |--> z + y, - Function Field morphism: - From: Function field in z defined by z^3 + 1 - To: Function field in v defined by v^6 + x*v^4 + x^2*v^2 + x^3 + 1 - Defn: z |--> v^4 + x^2 - y |--> v^4 + v + x^2) - - """ - M = self - L = M.base_field() - K = L.base_field() - - assert(isinstance(K, RationalFunctionField)) - assert(K is not L) - assert(L is not M) - - if not K.constant_field().is_perfect(): - raise NotImplementedError("simple_model() only implemented over perfect constant fields") - - x = K.gen() - b = L.gen() - a = M.gen() - - # using a+x^i*b tends to lead to huge powers of x in the minimal - # polynomial of the resulting field; it is better to try terms of - # the form a+i*b first (but in characteristic p>0 there are only - # finitely many of these) - # We systematically try elements of the form a+b*factor*x^exponent - factor = self.constant_base_field().zero() - exponent = 0 - while True: - v = M(a+b*factor*x**exponent) - minpoly = v.matrix(K).minpoly() - if minpoly.degree() == M.degree()*L.degree(): - break - factor += 1 - if factor == 0: - factor = self.constant_base_field().one() - exponent += 1 - - N = K.extension(minpoly, names=(name,)) - - # the morphism N -> M, v |-> v - N_to_M = N.hom(v) - - # the morphism M -> N, b |-> M_b, a |-> M_a - V, V_to_M, M_to_V = M.free_module(K) - V, V_to_N, N_to_V = N.free_module(K) - from sage.matrix.matrix_space import MatrixSpace - MS = MatrixSpace(V.base_field(), V.dimension()) - # the power basis of v over K - B = [M_to_V(v**i) for i in range(V.dimension())] - B = MS(B) - M_b = V_to_N(B.solve_left(M_to_V(b))) - M_a = V_to_N(B.solve_left(M_to_V(a))) - M_to_N = M.hom([M_a,M_b]) - - return N, N_to_M, M_to_N - - @cached_method - def simple_model(self, name=None): - """ - Return a function field isomorphic to this field which is a simple - extension of a rational function field. - - INPUT: - - - ``name`` -- a string (default: ``None``), the name of generator of - the simple extension. If ``None``, then the name of the generator - will be the same as the name of the generator of this function field. - - OUTPUT: - - A triple ``(F,f,t)`` where ``F`` is a field isomorphic to this field, - ``f`` is an isomorphism from ``F`` to this function field and ``t`` is - the inverse of ``f``. - - EXAMPLES: - - A tower of four function fields:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(z^2-x); R. = L[] - sage: M. = L.extension(u^2-z); R. = M[] - sage: N. = M.extension(v^2-u) - - The fields N and M as simple extensions of K:: - - sage: N.simple_model() - (Function field in v defined by v^8 - x, - Function Field morphism: - From: Function field in v defined by v^8 - x - To: Function field in v defined by v^2 - u - Defn: v |--> v, - Function Field morphism: - From: Function field in v defined by v^2 - u - To: Function field in v defined by v^8 - x - Defn: v |--> v - u |--> v^2 - z |--> v^4 - x |--> x) - sage: M.simple_model() - (Function field in u defined by u^4 - x, - Function Field morphism: - From: Function field in u defined by u^4 - x - To: Function field in u defined by u^2 - z - Defn: u |--> u, - Function Field morphism: - From: Function field in u defined by u^2 - z - To: Function field in u defined by u^4 - x - Defn: u |--> u - z |--> u^2 - x |--> x) - - An optional parameter ``name`` can be used to set the name of the - generator of the simple extension:: - - sage: M.simple_model(name='t') - (Function field in t defined by t^4 - x, Function Field morphism: - From: Function field in t defined by t^4 - x - To: Function field in u defined by u^2 - z - Defn: t |--> u, Function Field morphism: - From: Function field in u defined by u^2 - z - To: Function field in t defined by t^4 - x - Defn: u |--> t - z |--> t^2 - x |--> x) - - An example with higher degrees:: - - sage: K. = FunctionField(GF(3)); R. = K[] - sage: L. = K.extension(y^5-x); R. = L[] - sage: M. = L.extension(z^3-x) - sage: M.simple_model() - (Function field in z defined by z^15 + x*z^12 + x^2*z^9 + 2*x^3*z^6 + 2*x^4*z^3 + 2*x^5 + 2*x^3, - Function Field morphism: - From: Function field in z defined by z^15 + x*z^12 + x^2*z^9 + 2*x^3*z^6 + 2*x^4*z^3 + 2*x^5 + 2*x^3 - To: Function field in z defined by z^3 + 2*x - Defn: z |--> z + y, - Function Field morphism: - From: Function field in z defined by z^3 + 2*x - To: Function field in z defined by z^15 + x*z^12 + x^2*z^9 + 2*x^3*z^6 + 2*x^4*z^3 + 2*x^5 + 2*x^3 - Defn: z |--> 2/x*z^6 + 2*z^3 + z + 2*x - y |--> 1/x*z^6 + z^3 + x - x |--> x) - - This also works for inseparable extensions:: - - sage: K. = FunctionField(GF(2)); R. = K[] - sage: L. = K.extension(y^2-x); R. = L[] - sage: M. = L.extension(z^2-y) - sage: M.simple_model() - (Function field in z defined by z^4 + x, Function Field morphism: - From: Function field in z defined by z^4 + x - To: Function field in z defined by z^2 + y - Defn: z |--> z, Function Field morphism: - From: Function field in z defined by z^2 + y - To: Function field in z defined by z^4 + x - Defn: z |--> z - y |--> z^2 - x |--> x) - """ - if name is None: - name = self.variable_name() - - if isinstance(self.base_field(), RationalFunctionField): - # the extension is simple already - if name == self.variable_name(): - id = Hom(self,self).identity() - return self, id, id - else: - ret = self.base_field().extension(self.polynomial(), names=(name,)) - f = ret.hom(self.gen()) - t = self.hom(ret.gen()) - return ret, f, t - else: - # recursively collapse the tower of fields - base = self.base_field() - base_, from_base_, to_base_ = base.simple_model() - self_ = base_.extension(self.polynomial().map_coefficients(to_base_), names=(name,)) - gens_in_base_ = [to_base_(k.gen()) - for k in base._intermediate_fields(base.rational_function_field())] - to_self_ = self.hom([self_.gen()]+gens_in_base_) - from_self_ = self_.hom([self.gen(),from_base_(base_.gen())]) - - # now collapse self_/base_/K(x) - ret, ret_to_self_, self__to_ret = self_._simple_model(name) - ret_to_self = ret.hom(from_self_(ret_to_self_(ret.gen()))) - gens_in_ret = [self__to_ret(to_self_(k.gen())) - for k in self._intermediate_fields(self.rational_function_field())] - self_to_ret = self.hom(gens_in_ret) - return ret, ret_to_self, self_to_ret - - @cached_method - def primitive_element(self): - r""" - Return a primitive element over the underlying rational function field. - - If this is a finite extension of a rational function field `K(x)` with - `K` perfect, then this is a simple extension of `K(x)`, i.e., there is - a primitive element `y` which generates this field over `K(x)`. This - method returns such an element `y`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2-x) - sage: R. = L[] - sage: M. = L.extension(z^2-y) - sage: R. = L[] - sage: N. = L.extension(z^2-x-1) - sage: N.primitive_element() - u + y - sage: M.primitive_element() - z - sage: L.primitive_element() - y - - This also works for inseparable extensions:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(Y^2-x) - sage: R. = L[] - sage: M. = L.extension(Z^2-y) - sage: M.primitive_element() - z - """ - N, f, t = self.simple_model() - return f(N.gen()) - - @cached_method - def separable_model(self, names=None): - r""" - Return a function field isomorphic to this field which is a separable - extension of a rational function field. - - INPUT: - - - ``names`` -- a tuple of two strings or ``None`` (default: ``None``); - the second entry will be used as the variable name of the rational - function field, the first entry will be used as the variable name of - its separable extension. If ``None``, then the variable names will be - chosen automatically. - - OUTPUT: - - A triple ``(F,f,t)`` where ``F`` is a function field, ``f`` is an - isomorphism from ``F`` to this function field, and ``t`` is the inverse - of ``f``. - - ALGORITHM: - - Suppose that the constant base field is perfect. If this is a monic - integral inseparable extension of a rational function field, then the - defining polynomial is separable if we swap the variables (Proposition - 4.8 in Chapter VIII of [Lan2002]_.) - The algorithm reduces to this case with :meth:`monic_integral_model`. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x^3) - sage: L.separable_model(('t','w')) - (Function field in t defined by t^3 + w^2, - Function Field morphism: - From: Function field in t defined by t^3 + w^2 - To: Function field in y defined by y^2 + x^3 - Defn: t |--> x - w |--> y, - Function Field morphism: - From: Function field in y defined by y^2 + x^3 - To: Function field in t defined by t^3 + w^2 - Defn: y |--> w - x |--> t) - - This also works for non-integral polynomials:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2/x - x^2) - sage: L.separable_model() - (Function field in y_ defined by y_^3 + x_^2, - Function Field morphism: - From: Function field in y_ defined by y_^3 + x_^2 - To: Function field in y defined by 1/x*y^2 + x^2 - Defn: y_ |--> x - x_ |--> y, - Function Field morphism: - From: Function field in y defined by 1/x*y^2 + x^2 - To: Function field in y_ defined by y_^3 + x_^2 - Defn: y |--> x_ - x |--> y_) - - If the base field is not perfect this is only implemented in trivial cases:: - - sage: k. = FunctionField(GF(2)) - sage: k.is_perfect() - False - sage: K. = FunctionField(k) - sage: R. = K[] - sage: L. = K.extension(y^3 - t) - sage: L.separable_model() - (Function field in y defined by y^3 + t, - Function Field endomorphism of Function field in y defined by y^3 + t - Defn: y |--> y - x |--> x, - Function Field endomorphism of Function field in y defined by y^3 + t - Defn: y |--> y - x |--> x) - - Some other cases for which a separable model could be constructed are - not supported yet:: - - sage: R. = K[] - sage: L. = K.extension(y^2 - t) - sage: L.separable_model() - Traceback (most recent call last): - ... - NotImplementedError: constructing a separable model is only implemented for function fields over a perfect constant base field - - TESTS: - - Check that this also works in characteristic zero:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x^3) - sage: L.separable_model() - (Function field in y defined by y^2 - x^3, - Function Field endomorphism of Function field in y defined by y^2 - x^3 - Defn: y |--> y - x |--> x, - Function Field endomorphism of Function field in y defined by y^2 - x^3 - Defn: y |--> y - x |--> x) - - Check that this works for towers of inseparable extensions:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: R. = L[] - sage: M. = L.extension(z^2 - y) - sage: M.separable_model() - (Function field in z_ defined by z_ + x_^4, - Function Field morphism: - From: Function field in z_ defined by z_ + x_^4 - To: Function field in z defined by z^2 + y - Defn: z_ |--> x - x_ |--> z, - Function Field morphism: - From: Function field in z defined by z^2 + y - To: Function field in z_ defined by z_ + x_^4 - Defn: z |--> x_ - y |--> x_^2 - x |--> x_^4) - - Check that this also works if only the first extension is inseparable:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: R. = L[] - sage: M. = L.extension(z^3 - y) - sage: M.separable_model() - (Function field in z_ defined by z_ + x_^6, Function Field morphism: - From: Function field in z_ defined by z_ + x_^6 - To: Function field in z defined by z^3 + y - Defn: z_ |--> x - x_ |--> z, Function Field morphism: - From: Function field in z defined by z^3 + y - To: Function field in z_ defined by z_ + x_^6 - Defn: z |--> x_ - y |--> x_^3 - x |--> x_^6) - - """ - if names is None: - pass - elif not isinstance(names, tuple): - raise TypeError("names must be a tuple consisting of two strings") - elif len(names) != 2: - raise ValueError("must provide exactly two variable names") - - if self.base_ring() is not self.rational_function_field(): - L, from_L, to_L = self.simple_model() - K, from_K, to_K = L.separable_model(names=names) - f = K.hom([from_L(from_K(K.gen())), from_L(from_K(K.base_field().gen()))]) - t = self.hom([to_K(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())]) - return K, f, t - - if self.polynomial().gcd(self.polynomial().derivative()).is_one(): - # the model is already separable - if names is None: - names = self.variable_name(), self.base_field().variable_name() - return self.change_variable_name(names) - - if not self.constant_base_field().is_perfect(): - raise NotImplementedError("constructing a separable model is only implemented for function fields over a perfect constant base field") - - if names is None: - names = (self.variable_name()+"_", self.rational_function_field().variable_name()+"_") - - L, from_L, to_L = self.monic_integral_model() - - if L.polynomial().gcd(L.polynomial().derivative()).is_one(): - # L is separable - ret, ret_to_L, L_to_ret = L.change_variable_name(names) - f = ret.hom([from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))]) - t = self.hom([L_to_ret(to_L(self.gen())), L_to_ret(to_L(self.base_field().gen()))]) - return ret, f, t - else: - # otherwise, the polynomial of L must be separable in the other variable - from .constructor import FunctionField - K = FunctionField(self.constant_base_field(), names=(names[1],)) - # construct a field isomorphic to L on top of K - - # turn the minpoly of K into a bivariate polynomial - if names[0] == names[1]: - raise ValueError("names of generators must be distinct") - from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing - R = PolynomialRing(self.constant_base_field(), names=names) - S = R.remove_var(names[1]) - f = R( L.polynomial().change_variable_name(names[1]).map_coefficients( - lambda c:c.numerator().change_variable_name(names[0]), S)) - f = f.polynomial(R.gen(0)).change_ring(K) - f /= f.leading_coefficient() - # f must be separable in the other variable (otherwise it would factor) - assert f.gcd(f.derivative()).is_one() - - ret = K.extension(f, names=(names[0],)) - # isomorphisms between L and ret are given by swapping generators - ret_to_L = ret.hom( [L(L.base_field().gen()), L.gen()] ) - L_to_ret = L.hom( [ret(K.gen()), ret.gen()] ) - # compose with from_L and to_L to get the desired isomorphisms between self and ret - f = ret.hom( [from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))] ) - t = self.hom( [L_to_ret(to_L(self.gen())), L_to_ret(to_L(self.base_field().gen()))] ) - return ret, f, t - - def change_variable_name(self, name): - r""" - Return a field isomorphic to this field with variable(s) ``name``. - - INPUT: - - - ``name`` -- a string or a tuple consisting of a strings, the names of - the new variables starting with a generator of this field and going - down to the rational function field. - - OUTPUT: - - A triple ``F,f,t`` where ``F`` is a function field, ``f`` is an - isomorphism from ``F`` to this field, and ``t`` is the inverse of - ``f``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: R. = L[] - sage: M. = L.extension(z^2 - y) - - sage: M.change_variable_name('zz') - (Function field in zz defined by zz^2 - y, - Function Field morphism: - From: Function field in zz defined by zz^2 - y - To: Function field in z defined by z^2 - y - Defn: zz |--> z - y |--> y - x |--> x, - Function Field morphism: - From: Function field in z defined by z^2 - y - To: Function field in zz defined by zz^2 - y - Defn: z |--> zz - y |--> y - x |--> x) - sage: M.change_variable_name(('zz','yy')) - (Function field in zz defined by zz^2 - yy, Function Field morphism: - From: Function field in zz defined by zz^2 - yy - To: Function field in z defined by z^2 - y - Defn: zz |--> z - yy |--> y - x |--> x, Function Field morphism: - From: Function field in z defined by z^2 - y - To: Function field in zz defined by zz^2 - yy - Defn: z |--> zz - y |--> yy - x |--> x) - sage: M.change_variable_name(('zz','yy','xx')) - (Function field in zz defined by zz^2 - yy, - Function Field morphism: - From: Function field in zz defined by zz^2 - yy - To: Function field in z defined by z^2 - y - Defn: zz |--> z - yy |--> y - xx |--> x, - Function Field morphism: - From: Function field in z defined by z^2 - y - To: Function field in zz defined by zz^2 - yy - Defn: z |--> zz - y |--> yy - x |--> xx) - - """ - if not isinstance(name, tuple): - name = (name,) - if len(name) == 0: - raise ValueError("name must contain at least one string") - elif len(name) == 1: - base = self.base_field() - from_base = to_base = Hom(base,base).identity() - else: - base, from_base, to_base = self.base_field().change_variable_name(name[1:]) - - ret = base.extension(self.polynomial().map_coefficients(to_base), names=(name[0],)) - f = ret.hom( [k.gen() for k in self._intermediate_fields(self.rational_function_field())] ) - t = self.hom( [k.gen() for k in ret._intermediate_fields(ret.rational_function_field())] ) - return ret, f, t - - -class FunctionField_simple(FunctionField_polymod): - """ - Function fields defined by irreducible and separable polynomials - over rational function fields. - """ - @cached_method - def _inversion_isomorphism(self): - r""" - Return an inverted function field isomorphic to ``self`` and isomorphisms - between them. - - An *inverted* function field `M` is an extension of the base rational - function field `k(x)` of ``self``, and isomorphic to ``self`` by an - isomorphism sending `x` to `1/x`, which we call an *inversion* - isomorphism. Also the defining polynomial of the function field `M` is - required to be monic and integral. - - The inversion isomorphism is for internal use to reposition infinite - places to finite places. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: F._inversion_isomorphism() - (Function field in s defined by s^3 + x^16 + x^14 + x^12, Composite map: - From: Function field in s defined by s^3 + x^16 + x^14 + x^12 - To: Function field in y defined by y^3 + x^6 + x^4 + x^2 - Defn: Function Field morphism: - From: Function field in s defined by s^3 + x^16 + x^14 + x^12 - To: Function field in T defined by T^3 + (x^4 + x^2 + 1)/x^6 - Defn: s |--> x^6*T - x |--> x - then - Function Field morphism: - From: Function field in T defined by T^3 + (x^4 + x^2 + 1)/x^6 - To: Function field in y defined by y^3 + x^6 + x^4 + x^2 - Defn: T |--> y - x |--> 1/x, Composite map: - From: Function field in y defined by y^3 + x^6 + x^4 + x^2 - To: Function field in s defined by s^3 + x^16 + x^14 + x^12 - Defn: Function Field morphism: - From: Function field in y defined by y^3 + x^6 + x^4 + x^2 - To: Function field in T defined by T^3 + (x^4 + x^2 + 1)/x^6 - Defn: y |--> T - x |--> 1/x - then - Function Field morphism: - From: Function field in T defined by T^3 + (x^4 + x^2 + 1)/x^6 - To: Function field in s defined by s^3 + x^16 + x^14 + x^12 - Defn: T |--> 1/x^6*s - x |--> x) - """ - K = self.base_field() - R = PolynomialRing(K,'T') - x = K.gen() - xinv = 1/x - - h = K.hom(xinv) - F_poly = R([h(c) for c in self.polynomial().list()]) - F = K.extension(F_poly) - - self2F = self.hom([F.gen(),xinv]) - F2self = F.hom([self.gen(),xinv]) - - M, M2F, F2M = F.monic_integral_model('s') - - return M, F2self*M2F, F2M*self2F - - def places_above(self, p): - """ - Return places lying above ``p``. - - INPUT: - - - ``p`` -- place of the base rational function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: all(q.place_below() == p for p in K.places() for q in F.places_above(p)) - True - - sage: K. = FunctionField(QQ); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: O = K.maximal_order() - sage: pls = [O.ideal(x-c).place() for c in [-2, -1, 0, 1, 2]] - sage: all(q.place_below() == p for p in pls for q in F.places_above(p)) - True - - sage: K. = FunctionField(QQbar); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: O = K.maximal_order() - sage: pls = [O.ideal(x-QQbar(sqrt(c))).place() for c in [-2, -1, 0, 1, 2]] - sage: all(q.place_below() == p # long time (4s) - ....: for p in pls for q in F.places_above(p)) - True - """ - R = self.base_field() - - if p not in R.place_set(): - raise TypeError("not a place of the base rational function field") - - if p.is_infinite_place(): - dec = self.maximal_order_infinite().decomposition() - else: - dec = self.maximal_order().decomposition(p.prime_ideal()) - - return tuple([q.place() for q, deg, exp in dec]) - - def constant_field(self): - """ - 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: - - - ``name`` -- name (default: `t`) of the generator of the exact constant field - - EXAMPLES:: - - 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() - - 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 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 Integer(different_degree // 2 - self.degree() / k.degree()) + 1 - - 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) - - -class FunctionField_char_zero(FunctionField_simple): - """ - Function fields of characteristic zero. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L - Function field in y defined by y^3 + (-x^3 + 1)/(x^3 - 2) - sage: L.characteristic() - 0 - """ - @cached_method - def higher_derivation(self): - """ - Return the higher derivation (also called the Hasse-Schmidt derivation) - for the function field. - - The higher derivation of the function field is uniquely determined with - respect to the separating element `x` of the base rational function - field `k(x)`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L.higher_derivation() - Higher derivation map: - From: Function field in y defined by y^3 + (-x^3 + 1)/(x^3 - 2) - To: Function field in y defined by y^3 + (-x^3 + 1)/(x^3 - 2) - """ - from .maps import FunctionFieldHigherDerivation_char_zero - return FunctionFieldHigherDerivation_char_zero(self) - - -class FunctionField_global(FunctionField_simple): - """ - Global function fields. - - INPUT: - - - ``polynomial`` -- monic irreducible and separable polynomial - - - ``names`` -- name of the generator of the function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L - Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) - - The defining equation needs not be monic:: - - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension((1 - x)*Y^7 - x^3) - sage: L.gaps() # long time (6s) - [1, 2, 3] - - or may define a trivial extension:: - - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y-1) - sage: L.genus() - 0 - """ - _differentials_space = DifferentialsSpace_global - - def __init__(self, polynomial, names): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(5)); _. = K[] - sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: TestSuite(L).run() # long time (7s) - """ - FunctionField_polymod.__init__(self, polynomial, names) - - def maximal_order(self): - """ - Return the maximal order of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); - sage: R. = PolynomialRing(K); - sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18); - sage: O = F.maximal_order() - sage: O.basis() - (1, 1/x^4*y, 1/x^11*y^2 + 1/x^2, 1/x^15*y^3 + 1/x^6*y) - """ - from .order import FunctionFieldMaximalOrder_global - return FunctionFieldMaximalOrder_global(self) - - @cached_method - def higher_derivation(self): - """ - Return the higher derivation (also called the Hasse-Schmidt derivation) - for the function field. - - The higher derivation of the function field is uniquely determined with - respect to the separating element `x` of the base rational function - field `k(x)`. - - EXAMPLES:: - - sage: K.=FunctionField(GF(5)); _.=K[] - sage: L.=K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) - sage: L.higher_derivation() - Higher derivation map: - From: Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) - To: Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) - """ - from .maps import FunctionFieldHigherDerivation_global - return FunctionFieldHigherDerivation_global(self) - - def get_place(self, degree): - """ - Return a place of ``degree``. - - INPUT: - - - ``degree`` -- a positive integer - - OUTPUT: a place of ``degree`` if any exists; otherwise ``None`` - - EXAMPLES:: - - sage: F. = GF(2) - sage: K. = FunctionField(F) - sage: R. = PolynomialRing(K) - sage: L. = K.extension(Y^4 + Y - x^5) - sage: L.get_place(1) - Place (x, y) - sage: L.get_place(2) - Place (x, y^2 + y + 1) - sage: L.get_place(3) - Place (x^3 + x^2 + 1, y + x^2 + x) - sage: L.get_place(4) - Place (x + 1, x^5 + 1) - sage: L.get_place(5) - Place (x^5 + x^3 + x^2 + x + 1, y + x^4 + 1) - sage: L.get_place(6) - Place (x^3 + x^2 + 1, y^2 + y + x^2) - sage: L.get_place(7) - Place (x^7 + x + 1, y + x^6 + x^5 + x^4 + x^3 + x) - sage: L.get_place(8) - - """ - for p in self._places_finite(degree): - return p - - for p in self._places_infinite(degree): - return p - - return None - - 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() - - 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 gaps(self): - """ - Return the gaps of the function field. - - These are the gaps at the ordinary places, that is, places which are - not Weierstrass places. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3 * Y + x) - sage: L.gaps() - [1, 2, 3] - """ - return self._weierstrass_places()[1] - - def weierstrass_places(self): - """ - Return all Weierstrass places of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3 * Y + x) - sage: L.weierstrass_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), - Place (x + 1, (x^3 + 1)*y + x + 1), - Place (x^3 + x + 1, y + 1), - Place (x^3 + x + 1, y + x^2), - Place (x^3 + x + 1, y + x^2 + 1), - Place (x^3 + x^2 + 1, y + x), - Place (x^3 + x^2 + 1, y + x^2 + 1), - Place (x^3 + x^2 + 1, y + x^2 + x + 1)] - """ - return self._weierstrass_places()[0].support() - - @cached_method - def _weierstrass_places(self): - """ - Return the Weierstrass places together with the gap sequence for - ordinary places. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3 * Y + x) - sage: len(L.weierstrass_places()) # indirect doctest - 10 - - This method implements Algorithm 30 in [Hes2002b]_. - """ - from sage.matrix.constructor import matrix - - W = self(self.base_field().gen()).differential().divisor() - basis = W._basis() - - if not basis: - return [], [] - d = len(basis) - - der = self.higher_derivation() - M = matrix([basis]) - e = 1 - gaps = [1] - while M.nrows() < d: - row = vector([der._derive(basis[i], e) for i in range(d)]) - if row not in M.row_space(): - M = matrix(M.rows() + [row]) - gaps.append(e + 1) - e += 1 - - # This is faster than M.determinant(). Note that Mx - # is a matrix over univariate polynomial ring. - Mx = matrix(M.nrows(), [c._x for c in M.list()]) - detM = self(Mx.determinant() % self._polynomial) - - R = detM.divisor() + sum(gaps)*W # ramification divisor - - return R, gaps - - @cached_method - def L_polynomial(self, name='t'): - """ - Return the L-polynomial of the function field. - - INPUT: - - - ``name`` -- (default: ``t``) name of the variable of the polynomial - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(Y^2 + Y + x + 1/x) - sage: F.L_polynomial() - 2*t^2 + t + 1 - """ - from sage.rings.integer_ring import ZZ - q = self.constant_field().order() - g = self.genus() - - B = [len(self.places(i+1)) for i in range(g)] - N = [sum(d * B[d-1] for d in ZZ(i+1).divisors()) for i in range(g)] - S = [N[i] - q**(i+1) - 1 for i in range(g)] - - a = [1] - for i in range(1, g+1): - a.append(sum(S[j] * a[i-j-1] for j in range(i)) / i) - for j in range(1, g+1): - a.append(q**j * a[g-j]) - - return ZZ[name](a) - - def number_of_rational_places(self, r=1): - """ - Return the number of rational places of the function field whose - constant field extended by degree ``r``. - - INPUT: - - - ``r`` -- positive integer (default: `1`) - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(Y^2 + Y + x + 1/x) - sage: F.number_of_rational_places() - 4 - sage: [F.number_of_rational_places(r) for r in [1..10]] - [4, 8, 4, 16, 44, 56, 116, 288, 508, 968] - """ - from sage.rings.integer_ring import IntegerRing - - q = self.constant_field().order() - L = self.L_polynomial() - Lp = L.derivative() - - R = IntegerRing()[[L.parent().gen()]] # power series ring - - f = R(Lp / L, prec=r) - n = f[r-1] + q**r + 1 - - return n - - -@handle_AA_and_QQbar -def _singular_normal(ideal): - r""" - Compute the normalization of the affine algebra defined by ``ideal`` using - Singular. - - The affine algebra is the quotient algebra of a multivariate polynomial - ring `R` by the ideal. The normalization is by definition the integral - closure of the algebra in its total ring of fractions. - - INPUT: - - - ``ideal`` -- a radical ideal in a multivariate polynomial ring - - OUTPUT: - - a list of lists, one list for each ideal in the equidimensional - decomposition of the ``ideal``, each list giving a set of generators of the - normalization of each ideal as an R-module by dividing all elements of the - list by the final element. Thus the list ``[x, y]`` means that `\{x/y, 1\}` - is the set of generators of the normalization of `R/(x,y)`. - - ALGORITHM: - - Singular's implementation of the normalization algorithm described in G.-M. - Greuel, S. Laplagne, F. Seelisch: Normalization of Rings (2009). - - EXAMPLES:: - - sage: from sage.rings.function_field.function_field import _singular_normal - sage: R. = QQ[] - - sage: f = (x^2-y^3) * x - sage: _singular_normal(ideal(f)) - [[x, y], [1]] - - sage: f = (y^2-x) - sage: _singular_normal(ideal(f)) - [[1]] - """ - from sage.libs.singular.function import singular_function, lib - lib('normal.lib') - normal = singular_function('normal') - execute = singular_function('execute') - - try: - get_printlevel = singular_function('get_printlevel') - except NameError: - execute('proc get_printlevel {return (printlevel);}') - get_printlevel = singular_function('get_printlevel') - - # It's fairly verbose unless printlevel is -1. - saved_printlevel = get_printlevel() - execute('printlevel=-1') - nor = normal(ideal) - execute('printlevel={}'.format(saved_printlevel)) - - return nor[1] - - -class FunctionField_integral(FunctionField_simple): - """ - Integral function fields. - - A function field is integral if it is defined by an irreducible separable - polynomial, which is integral over the maximal order of the base rational - function field. - """ - def _maximal_order_basis(self): - """ - Return a basis of the maximal order of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: R. = PolynomialRing(K) - sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) - sage: F._maximal_order_basis() - [1, 1/x^4*y, 1/x^11*y^2 + 1/x^2, 1/x^15*y^3 + 1/x^6*y] - - The basis of the maximal order *always* starts with 1. This is assumed - in some algorithms. - """ - from sage.matrix.constructor import matrix - from .hermite_form_polynomial import reversed_hermite_form - - k = self.constant_base_field() - K = self.base_field() # rational function field - n = self.degree() - - # Construct the defining polynomial of the function field as a - # two-variate polynomial g in the ring k[y,x] where k is the constant - # base field. - S,(y,x) = PolynomialRing(k, names='y,x', order='lex').objgens() - v = self.polynomial().list() - g = sum([v[i].numerator().subs(x) * y**i for i in range(len(v))]) - - if self.is_global(): - from sage.libs.singular.function import singular_function, lib - from sage.env import SAGE_EXTCODE - lib(SAGE_EXTCODE + '/singular/function_field/core.lib') - normalize = singular_function('core_normalize') - - # Singular "normalP" algorithm assumes affine domain over - # a prime field. So we construct gflat lifting g as in - # k_prime[yy,xx,zz]/(k_poly) where k = k_prime[zz]/(k_poly) - R = PolynomialRing(k.prime_subfield(), names='yy,xx,zz') - gflat = R.zero() - for m in g.monomials(): - c = g.monomial_coefficient(m).polynomial('zz') - gflat += R(c) * R(m) # R(m) is a monomial in yy and xx - - k_poly = R(k.polynomial('zz')) - - # invoke Singular - pols_in_R = normalize(R.ideal([k_poly, gflat])) - - # reconstruct polynomials in S - h = R.hom([y,x,k.gen()],S) - pols_in_S = [h(f) for f in pols_in_R] - else: - # Call Singular. Singular's "normal" function returns a basis - # of the integral closure of k(x,y)/(g) as a k[x,y]-module. - pols_in_S = _singular_normal(S.ideal(g))[0] - - # reconstruct the polynomials in the function field - x = K.gen() - y = self.gen() - pols = [] - for f in pols_in_S: - p = f.polynomial(S.gen(0)) - s = 0 - for i in range(p.degree()+1): - s += p[i].subs(x) * y**i - pols.append(s) - - # Now if pols = [g1,g2,...gn,g0], then the g1/g0,g2/g0,...,gn/g0, - # and g0/g0=1 are the module generators of the integral closure - # of the equation order Sb = k[xb,yb] in its fraction field, - # that is, the function field. The integral closure of k[x] - # is then obtained by multiplying these generators with powers of y - # as the equation order itself is an integral extension of k[x]. - d = ~ pols[-1] - _basis = [] - for f in pols: - b = d * f - for i in range(n): - _basis.append(b) - b *= y - - # Finally we reduce _basis to get a basis over k[x]. This is done of - # course by Hermite normal form computation. Here we apply a trick to - # get a basis that starts with 1 and is ordered in increasing - # y-degrees. The trick is to use the reversed Hermite normal form. - # Note that it is important that the overall denominator l lies in k[x]. - V, fr_V, to_V = self.free_module() - basis_V = [to_V(bvec) for bvec in _basis] - l = lcm([vvec.denominator() for vvec in basis_V]) - - _mat = matrix([[coeff.numerator() for coeff in l*v] for v in basis_V]) - reversed_hermite_form(_mat) - - basis = [fr_V(v) / l for v in _mat if not v.is_zero()] - return basis - - @cached_method - def equation_order(self): - """ - Return the equation order 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.equation_order() - Order in Function field in y defined by y^3 + x^6 + x^4 + x^2 - - sage: K. = FunctionField(QQ); R. = PolynomialRing(K) - sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) - sage: F.equation_order() - Order in Function field in y defined by y^3 - x^6 - 2*x^5 - 3*x^4 - 2*x^3 - x^2 - """ - from .order import FunctionFieldOrder_basis - a = self.gen() - basis = [a**i for i in range(self.degree())] - return FunctionFieldOrder_basis(tuple(basis)) - - @cached_method - def primitive_integal_element_infinite(self): - """ - Return a primitive integral element over the base maximal infinite order. - - This element is integral over the maximal infinite order of the base - rational function field and the function field is a simple extension by - this element over the base order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) - sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) - sage: b = F.primitive_integal_element_infinite(); b - 1/x^2*y - sage: b.minimal_polynomial('t') - t^3 + (x^4 + x^2 + 1)/x^4 - """ - f = self.polynomial() - n = f.degree() - y = self.gen() - x = self.base_field().gen() - - cf = max([(f[i].numerator().degree()/(n-i)).ceil() for i in range(n) - if f[i] != 0]) - return y*x**(-cf) - - @cached_method - def equation_order_infinite(self): - """ - Return the infinite equation order of the function field. - - This is by definition `o[b]` where `b` is the primitive integral - element from :meth:`primitive_integal_element_infinite()` and `o` is - the maximal infinite order of the base rational 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.equation_order_infinite() - Infinite order in Function field in y defined by y^3 + x^6 + x^4 + x^2 - - sage: K. = FunctionField(QQ); R. = PolynomialRing(K) - sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) - sage: F.equation_order_infinite() - Infinite order in Function field in y defined by y^3 - x^6 - 2*x^5 - 3*x^4 - 2*x^3 - x^2 - """ - from .order import FunctionFieldOrderInfinite_basis - b = self.primitive_integal_element_infinite() - basis = [b**i for i in range(self.degree())] - return FunctionFieldOrderInfinite_basis(tuple(basis)) - - -class FunctionField_char_zero_integral(FunctionField_char_zero, FunctionField_integral): - """ - Function fields of characteristic zero, defined by an irreducible and - separable polynomial, integral over the maximal order of the base rational - function field with a finite constant field. - """ - pass - - -class FunctionField_global_integral(FunctionField_global, FunctionField_integral): - """ - Global function fields, defined by an irreducible and separable polynomial, - integral over the maximal order of the base rational function field with a - finite constant field. - """ - pass - - -class RationalFunctionField(FunctionField): - """ - Rational function field in one variable, over an arbitrary base field. - - INPUT: - - - ``constant_field`` -- arbitrary field - - - ``names`` -- string or tuple of length 1 - - EXAMPLES:: - - sage: K. = FunctionField(GF(3)); K - Rational function field in t over Finite Field of size 3 - sage: K.gen() - t - sage: 1/t + t^3 + 5 - (t^4 + 2*t + 1)/t - - sage: K. = FunctionField(QQ); K - Rational function field in t over Rational Field - sage: K.gen() - t - sage: 1/t + t^3 + 5 - (t^4 + 5*t + 1)/t - - There are various ways to get at the underlying fields and rings - associated to a rational function field:: - - sage: K. = FunctionField(GF(7)) - sage: K.base_field() - Rational function field in t over Finite Field of size 7 - sage: K.field() - Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 - sage: K.constant_field() - Finite Field of size 7 - sage: K.maximal_order() - Maximal order of Rational function field in t over Finite Field of size 7 - - sage: K. = FunctionField(QQ) - sage: K.base_field() - Rational function field in t over Rational Field - sage: K.field() - Fraction Field of Univariate Polynomial Ring in t over Rational Field - sage: K.constant_field() - Rational Field - sage: K.maximal_order() - Maximal order of Rational function field in t over Rational Field - - We define a morphism:: - - sage: K. = FunctionField(QQ) - sage: L = FunctionField(QQ, 'tbar') # give variable name as second input - sage: K.hom(L.gen()) - Function Field morphism: - From: Rational function field in t over Rational Field - To: Rational function field in tbar over Rational Field - Defn: t |--> tbar - - Here are some calculations over a number field:: - - sage: R. = FunctionField(QQ) - sage: L. = R[] - sage: F. = R.extension(y^2 - (x^2+1)) - sage: (y/x).divisor() - - Place (x, y - 1) - - Place (x, y + 1) - + Place (x^2 + 1, y) - - sage: A. = QQ[] - sage: NF. = NumberField(z^2+1) - sage: R. = FunctionField(NF) - sage: L. = R[] - sage: F. = R.extension(y^2 - (x^2+1)) - - sage: (x/y*x.differential()).divisor() - -2*Place (1/x, 1/x*y - 1) - - 2*Place (1/x, 1/x*y + 1) - + Place (x, y - 1) - + Place (x, y + 1) - - sage: (x/y).divisor() - - Place (x - i, y) - + Place (x, y - 1) - + Place (x, y + 1) - - Place (x + i, y) - - """ - Element = FunctionFieldElement_rational - - def __init__(self, constant_field, names, category=None): - """ - Initialize. - - EXAMPLES:: - - sage: K. = FunctionField(CC); K - Rational function field in t over Complex Field with 53 bits of precision - sage: TestSuite(K).run() # long time (5s) - - sage: FunctionField(QQ[I], 'alpha') - Rational function field in alpha over Number Field in I with defining polynomial x^2 + 1 with I = 1*I - - Must be over a field:: - - sage: FunctionField(ZZ, 't') - Traceback (most recent call last): - ... - TypeError: constant_field must be a field - """ - if names is None: - raise ValueError("variable name must be specified") - elif not isinstance(names, tuple): - names = (names, ) - if not constant_field.is_field(): - raise TypeError("constant_field must be a field") - - self._constant_field = constant_field - - 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())) - - from sage.categories.sets_with_partial_maps import SetsWithPartialMaps - from sage.categories.morphism import SetMorphism - R.register_conversion(SetMorphism(self.Hom(R, SetsWithPartialMaps()), self._to_polynomial)) - - self._gen = self(R.gen()) - - def __reduce__(self): - """ - Return the arguments which were used to create this instance. The - rationale for this is explained in the documentation of - :class:`UniqueRepresentation`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: clazz,args = K.__reduce__() - sage: clazz(*args) - Rational function field in x over Rational Field - """ - from .constructor import FunctionField - return FunctionField, (self._constant_field, self._names) - - def __hash__(self): - """ - Return hash of the function field. - - The hash is formed from the constant field and the variable names. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: hash(K) == hash((K.constant_base_field(), K.variable_names())) - True - - """ - return self._hash - - def _repr_(self): - """ - Return string representation of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K._repr_() - 'Rational function field in t over Rational Field' - """ - return "Rational function field in %s over %s"%( - self.variable_name(), self._constant_field) - - def _element_constructor_(self, x): - r""" - Coerce ``x`` into an element of the function field, possibly not canonically. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: a = K._element_constructor_(K.maximal_order().gen()); a - t - sage: a.parent() - Rational function field in t over Rational Field - - TESTS: - - Conversion of a string:: - - sage: K('t') - t - sage: K('1/t') - 1/t - - Conversion of a constant polynomial over the function field:: - - sage: K(K.polynomial_ring().one()) - 1 - - Some indirect test of conversion:: - - sage: S. = K[] - sage: I = S*[x^2 - y^2, y-t] - sage: I.groebner_basis() - [x^2 - t^2, y - t] - - """ - if isinstance(x, FunctionFieldElement): - return self.element_class(self, self._field(x._x)) - try: - x = self._field(x) - except TypeError as Err: - try: - if x.parent() is self.polynomial_ring(): - return x[0] - except AttributeError: - pass - raise Err - return self.element_class(self, x) - - def _to_constant_base_field(self, f): - r""" - Return ``f`` as an element of the constant base field. - - INPUT: - - - ``f`` -- element of the rational function field which is a - constant of the underlying rational function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K._to_constant_base_field(K(1)) - 1 - sage: K._to_constant_base_field(K(x)) - Traceback (most recent call last): - ... - ValueError: only constants can be converted into the constant base field but x is not a constant - - TESTS: - - Verify that :trac:`21872` has been resolved:: - - sage: K(1) in QQ - True - sage: x in QQ - False - - """ - K = self.constant_base_field() - if f.denominator() in K and f.numerator() in K: - # When K is not exact, f.denominator() might not be an exact 1, so - # we need to divide explicitly to get the correct precision - return K(f.numerator()) / K(f.denominator()) - raise ValueError("only constants can be converted into the constant base field but %r is not a constant"%(f,)) - - def _to_polynomial(self, f): - """ - If ``f`` is integral, return it as a polynomial. - - INPUT: - - - ``f`` -- an element of this rational function field whose denominator is a constant. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K._ring(x) # indirect doctest - x - """ - K = f.parent().constant_base_field() - if f.denominator() in K: - return f.numerator()/K(f.denominator()) - raise ValueError("only polynomials can be converted to the underlying polynomial ring") - - def _to_bivariate_polynomial(self, f): - """ - Convert ``f`` from a univariate polynomial over the rational function - field into a bivariate polynomial and a denominator. - - INPUT: - - - ``f`` -- univariate polynomial over the function field - - OUTPUT: - - - bivariate polynomial, denominator - - EXAMPLES:: - - sage: R. = FunctionField(GF(7)) - sage: S. = R[] - sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) - sage: R._to_bivariate_polynomial(f) - (X^7*t^2 - X^4*t^5 - X^3 + t^3, t^3) - """ - v = f.list() - denom = lcm([a.denominator() for a in v]) - S = denom.parent() - x,t = S.base_ring()['%s,%s'%(f.parent().variable_name(),self.variable_name())].gens() - phi = S.hom([t]) - return sum([phi((denom * v[i]).numerator()) * x**i for i in range(len(v))]), denom - - def _factor_univariate_polynomial(self, f, proof=None): - """ - Factor the univariate polynomial f over the function field. - - INPUT: - - - ``f`` -- univariate polynomial over the function field - - EXAMPLES: - - We do a factorization over the function field over the rationals:: - - sage: R. = FunctionField(QQ) - sage: S. = R[] - sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) - sage: f.factor() # indirect doctest - (1/t) * (X - t) * (X^2 - 1/t) * (X^2 + 1/t) * (X^2 + t*X + t^2) - sage: f.factor().prod() == f - True - - We do a factorization over a finite prime field:: - - sage: R. = FunctionField(GF(7)) - sage: S. = R[] - sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) - sage: f.factor() - (1/t) * (X + 3*t) * (X + 5*t) * (X + 6*t) * (X^2 + 1/t) * (X^2 + 6/t) - sage: f.factor().prod() == f - True - - Factoring over a function field over a non-prime finite field:: - - sage: k. = GF(9) - sage: R. = FunctionField(k) - sage: S. = R[] - sage: f = (1/t)*(X^3 - a*t^3) - sage: f.factor() - (1/t) * (X + (a + 2)*t)^3 - sage: f.factor().prod() == f - True - - Factoring over a function field over a tower of finite fields:: - - sage: k. = GF(4) - sage: R. = k[] - sage: l. = k.extension(b^2 + b + a) - sage: K. = FunctionField(l) - sage: R. = K[] - sage: F = t*x - sage: F.factor(proof=False) - (x) * t - - """ - old_variable_name = f.variable_name() - # the variables of the bivariate polynomial must be distinct - if self.variable_name() == f.variable_name(): - # replace x with xx to make the variable names distinct - f = f.change_variable_name(old_variable_name + old_variable_name) - - F, d = self._to_bivariate_polynomial(f) - fac = F.factor(proof=proof) - x = f.parent().gen() - t = f.parent().base_ring().gen() - phi = F.parent().hom([x, t]) - v = [(phi(P),e) for P, e in fac] - unit = phi(fac.unit())/d - w = [] - for a, e in v: - c = a.leading_coefficient() - a = a/c - # undo any variable substitution that we introduced for the bivariate polynomial - if old_variable_name != a.variable_name(): - a = a.change_variable_name(old_variable_name) - unit *= (c**e) - if a.is_unit(): - unit *= a**e - else: - w.append((a,e)) - from sage.structure.factorization import Factorization - return Factorization(w, unit=unit) - - def extension(self, f, names=None): - """ - Create an extension `L = K[y]/(f(y))` of the rational function field. - - INPUT: - - - ``f`` -- univariate polynomial over self - - - ``names`` -- string or length-1 tuple - - OUTPUT: - - - a function field - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: K.extension(y^5 - x^3 - 3*x + x*y) - Function field in y defined by y^5 + x*y - x^3 - 3*x - - A nonintegral defining polynomial:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: K.extension(y^3 + (1/t)*y + t^3/(t+1)) - Function field in y defined by y^3 + 1/t*y + t^3/(t + 1) - - The defining polynomial need not be monic or integral:: - - sage: K.extension(t*y^3 + (1/t)*y + t^3/(t+1)) - Function field in y defined by t*y^3 + 1/t*y + t^3/(t + 1) - """ - from . import constructor - return constructor.FunctionFieldExtension(f, names) - - @cached_method - def polynomial_ring(self, var='x'): - """ - Return a polynomial ring in one variable over the rational function field. - - INPUT: - - - ``var`` -- string; name of the variable - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.polynomial_ring() - Univariate Polynomial Ring in x over Rational function field in x over Rational Field - sage: K.polynomial_ring('T') - Univariate Polynomial Ring in T over Rational function field in x over Rational Field - """ - return self[var] - - @cached_method(key=lambda self, base, basis, map: map) - def free_module(self, base=None, basis=None, map=True): - """ - Return a vector space `V` and isomorphisms from the field to `V` and - from `V` to the field. - - This function allows us to identify the elements of this field with - elements of a one-dimensional vector space over the field itself. This - method exists so that all function fields (rational or not) have the - same interface. - - INPUT: - - - ``base`` -- the base field of the vector space; must be the function - field itself (the default) - - - ``basis`` -- (ignored) a basis for the vector space - - - ``map`` -- (default ``True``), whether to return maps to and from the vector space - - OUTPUT: - - - a vector space `V` over base field - - - an isomorphism from `V` to the field - - - the inverse isomorphism from the field to `V` - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.free_module() - (Vector space of dimension 1 over Rational function field in x over Rational Field, Isomorphism: - From: Vector space of dimension 1 over Rational function field in x over Rational Field - To: Rational function field in x over Rational Field, Isomorphism: - From: Rational function field in x over Rational Field - To: Vector space of dimension 1 over Rational function field in x over Rational Field) - - TESTS:: - - sage: K.free_module() - (Vector space of dimension 1 over Rational function field in x over Rational Field, Isomorphism: - From: Vector space of dimension 1 over Rational function field in x over Rational Field - To: Rational function field in x over Rational Field, Isomorphism: - From: Rational function field in x over Rational Field - To: Vector space of dimension 1 over Rational function field in x over Rational Field) - - """ - if basis is not None: - raise NotImplementedError - from .maps import MapVectorSpaceToFunctionField, MapFunctionFieldToVectorSpace - if base is None: - base = self - elif base is not self: - raise ValueError("base must be the rational function field itself") - V = base**1 - if not map: - return V - from_V = MapVectorSpaceToFunctionField(V, self) - to_V = MapFunctionFieldToVectorSpace(self, V) - return (V, from_V, to_V) - - def random_element(self, *args, **kwds): - """ - Create a random element of the rational function field. - - Parameters are passed to the random_element method of the - underlying fraction field. - - EXAMPLES:: - - sage: FunctionField(QQ,'alpha').random_element() # random - (-1/2*alpha^2 - 4)/(-12*alpha^2 + 1/2*alpha - 1/95) - """ - return self(self._field.random_element(*args, **kwds)) - - def degree(self, base=None): - """ - Return the degree over the base field of the rational function - field. Since the base field is the rational function field itself, the - degree is 1. - - INPUT: - - - ``base`` -- the base field of the vector space; must be the function - field itself (the default) - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.degree() - 1 - """ - if base is None: - base = self - elif base is not self: - raise ValueError("base must be the rational function field itself") - from sage.rings.integer_ring import ZZ - return ZZ(1) - - def gen(self, n=0): - """ - Return the ``n``-th generator of the function field. If ``n`` is not - 0, then an IndexError is raised. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); K.gen() - t - sage: K.gen().parent() - Rational function field in t over Rational Field - sage: K.gen(1) - Traceback (most recent call last): - ... - IndexError: Only one generator. - """ - if n != 0: - raise IndexError("Only one generator.") - return self._gen - - def ngens(self): - """ - Return the number of generators, which is 1. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.ngens() - 1 - """ - return 1 - - def base_field(self): - """ - Return the base field of the rational function field, which is just - the function field itself. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)) - sage: K.base_field() - Rational function field in t over Finite Field of size 7 - """ - return self - - def hom(self, im_gens, base_morphism=None): - """ - Create a homomorphism from ``self`` to another ring. - - INPUT: - - - ``im_gens`` -- exactly one element of some ring. It must be - invertible and transcendental over the image of - ``base_morphism``; this is not checked. - - - ``base_morphism`` -- a homomorphism from the base field into the - other ring. If ``None``, try to use a coercion map. - - OUTPUT: - - - a map between function fields - - EXAMPLES: - - We make a map from a rational function field to itself:: - - sage: K. = FunctionField(GF(7)) - sage: K.hom( (x^4 + 2)/x) - Function Field endomorphism of Rational function field in x over Finite Field of size 7 - Defn: x |--> (x^4 + 2)/x - - We construct a map from a rational function field into a - non-rational extension field:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^3 + 6*x^3 + x) - sage: f = K.hom(y^2 + y + 2); f - Function Field morphism: - From: Rational function field in x over Finite Field of size 7 - To: Function field in y defined by y^3 + 6*x^3 + x - Defn: x |--> y^2 + y + 2 - sage: f(x) - y^2 + y + 2 - sage: f(x^2) - 5*y^2 + (x^3 + 6*x + 4)*y + 2*x^3 + 5*x + 4 - """ - if isinstance(im_gens, CategoryObject): - return self.Hom(im_gens).natural_map() - if not isinstance(im_gens, (list,tuple)): - im_gens = [im_gens] - if len(im_gens) != 1: - raise ValueError("there must be exactly one generator") - x = im_gens[0] - R = x.parent() - if base_morphism is None and not R.has_coerce_map_from(self.constant_field()): - raise ValueError("you must specify a morphism on the base field") - from .maps import FunctionFieldMorphism_rational - return FunctionFieldMorphism_rational(self.Hom(R), x, base_morphism) - - def field(self): - """ - Return the underlying field, forgetting the function field - structure. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)) - sage: K.field() - Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 - - .. SEEALSO:: - - :meth:`sage.rings.fraction_field.FractionField_1poly_field.function_field` - - """ - return self._field - - @cached_method - def maximal_order(self): - """ - Return the maximal order of the function field. - - Since this is a rational function field it is of the form `K(t)`, and the - maximal order is by definition `K[t]`, where `K` is the constant field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.maximal_order() - Maximal order of Rational function field in t over Rational Field - sage: K.equation_order() - Maximal order of Rational function field in t over Rational Field - """ - from .order import FunctionFieldMaximalOrder_rational - return FunctionFieldMaximalOrder_rational(self) - - equation_order = maximal_order - - @cached_method - def maximal_order_infinite(self): - """ - Return the maximal infinite order of the function field. - - By definition, this is the valuation ring of the degree valuation of - the rational function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.maximal_order_infinite() - Maximal infinite order of Rational function field in t over Rational Field - sage: K.equation_order_infinite() - Maximal infinite order of Rational function field in t over Rational Field - """ - from .order import FunctionFieldMaximalOrderInfinite_rational - return FunctionFieldMaximalOrderInfinite_rational(self) - - equation_order_infinite = maximal_order_infinite - - def constant_base_field(self): - """ - Return the field of which the rational function field is a - transcendental extension. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.constant_base_field() - Rational Field - """ - return self._constant_field - - 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. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.genus() - 0 - """ - return Integer(0) - - def change_variable_name(self, name): - r""" - Return a field isomorphic to this field with variable ``name``. - - INPUT: - - - ``name`` -- a string or a tuple consisting of a single string, the - name of the new variable - - OUTPUT: - - A triple ``F,f,t`` where ``F`` is a rational function field, ``f`` is - an isomorphism from ``F`` to this field, and ``t`` is the inverse of - ``f``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: L,f,t = K.change_variable_name('y') - sage: L,f,t - (Rational function field in y over Rational Field, - Function Field morphism: - From: Rational function field in y over Rational Field - To: Rational function field in x over Rational Field - Defn: y |--> x, - Function Field morphism: - From: Rational function field in x over Rational Field - To: Rational function field in y over Rational Field - Defn: x |--> y) - sage: L.change_variable_name('x')[0] is K - True - - """ - if isinstance(name, tuple): - if len(name) != 1: - raise ValueError("names must be a tuple with a single string") - name = name[0] - if name == self.variable_name(): - id = Hom(self,self).identity() - return self,id,id - else: - from .constructor import FunctionField - ret = FunctionField(self.constant_base_field(), name) - return ret, ret.hom(self.gen()), self.hom(ret.gen()) - - 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) - - -class RationalFunctionField_char_zero(RationalFunctionField): - """ - Rational function fields of characteristic zero. - """ - @cached_method - def higher_derivation(self): - """ - Return the higher derivation for the function field. - - This is also called the Hasse-Schmidt derivation. - - EXAMPLES:: - - sage: F. = FunctionField(QQ) - sage: d = F.higher_derivation() - sage: [d(x^5,i) for i in range(10)] - [x^5, 5*x^4, 10*x^3, 10*x^2, 5*x, 1, 0, 0, 0, 0] - sage: [d(x^9,i) for i in range(10)] - [x^9, 9*x^8, 36*x^7, 84*x^6, 126*x^5, 126*x^4, 84*x^3, 36*x^2, 9*x, 1] - """ - from .maps import FunctionFieldHigherDerivation_char_zero - return FunctionFieldHigherDerivation_char_zero(self) - - -class RationalFunctionField_global(RationalFunctionField): - """ - Rational function field over finite fields. - """ - _differentials_space = DifferentialsSpace_global - - 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(max_degree=degree - 1) - lm = R.monomial(degree) - for g in G: - h = lm + g - if h.is_irreducible(): - yield O.ideal(h).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 get_place(self, degree): - """ - Return a place of ``degree``. - - INPUT: - - - ``degree`` -- a positive integer - - EXAMPLES:: - - sage: F. = GF(2) - sage: K. = FunctionField(F) - sage: K.get_place(1) - Place (x) - sage: K.get_place(2) - Place (x^2 + x + 1) - sage: K.get_place(3) - Place (x^3 + x + 1) - sage: K.get_place(4) - Place (x^4 + x + 1) - sage: K.get_place(5) - Place (x^5 + x^2 + 1) - - """ - for p in self._places_finite(degree): - return p - - assert False, "there is a bug around" - - @cached_method - def higher_derivation(self): - """ - Return the higher derivation for the function field. - - This is also called the Hasse-Schmidt derivation. - - EXAMPLES:: - - sage: F. = FunctionField(GF(5)) - sage: d = F.higher_derivation() - sage: [d(x^5,i) for i in range(10)] - [x^5, 0, 0, 0, 0, 1, 0, 0, 0, 0] - sage: [d(x^7,i) for i in range(10)] - [x^7, 2*x^6, x^5, 0, 0, x^2, 2*x, 1, 0, 0] - """ - from .maps import RationalFunctionFieldHigherDerivation_global - return RationalFunctionFieldHigherDerivation_global(self) diff --git a/src/sage/rings/function_field/function_field_polymod.py b/src/sage/rings/function_field/function_field_polymod.py new file mode 100644 index 00000000000..76d12423004 --- /dev/null +++ b/src/sage/rings/function_field/function_field_polymod.py @@ -0,0 +1,2539 @@ +# sage.doctest: optional - sage.rings.function_field +r""" +Function Fields: extension +""" + +#***************************************************************************** +# Copyright (C) 2023 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 sage.arith.functions import lcm +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_import import LazyImport +from sage.rings.qqbar_decorators import handle_AA_and_QQbar +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.integer import Integer +from sage.categories.homset import Hom +from sage.categories.function_fields import FunctionFields + +from .element import FunctionFieldElement +from .element_polymod import FunctionFieldElement_polymod +from .function_field import FunctionField +from .function_field_rational import RationalFunctionField + + +class FunctionField_polymod(FunctionField): + """ + Function fields defined by a univariate polynomial, as an extension of the + base field. + + INPUT: + + - ``polynomial`` -- univariate polynomial over a function field + + - ``names`` -- tuple of length 1 or string; variable names + + - ``category`` -- category (default: category of function fields) + + EXAMPLES: + + We make a function field defined by a degree 5 polynomial over the + rational function field over the rational numbers:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L + Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + + We next make a function field over the above nontrivial function + field L:: + + sage: S. = L[] + sage: M. = L.extension(z^2 + y*z + y); M + Function field in z defined by z^2 + y*z + y + sage: 1/z + ((-x/(x^4 + 1))*y^4 + 2*x^2/(x^4 + 1))*z - 1 + sage: z * (1/z) + 1 + + We drill down the tower of function fields:: + + sage: M.base_field() + Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + sage: M.base_field().base_field() + Rational function field in x over Rational Field + sage: M.base_field().base_field().constant_field() + Rational Field + sage: M.constant_base_field() + Rational Field + + .. WARNING:: + + It is not checked if the polynomial used to define the function field is irreducible + Hence it is not guaranteed that this object really is a field! + This is illustrated below. + + :: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(x^2 - y^2) + sage: (y - x)*(y + x) + 0 + sage: 1/(y - x) + 1 + sage: y - x == 0; y + x == 0 + False + False + """ + 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. + + EXAMPLES: + + We create an extension of a function field:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L = K.extension(y^5 - x^3 - 3*x + x*y); L + Function field in y defined by y^5 + x*y - x^3 - 3*x + sage: TestSuite(L).run(max_runs=512) # long time (15s) + + We can set the variable name, which doesn't have to be y:: + + sage: L. = K.extension(y^5 - x^3 - 3*x + x*y); L + Function field in w defined by w^5 + x*w - x^3 - 3*x + + TESTS: + + Test that :trac:`17033` is fixed:: + + sage: K. = FunctionField(QQ) + sage: R. = QQ[] + sage: M. = K.extension(x^7 - x - t) + sage: M(x) + z + sage: M('z') + z + sage: M('x') + Traceback (most recent call last): + ... + TypeError: unable to evaluate 'x' in Fraction Field of Univariate + Polynomial Ring in t over Rational Field + """ + from sage.rings.polynomial.polynomial_element import Polynomial + if polynomial.parent().ngens() > 1 or not isinstance(polynomial, Polynomial): + raise TypeError("polynomial must be univariate a polynomial") + if names is None: + names = (polynomial.variable_name(), ) + elif names != polynomial.variable_name(): + polynomial = polynomial.change_variable_name(names) + if polynomial.degree() <= 0: + raise ValueError("polynomial must have positive degree") + base_field = polynomial.base_ring() + if not isinstance(base_field, FunctionField): + raise TypeError("polynomial must be over a FunctionField") + + self._base_field = base_field + self._polynomial = polynomial + + FunctionField.__init__(self, base_field, names=names, + category=FunctionFields().or_subcategory(category)) + + from .place_polymod import FunctionFieldPlace_polymod + self._place_class = FunctionFieldPlace_polymod + + 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()) + + def __hash__(self): + """ + Return hash of the function field. + + The hash value is equal to the hash of the defining polynomial. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L = K.extension(y^5 - x^3 - 3*x + x*y) + sage: hash(L) == hash(L.polynomial()) + True + """ + return self._hash + + def _element_constructor_(self, x): + r""" + Make ``x`` into an element of the function field, possibly not canonically. + + INPUT: + + - ``x`` -- element + + TESTS:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L._element_constructor_(L.polynomial_ring().gen()) + y + """ + if isinstance(x, FunctionFieldElement): + return self.element_class(self, self._ring(x.element())) + return self.element_class(self, self._ring(x)) + + def gen(self, n=0): + """ + Return the `n`-th generator of the function field. By default, `n` is 0; any other + value of `n` leads to an error. The generator is the class of `y`, if we view + the function field as being presented as `K[y]/(f(y))`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.gen() + y + sage: L.gen(1) + Traceback (most recent call last): + ... + IndexError: there is only one generator + """ + if n != 0: + raise IndexError("there is only one generator") + return self._gen + + def ngens(self): + """ + Return the number of generators of the function field over its base + field. This is by definition 1. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.ngens() + 1 + """ + return 1 + + def _to_base_field(self, f): + r""" + Return ``f`` as an element of the :meth:`base_field`. + + INPUT: + + - ``f`` -- element of the function field which lies in the base + field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: L._to_base_field(L(x)) + x + sage: L._to_base_field(y) + Traceback (most recent call last): + ... + ValueError: y is not an element of the base field + + TESTS: + + Verify that :trac:`21872` has been resolved:: + + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + + sage: M(1) in QQ + True + sage: M(y) in L + True + sage: M(x) in K + True + sage: z in K + False + """ + K = self.base_field() + if f.element().is_constant(): + return K(f.element()) + raise ValueError("%r is not an element of the base field"%(f,)) + + def _to_constant_base_field(self, f): + """ + Return ``f`` as an element of the :meth:`constant_base_field`. + + INPUT: + + - ``f`` -- element of the rational function field which is a + constant + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: L._to_constant_base_field(L(1)) + 1 + sage: L._to_constant_base_field(y) + Traceback (most recent call last): + ... + ValueError: y is not an element of the base field + + TESTS: + + Verify that :trac:`21872` has been resolved:: + + sage: L(1) in QQ + True + sage: y in QQ + False + """ + return self.base_field()._to_constant_base_field(self._to_base_field(f)) + + def monic_integral_model(self, names=None): + """ + Return a function field isomorphic to this field but which is an + extension of a rational function field with defining polynomial that is + monic and integral over the constant base field. + + INPUT: + + - ``names`` -- a string or a tuple of up to two strings (default: + ``None``), the name of the generator of the field, and the name of + the generator of the underlying rational function field (if a tuple); + if not given, then the names are chosen automatically. + + OUTPUT: + + A triple ``(F,f,t)`` where ``F`` is a function field, ``f`` is an + isomorphism from ``F`` to this field, and ``t`` is the inverse of + ``f``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(x^2*y^5 - 1/x); L + Function field in y defined by x^2*y^5 - 1/x + sage: A, from_A, to_A = L.monic_integral_model('z') + sage: A + Function field in z defined by z^5 - x^12 + sage: from_A + Function Field morphism: + From: Function field in z defined by z^5 - x^12 + To: Function field in y defined by x^2*y^5 - 1/x + Defn: z |--> x^3*y + x |--> x + sage: to_A + Function Field morphism: + From: Function field in y defined by x^2*y^5 - 1/x + To: Function field in z defined by z^5 - x^12 + Defn: y |--> 1/x^3*z + x |--> x + sage: to_A(y) + 1/x^3*z + sage: from_A(to_A(y)) + y + sage: from_A(to_A(1/y)) + x^3*y^4 + sage: from_A(to_A(1/y)) == 1/y + True + + This also works for towers of function fields:: + + sage: R. = L[] + sage: M. = L.extension(z^2*y - 1/x) + sage: M.monic_integral_model() + (Function field in z_ defined by z_^10 - x^18, + Function Field morphism: + From: Function field in z_ defined by z_^10 - x^18 + To: Function field in z defined by y*z^2 - 1/x + Defn: z_ |--> x^2*z + x |--> x, Function Field morphism: + From: Function field in z defined by y*z^2 - 1/x + To: Function field in z_ defined by z_^10 - x^18 + Defn: z |--> 1/x^2*z_ + y |--> 1/x^15*z_^8 + x |--> x) + + TESTS: + + If the field is already a monic integral extension, then it is returned + unchanged:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: L.monic_integral_model() + (Function field in y defined by y^2 - x, + Function Field endomorphism of Function field in y defined by y^2 - x + Defn: y |--> y + x |--> x, Function Field endomorphism of Function field in y defined by y^2 - x + Defn: y |--> y + x |--> x) + + unless ``names`` does not match with the current names:: + + sage: L.monic_integral_model(names=('yy','xx')) + (Function field in yy defined by yy^2 - xx, + Function Field morphism: + From: Function field in yy defined by yy^2 - xx + To: Function field in y defined by y^2 - x + Defn: yy |--> y + xx |--> x, Function Field morphism: + From: Function field in y defined by y^2 - x + To: Function field in yy defined by yy^2 - xx + Defn: y |--> yy + x |--> xx) + + """ + if names: + if not isinstance(names, tuple): + names = (names,) + if len(names) > 2: + raise ValueError("names must contain at most 2 entries") + + if self.base_field() is not self.rational_function_field(): + L,from_L,to_L = self.simple_model() + ret,ret_to_L,L_to_ret = L.monic_integral_model(names) + from_ret = ret.hom( [from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))] ) + to_ret = self.hom( [L_to_ret(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())] ) + return ret, from_ret, to_ret + else: + if self.polynomial().is_monic() and all(c.denominator().is_one() for c in self.polynomial()): + # self is already monic and integral + if names is None or names == (): + names = (self.variable_name(),) + return self.change_variable_name(names) + else: + if not names: + names = (self.variable_name()+"_",) + if len(names) == 1: + names = (names[0], self.rational_function_field().variable_name()) + + g, d = self._make_monic_integral(self.polynomial()) + K,from_K,to_K = self.base_field().change_variable_name(names[1]) + g = g.map_coefficients(to_K) + ret = K.extension(g, names=names[0]) + from_ret = ret.hom([self.gen() * d, self.base_field().gen()]) + to_ret = self.hom([ret.gen() / d, ret.base_field().gen()]) + return ret, from_ret, to_ret + + def _make_monic_integral(self, f): + """ + Return a monic integral polynomial `g` and an element `d` of the base + field such that `g(y*d)=0` where `y` is a root of `f`. + + INPUT: + + - ``f`` -- polynomial + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(x^2*y^5 - 1/x) + sage: g, d = L._make_monic_integral(L.polynomial()); g,d + (y^5 - x^12, x^3) + sage: (y*d).is_integral() + True + sage: g.is_monic() + True + sage: g(y*d) + 0 + """ + R = f.base_ring() + if not isinstance(R, RationalFunctionField): + raise NotImplementedError + + # make f monic + n = f.degree() + c = f.leading_coefficient() + if c != 1: + f = f / c + + # find lcm of denominators + # would be good to replace this by minimal... + d = lcm([b.denominator() for b in f.list() if b]) + if d != 1: + x = f.parent().gen() + g = (d**n) * f(x/d) + else: + g = f + return g, d + + def constant_field(self): + """ + Return the algebraic closure of the constant field of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^5 - x) # optional - sage.rings.finite_rings + sage: L.constant_field() # optional - sage.rings.finite_rings + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + def constant_base_field(self): + """ + Return the base constant field of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L + Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + sage: L.constant_base_field() + Rational Field + sage: S. = L[] + sage: M. = L.extension(z^2 - y) + sage: M.constant_base_field() + Rational Field + """ + return self.base_field().constant_base_field() + + @cached_method(key=lambda self, base: self.base_field() if base is None else base) + def degree(self, base=None): + """ + Return the degree of the function field over the function field ``base``. + + INPUT: + + - ``base`` -- a function field (default: ``None``), a function field + from which this field has been constructed as a finite extension. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L + Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + sage: L.degree() + 5 + sage: L.degree(L) + 1 + + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: M.degree(L) + 2 + sage: M.degree(K) + 10 + + TESTS:: + + sage: L.degree(M) + Traceback (most recent call last): + ... + ValueError: base must be the rational function field itself + + """ + if base is None: + base = self.base_field() + if base is self: + from sage.rings.integer_ring import ZZ + return ZZ(1) + return self._polynomial.degree() * self.base_field().degree(base) + + def _repr_(self): + """ + Return the string representation of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L._repr_() + 'Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x' + """ + return "Function field in %s defined by %s"%(self.variable_name(), self._polynomial) + + def base_field(self): + """ + Return the base field of the function field. This function field is + presented as `L = K[y]/(f(y))`, and the base field is by definition the + field `K`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.base_field() + Rational function field in x over Rational Field + """ + return self._base_field + + def random_element(self, *args, **kwds): + """ + Create a random element of the function field. Parameters are passed + onto the random_element method of the base_field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - (x^2 + x)) + sage: L.random_element() # random + ((x^2 - x + 2/3)/(x^2 + 1/3*x - 1))*y^2 + ((-1/4*x^2 + 1/2*x - 1)/(-5/2*x + 2/3))*y + + (-1/2*x^2 - 4)/(-12*x^2 + 1/2*x - 1/95) + """ + return self(self._ring.random_element(degree=self.degree(), *args, **kwds)) + + def polynomial(self): + """ + Return the univariate polynomial that defines the function field, that + is, the polynomial `f(y)` so that the function field is of the form + `K[y]/(f(y))`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.polynomial() + y^5 - 2*x*y + (-x^4 - 1)/x + """ + return self._polynomial + + def is_separable(self, base=None): + r""" + Return whether this is a separable extension of ``base``. + + INPUT: + + - ``base`` -- a function field from which this field has been created + as an extension or ``None`` (default: ``None``); if ``None``, then + return whether this is a separable extension over its base field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: L.is_separable() # optional - sage.rings.finite_rings + False + sage: R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^3 - y) # optional - sage.rings.finite_rings + sage: M.is_separable() # optional - sage.rings.finite_rings + True + sage: M.is_separable(K) # optional - sage.rings.finite_rings + False + + sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # optional - sage.rings.finite_rings + sage: L.is_separable() # optional - sage.rings.finite_rings + True + + sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^5 - 1) # optional - sage.rings.finite_rings + sage: L.is_separable() # optional - sage.rings.finite_rings + False + + """ + if base is None: + base = self.base_field() + for k in self._intermediate_fields(base)[:-1]: + f = k.polynomial() + g = f.derivative() + if f.gcd(g).degree() != 0: + return False + return True + + def polynomial_ring(self): + """ + Return the polynomial ring used to represent elements of the + function field. If we view the function field as being presented + as `K[y]/(f(y))`, then this function returns the ring `K[y]`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.polynomial_ring() + Univariate Polynomial Ring in y over Rational function field in x over Rational Field + """ + return self._ring + + @cached_method(key=lambda self, base, basis, map: (self.base_field() if base is None else base, basis, map)) + def free_module(self, base=None, basis=None, map=True): + """ + Return a vector space and isomorphisms from the field to and from the + vector space. + + This function allows us to identify the elements of this field with + elements of a vector space over the base field, which is useful for + representation and arithmetic with orders, ideals, etc. + + INPUT: + + - ``base`` -- a function field (default: ``None``), the returned vector + space is over this subfield `R`, which defaults to the base field of this + function field. + + - ``basis`` -- a basis for this field over the base. + + - ``maps`` -- boolean (default ``True``), whether to return + `R`-linear maps to and from `V`. + + OUTPUT: + + - a vector space over the base function field + + - an isomorphism from the vector space to the field (if requested) + + - an isomorphism from the field to the vector space (if requested) + + EXAMPLES: + + We define a function field:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)); L + Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + + We get the vector spaces, and maps back and forth:: + + sage: V, from_V, to_V = L.free_module() # optional - sage.modules + sage: V # optional - sage.modules + Vector space of dimension 5 over Rational function field in x over Rational Field + sage: from_V # optional - sage.modules + Isomorphism: + From: Vector space of dimension 5 over Rational function field in x over Rational Field + To: Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + sage: to_V # optional - sage.modules + Isomorphism: + From: Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + To: Vector space of dimension 5 over Rational function field in x over Rational Field + + We convert an element of the vector space back to the function field:: + + sage: from_V(V.1) # optional - sage.modules + y + + We define an interesting element of the function field:: + + sage: a = 1/L.0; a # optional - sage.modules + (x/(x^4 + 1))*y^4 - 2*x^2/(x^4 + 1) + + We convert it to the vector space, and get a vector over the base field:: + + sage: to_V(a) # optional - sage.modules + (-2*x^2/(x^4 + 1), 0, 0, 0, x/(x^4 + 1)) + + We convert to and back, and get the same element:: + + sage: from_V(to_V(a)) == a # optional - sage.modules + True + + In the other direction:: + + sage: v = x*V.0 + (1/x)*V.1 # optional - sage.modules + sage: to_V(from_V(v)) == v # optional - sage.modules + True + + And we show how it works over an extension of an extension field:: + + sage: R2. = L[]; M. = L.extension(z^2 - y) + sage: M.free_module() # optional - sage.modules + (Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x, Isomorphism: + From: Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + To: Function field in z defined by z^2 - y, Isomorphism: + From: Function field in z defined by z^2 - y + To: Vector space of dimension 2 over Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x) + + We can also get the vector space of ``M`` over ``K``:: + + sage: M.free_module(K) # optional - sage.modules + (Vector space of dimension 10 over Rational function field in x over Rational Field, Isomorphism: + From: Vector space of dimension 10 over Rational function field in x over Rational Field + To: Function field in z defined by z^2 - y, Isomorphism: + From: Function field in z defined by z^2 - y + To: Vector space of dimension 10 over Rational function field in x over Rational Field) + + """ + if basis is not None: + raise NotImplementedError + from .maps import MapVectorSpaceToFunctionField, MapFunctionFieldToVectorSpace + if base is None: + base = self.base_field() + degree = self.degree(base) + V = base**degree + if not map: + return V + from_V = MapVectorSpaceToFunctionField(V, self) + to_V = MapFunctionFieldToVectorSpace(self, V) + return (V, from_V, to_V) + + def maximal_order(self): + """ + Return the maximal order of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.maximal_order() + Maximal order of Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + """ + from .order_polymod import FunctionFieldMaximalOrder_polymod + return FunctionFieldMaximalOrder_polymod(self) + + def maximal_order_infinite(self): + """ + Return the maximal infinite order of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: L.maximal_order_infinite() + Maximal infinite order of Function field in y defined by y^5 - 2*x*y + (-x^4 - 1)/x + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: F.maximal_order_infinite() # optional - sage.rings.finite_rings + Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: L.maximal_order_infinite() # optional - sage.rings.finite_rings + Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x + """ + from .order_polymod import FunctionFieldMaximalOrderInfinite_polymod + return FunctionFieldMaximalOrderInfinite_polymod(self) + + def different(self): + """ + Return the different of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: F.different() # optional - sage.rings.finite_rings + 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 equation_order(self): + """ + Return the equation order of the function field. + + If we view the function field as being presented as `K[y]/(f(y))`, then + the order generated by the class of `y` is returned. If `f` + is not monic, then :meth:`_make_monic_integral` is called, and instead + we get the order generated by some integral multiple of a root of `f`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) + sage: O = L.equation_order() + sage: O.basis() + (1, x*y, x^2*y^2, x^3*y^3, x^4*y^4) + + We try an example, in which the defining polynomial is not + monic and is not integral:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(x^2*y^5 - 1/x); L + Function field in y defined by x^2*y^5 - 1/x + sage: O = L.equation_order() + sage: O.basis() + (1, x^3*y, x^6*y^2, x^9*y^3, x^12*y^4) + """ + d = self._make_monic_integral(self.polynomial())[1] + return self.order(d*self.gen(), check=False) + + def hom(self, im_gens, base_morphism=None): + """ + Create a homomorphism from the function field to another function field. + + INPUT: + + - ``im_gens`` -- list of images of the generators of the function field + and of successive base rings. + + - ``base_morphism`` -- homomorphism of the base ring, after the + ``im_gens`` are used. Thus if ``im_gens`` has length 2, then + ``base_morphism`` should be a morphism from the base ring of the base + ring of the function field. + + EXAMPLES: + + We create a rational function field, and a quadratic extension of it:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + + We make the field automorphism that sends y to -y:: + + sage: f = L.hom(-y); f + Function Field endomorphism of Function field in y defined by y^2 - x^3 - 1 + Defn: y |--> -y + + Evaluation works:: + + sage: f(y*x - 1/x) + -x*y - 1/x + + We try to define an invalid morphism:: + + sage: f = L.hom(y + 1) + Traceback (most recent call last): + ... + ValueError: invalid morphism + + We make a morphism of the base rational function field:: + + sage: phi = K.hom(x + 1); phi + Function Field endomorphism of Rational function field in x over Rational Field + Defn: x |--> x + 1 + sage: phi(x^3 - 3) + x^3 + 3*x^2 + 3*x - 2 + sage: (x+1)^3 - 3 + x^3 + 3*x^2 + 3*x - 2 + + We make a morphism by specifying where the generators and the + base generators go:: + + sage: L.hom([-y, x]) + Function Field endomorphism of Function field in y defined by y^2 - x^3 - 1 + Defn: y |--> -y + x |--> x + + You can also specify a morphism on the base:: + + sage: R1. = K[] + sage: L1. = K.extension(q^2 - (x+1)^3 - 1) + sage: L.hom(q, base_morphism=phi) + Function Field morphism: + From: Function field in y defined by y^2 - x^3 - 1 + To: Function field in q defined by q^2 - x^3 - 3*x^2 - 3*x - 2 + Defn: y |--> q + x |--> x + 1 + + We make another extension of a rational function field:: + + sage: K2. = FunctionField(QQ); R2. = K2[] + sage: L2. = K2.extension((4*w)^2 - (t+1)^3 - 1) + + We define a morphism, by giving the images of generators:: + + sage: f = L.hom([4*w, t + 1]); f + Function Field morphism: + From: Function field in y defined by y^2 - x^3 - 1 + To: Function field in w defined by 16*w^2 - t^3 - 3*t^2 - 3*t - 2 + Defn: y |--> 4*w + x |--> t + 1 + + Evaluation works, as expected:: + + sage: f(y+x) + 4*w + t + 1 + sage: f(x*y + x/(x^2+1)) + (4*t + 4)*w + (t + 1)/(t^2 + 2*t + 2) + + We make another extension of a rational function field:: + + sage: K3. = FunctionField(QQ); R3. = K3[] + sage: L3. = K3.extension(yy^2 - xx^3 - 1) + + This is the function field L with the generators exchanged. We define a morphism to L:: + + sage: g = L3.hom([x,y]); g + Function Field morphism: + From: Function field in xx defined by -xx^3 + yy^2 - 1 + To: Function field in y defined by y^2 - x^3 - 1 + Defn: xx |--> x + yy |--> y + + """ + if not isinstance(im_gens, (list,tuple)): + im_gens = [im_gens] + if len(im_gens) == 0: + raise ValueError("no images specified") + + if len(im_gens) > 1: + base_morphism = self.base_field().hom(im_gens[1:], base_morphism) + + # the codomain of this morphism is the field containing all the im_gens + codomain = im_gens[0].parent() + if base_morphism is not None: + from sage.categories.pushout import pushout + codomain = pushout(codomain, base_morphism.codomain()) + + from .maps import FunctionFieldMorphism_polymod + return FunctionFieldMorphism_polymod(self.Hom(codomain), im_gens[0], base_morphism) + + @cached_method + def genus(self): + """ + Return the genus of the function field. + + For now, the genus is computed using Singular. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^3 - (x^3 + 2*x*y + 1/x)) + sage: L.genus() + 3 + """ + # Unfortunately Singular can not compute the genus with the + # polynomial_ring()._singular_ object because genus method only accepts + # a ring of transcendental degree 2 over a prime field not a ring of + # transcendental degree 1 over a rational function field of one variable + + if (isinstance(self._base_field, RationalFunctionField) and + self._base_field.constant_field().is_prime_field()): + from sage.interfaces.singular import singular + + # making the auxiliary ring which only has polynomials + # with integral coefficients. + tmpAuxRing = PolynomialRing(self._base_field.constant_field(), + str(self._base_field.gen())+','+str(self._ring.gen())) + intMinPoly, d = self._make_monic_integral(self._polynomial) + curveIdeal = tmpAuxRing.ideal(intMinPoly) + + singular.lib('normal.lib') #loading genus method in Singular + return int(curveIdeal._singular_().genus()) + + else: + raise NotImplementedError("computation of genus over non-prime " + "constant fields not implemented yet") + + def _simple_model(self, name='v'): + r""" + Return a finite extension `N/K(x)` isomorphic to the tower of + extensions `M/L/K(x)` with `K` perfect. + + Helper method for :meth:`simple_model`. + + INPUT: + + - ``name`` -- a string, the name of the generator of `N` + + ALGORITHM: + + Since `K` is perfect, the extension `M/K(x)` is simple, i.e., generated + by a single element [BM1940]_. Therefore, there are only finitely many + intermediate fields (Exercise 3.6.7 in [Bo2009]_). + Let `a` be a generator of `M/L` and let `b` be a generator of `L/K(x)`. + For some `i` the field `N_i=K(x)(a+x^ib)` is isomorphic to `M` and so + it is enough to test for all terms of the form `a+x^ib` whether they + generate a field of the right degree. + Indeed, suppose for contradiction that for all `i` we had `N_i\neq M`. + Then `N_i=N_j` for some `i,j`. Thus `(a+x^ib)-(a+x^jb)=b(x^i-x^j)\in + N_j` and so `b\in N_j`. Similarly, + `a+x^ib-x^{i-j}(a+x^jb)=a(1+x^{i-j})\in N_j` and so `a\in N_j`. + Therefore, `N_j=M`. + + TESTS:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: M._simple_model() + (Function field in v defined by v^4 - x, + Function Field morphism: + From: Function field in v defined by v^4 - x + To: Function field in z defined by z^2 - y + Defn: v |--> z, + Function Field morphism: + From: Function field in z defined by z^2 - y + To: Function field in v defined by v^4 - x + Defn: z |--> v + y |--> v^2) + + Check that this also works for inseparable extensions:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings + sage: M._simple_model() # optional - sage.rings.finite_rings + (Function field in v defined by v^4 + x, + Function Field morphism: + From: Function field in v defined by v^4 + x + To: Function field in z defined by z^2 + y + Defn: v |--> z, + Function Field morphism: + From: Function field in z defined by z^2 + y + To: Function field in v defined by v^4 + x + Defn: z |--> v + y |--> v^2) + + An example where the generator of the last extension does not generate + the extension of the rational function field:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^3 - 1) # optional - sage.rings.finite_rings + sage: M._simple_model() # optional - sage.rings.finite_rings + (Function field in v defined by v^6 + x*v^4 + x^2*v^2 + x^3 + 1, + Function Field morphism: + From: Function field in v defined by v^6 + x*v^4 + x^2*v^2 + x^3 + 1 + To: Function field in z defined by z^3 + 1 + Defn: v |--> z + y, + Function Field morphism: + From: Function field in z defined by z^3 + 1 + To: Function field in v defined by v^6 + x*v^4 + x^2*v^2 + x^3 + 1 + Defn: z |--> v^4 + x^2 + y |--> v^4 + v + x^2) + + """ + M = self + L = M.base_field() + K = L.base_field() + + assert(isinstance(K, RationalFunctionField)) + assert(K is not L) + assert(L is not M) + + if not K.constant_field().is_perfect(): + raise NotImplementedError("simple_model() only implemented over perfect constant fields") + + x = K.gen() + b = L.gen() + a = M.gen() + + # using a+x^i*b tends to lead to huge powers of x in the minimal + # polynomial of the resulting field; it is better to try terms of + # the form a+i*b first (but in characteristic p>0 there are only + # finitely many of these) + # We systematically try elements of the form a+b*factor*x^exponent + factor = self.constant_base_field().zero() + exponent = 0 + while True: + v = M(a+b*factor*x**exponent) + minpoly = v.matrix(K).minpoly() + if minpoly.degree() == M.degree()*L.degree(): + break + factor += 1 + if factor == 0: + factor = self.constant_base_field().one() + exponent += 1 + + N = K.extension(minpoly, names=(name,)) + + # the morphism N -> M, v |-> v + N_to_M = N.hom(v) + + # the morphism M -> N, b |-> M_b, a |-> M_a + V, V_to_M, M_to_V = M.free_module(K) + V, V_to_N, N_to_V = N.free_module(K) + from sage.matrix.matrix_space import MatrixSpace + MS = MatrixSpace(V.base_field(), V.dimension()) + # the power basis of v over K + B = [M_to_V(v**i) for i in range(V.dimension())] + B = MS(B) + M_b = V_to_N(B.solve_left(M_to_V(b))) + M_a = V_to_N(B.solve_left(M_to_V(a))) + M_to_N = M.hom([M_a,M_b]) + + return N, N_to_M, M_to_N + + @cached_method + def simple_model(self, name=None): + """ + Return a function field isomorphic to this field which is a simple + extension of a rational function field. + + INPUT: + + - ``name`` -- a string (default: ``None``), the name of generator of + the simple extension. If ``None``, then the name of the generator + will be the same as the name of the generator of this function field. + + OUTPUT: + + A triple ``(F,f,t)`` where ``F`` is a field isomorphic to this field, + ``f`` is an isomorphism from ``F`` to this function field and ``t`` is + the inverse of ``f``. + + EXAMPLES: + + A tower of four function fields:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(z^2 - x); R. = L[] + sage: M. = L.extension(u^2 - z); R. = M[] + sage: N. = M.extension(v^2 - u) + + The fields N and M as simple extensions of K:: + + sage: N.simple_model() + (Function field in v defined by v^8 - x, + Function Field morphism: + From: Function field in v defined by v^8 - x + To: Function field in v defined by v^2 - u + Defn: v |--> v, + Function Field morphism: + From: Function field in v defined by v^2 - u + To: Function field in v defined by v^8 - x + Defn: v |--> v + u |--> v^2 + z |--> v^4 + x |--> x) + sage: M.simple_model() + (Function field in u defined by u^4 - x, + Function Field morphism: + From: Function field in u defined by u^4 - x + To: Function field in u defined by u^2 - z + Defn: u |--> u, + Function Field morphism: + From: Function field in u defined by u^2 - z + To: Function field in u defined by u^4 - x + Defn: u |--> u + z |--> u^2 + x |--> x) + + An optional parameter ``name`` can be used to set the name of the + generator of the simple extension:: + + sage: M.simple_model(name='t') + (Function field in t defined by t^4 - x, Function Field morphism: + From: Function field in t defined by t^4 - x + To: Function field in u defined by u^2 - z + Defn: t |--> u, Function Field morphism: + From: Function field in u defined by u^2 - z + To: Function field in t defined by t^4 - x + Defn: u |--> t + z |--> t^2 + x |--> x) + + An example with higher degrees:: + + sage: K. = FunctionField(GF(3)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^5 - x); R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^3 - x) # optional - sage.rings.finite_rings + sage: M.simple_model() # optional - sage.rings.finite_rings + (Function field in z defined by z^15 + x*z^12 + x^2*z^9 + 2*x^3*z^6 + 2*x^4*z^3 + 2*x^5 + 2*x^3, + Function Field morphism: + From: Function field in z defined by z^15 + x*z^12 + x^2*z^9 + 2*x^3*z^6 + 2*x^4*z^3 + 2*x^5 + 2*x^3 + To: Function field in z defined by z^3 + 2*x + Defn: z |--> z + y, + Function Field morphism: + From: Function field in z defined by z^3 + 2*x + To: Function field in z defined by z^15 + x*z^12 + x^2*z^9 + 2*x^3*z^6 + 2*x^4*z^3 + 2*x^5 + 2*x^3 + Defn: z |--> 2/x*z^6 + 2*z^3 + z + 2*x + y |--> 1/x*z^6 + z^3 + x + x |--> x) + + This also works for inseparable extensions:: + + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x); R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings + sage: M.simple_model() # optional - sage.rings.finite_rings + (Function field in z defined by z^4 + x, Function Field morphism: + From: Function field in z defined by z^4 + x + To: Function field in z defined by z^2 + y + Defn: z |--> z, Function Field morphism: + From: Function field in z defined by z^2 + y + To: Function field in z defined by z^4 + x + Defn: z |--> z + y |--> z^2 + x |--> x) + """ + if name is None: + name = self.variable_name() + + if isinstance(self.base_field(), RationalFunctionField): + # the extension is simple already + if name == self.variable_name(): + id = Hom(self,self).identity() + return self, id, id + else: + ret = self.base_field().extension(self.polynomial(), names=(name,)) + f = ret.hom(self.gen()) + t = self.hom(ret.gen()) + return ret, f, t + else: + # recursively collapse the tower of fields + base = self.base_field() + base_, from_base_, to_base_ = base.simple_model() + self_ = base_.extension(self.polynomial().map_coefficients(to_base_), names=(name,)) + gens_in_base_ = [to_base_(k.gen()) + for k in base._intermediate_fields(base.rational_function_field())] + to_self_ = self.hom([self_.gen()]+gens_in_base_) + from_self_ = self_.hom([self.gen(),from_base_(base_.gen())]) + + # now collapse self_/base_/K(x) + ret, ret_to_self_, self__to_ret = self_._simple_model(name) + ret_to_self = ret.hom(from_self_(ret_to_self_(ret.gen()))) + gens_in_ret = [self__to_ret(to_self_(k.gen())) + for k in self._intermediate_fields(self.rational_function_field())] + self_to_ret = self.hom(gens_in_ret) + return ret, ret_to_self, self_to_ret + + @cached_method + def primitive_element(self): + r""" + Return a primitive element over the underlying rational function field. + + If this is a finite extension of a rational function field `K(x)` with + `K` perfect, then this is a simple extension of `K(x)`, i.e., there is + a primitive element `y` which generates this field over `K(x)`. This + method returns such an element `y`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + sage: R. = L[] + sage: N. = L.extension(z^2 - x - 1) + sage: N.primitive_element() + u + y + sage: M.primitive_element() + z + sage: L.primitive_element() + y + + This also works for inseparable extensions:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x) # optional - sage.rings.finite_rings + sage: R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(Z^2 - y) # optional - sage.rings.finite_rings + sage: M.primitive_element() # optional - sage.rings.finite_rings + z + """ + N, f, t = self.simple_model() + return f(N.gen()) + + @cached_method + def separable_model(self, names=None): + r""" + Return a function field isomorphic to this field which is a separable + extension of a rational function field. + + INPUT: + + - ``names`` -- a tuple of two strings or ``None`` (default: ``None``); + the second entry will be used as the variable name of the rational + function field, the first entry will be used as the variable name of + its separable extension. If ``None``, then the variable names will be + chosen automatically. + + OUTPUT: + + A triple ``(F,f,t)`` where ``F`` is a function field, ``f`` is an + isomorphism from ``F`` to this function field, and ``t`` is the inverse + of ``f``. + + ALGORITHM: + + Suppose that the constant base field is perfect. If this is a monic + integral inseparable extension of a rational function field, then the + defining polynomial is separable if we swap the variables (Proposition + 4.8 in Chapter VIII of [Lan2002]_.) + The algorithm reduces to this case with :meth:`monic_integral_model`. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3) # optional - sage.rings.finite_rings + sage: L.separable_model(('t','w')) # optional - sage.rings.finite_rings + (Function field in t defined by t^3 + w^2, + Function Field morphism: + From: Function field in t defined by t^3 + w^2 + To: Function field in y defined by y^2 + x^3 + Defn: t |--> x + w |--> y, + Function Field morphism: + From: Function field in y defined by y^2 + x^3 + To: Function field in t defined by t^3 + w^2 + Defn: y |--> w + x |--> t) + + This also works for non-integral polynomials:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2/x - x^2) # optional - sage.rings.finite_rings + sage: L.separable_model() # optional - sage.rings.finite_rings + (Function field in y_ defined by y_^3 + x_^2, + Function Field morphism: + From: Function field in y_ defined by y_^3 + x_^2 + To: Function field in y defined by 1/x*y^2 + x^2 + Defn: y_ |--> x + x_ |--> y, + Function Field morphism: + From: Function field in y defined by 1/x*y^2 + x^2 + To: Function field in y_ defined by y_^3 + x_^2 + Defn: y |--> x_ + x |--> y_) + + If the base field is not perfect this is only implemented in trivial cases:: + + sage: k. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: k.is_perfect() # optional - sage.rings.finite_rings + False + sage: K. = FunctionField(k) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 - t) # optional - sage.rings.finite_rings + sage: L.separable_model() # optional - sage.rings.finite_rings + (Function field in y defined by y^3 + t, + Function Field endomorphism of Function field in y defined by y^3 + t + Defn: y |--> y + x |--> x, + Function Field endomorphism of Function field in y defined by y^3 + t + Defn: y |--> y + x |--> x) + + Some other cases for which a separable model could be constructed are + not supported yet:: + + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - t) # optional - sage.rings.finite_rings + sage: L.separable_model() # optional - sage.rings.finite_rings + Traceback (most recent call last): + ... + NotImplementedError: constructing a separable model is only implemented for function fields over a perfect constant base field + + TESTS: + + Check that this also works in characteristic zero:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x^3) + sage: L.separable_model() + (Function field in y defined by y^2 - x^3, + Function Field endomorphism of Function field in y defined by y^2 - x^3 + Defn: y |--> y + x |--> x, + Function Field endomorphism of Function field in y defined by y^2 - x^3 + Defn: y |--> y + x |--> x) + + Check that this works for towers of inseparable extensions:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings + sage: M.separable_model() # optional - sage.rings.finite_rings + (Function field in z_ defined by z_ + x_^4, + Function Field morphism: + From: Function field in z_ defined by z_ + x_^4 + To: Function field in z defined by z^2 + y + Defn: z_ |--> x + x_ |--> z, + Function Field morphism: + From: Function field in z defined by z^2 + y + To: Function field in z_ defined by z_ + x_^4 + Defn: z |--> x_ + y |--> x_^2 + x |--> x_^4) + + Check that this also works if only the first extension is inseparable:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x) # optional - sage.rings.finite_rings + sage: R. = L[] # optional - sage.rings.finite_rings + sage: M. = L.extension(z^3 - y) # optional - sage.rings.finite_rings + sage: M.separable_model() # optional - sage.rings.finite_rings + (Function field in z_ defined by z_ + x_^6, Function Field morphism: + From: Function field in z_ defined by z_ + x_^6 + To: Function field in z defined by z^3 + y + Defn: z_ |--> x + x_ |--> z, Function Field morphism: + From: Function field in z defined by z^3 + y + To: Function field in z_ defined by z_ + x_^6 + Defn: z |--> x_ + y |--> x_^3 + x |--> x_^6) + + """ + if names is None: + pass + elif not isinstance(names, tuple): + raise TypeError("names must be a tuple consisting of two strings") + elif len(names) != 2: + raise ValueError("must provide exactly two variable names") + + if self.base_ring() is not self.rational_function_field(): + L, from_L, to_L = self.simple_model() + K, from_K, to_K = L.separable_model(names=names) + f = K.hom([from_L(from_K(K.gen())), from_L(from_K(K.base_field().gen()))]) + t = self.hom([to_K(to_L(k.gen())) for k in self._intermediate_fields(self.rational_function_field())]) + return K, f, t + + if self.polynomial().gcd(self.polynomial().derivative()).is_one(): + # the model is already separable + if names is None: + names = self.variable_name(), self.base_field().variable_name() + return self.change_variable_name(names) + + if not self.constant_base_field().is_perfect(): + raise NotImplementedError("constructing a separable model is only implemented for function fields over a perfect constant base field") + + if names is None: + names = (self.variable_name()+"_", self.rational_function_field().variable_name()+"_") + + L, from_L, to_L = self.monic_integral_model() + + if L.polynomial().gcd(L.polynomial().derivative()).is_one(): + # L is separable + ret, ret_to_L, L_to_ret = L.change_variable_name(names) + f = ret.hom([from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))]) + t = self.hom([L_to_ret(to_L(self.gen())), L_to_ret(to_L(self.base_field().gen()))]) + return ret, f, t + else: + # otherwise, the polynomial of L must be separable in the other variable + from .constructor import FunctionField + K = FunctionField(self.constant_base_field(), names=(names[1],)) + # construct a field isomorphic to L on top of K + + # turn the minpoly of K into a bivariate polynomial + if names[0] == names[1]: + raise ValueError("names of generators must be distinct") + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + R = PolynomialRing(self.constant_base_field(), names=names) + S = R.remove_var(names[1]) + f = R( L.polynomial().change_variable_name(names[1]).map_coefficients( + lambda c:c.numerator().change_variable_name(names[0]), S)) + f = f.polynomial(R.gen(0)).change_ring(K) + f /= f.leading_coefficient() + # f must be separable in the other variable (otherwise it would factor) + assert f.gcd(f.derivative()).is_one() + + ret = K.extension(f, names=(names[0],)) + # isomorphisms between L and ret are given by swapping generators + ret_to_L = ret.hom( [L(L.base_field().gen()), L.gen()] ) + L_to_ret = L.hom( [ret(K.gen()), ret.gen()] ) + # compose with from_L and to_L to get the desired isomorphisms between self and ret + f = ret.hom( [from_L(ret_to_L(ret.gen())), from_L(ret_to_L(ret.base_field().gen()))] ) + t = self.hom( [L_to_ret(to_L(self.gen())), L_to_ret(to_L(self.base_field().gen()))] ) + return ret, f, t + + def change_variable_name(self, name): + r""" + Return a field isomorphic to this field with variable(s) ``name``. + + INPUT: + + - ``name`` -- a string or a tuple consisting of a strings, the names of + the new variables starting with a generator of this field and going + down to the rational function field. + + OUTPUT: + + A triple ``F,f,t`` where ``F`` is a function field, ``f`` is an + isomorphism from ``F`` to this field, and ``t`` is the inverse of + ``f``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^2 - x) + sage: R. = L[] + sage: M. = L.extension(z^2 - y) + + sage: M.change_variable_name('zz') + (Function field in zz defined by zz^2 - y, + Function Field morphism: + From: Function field in zz defined by zz^2 - y + To: Function field in z defined by z^2 - y + Defn: zz |--> z + y |--> y + x |--> x, + Function Field morphism: + From: Function field in z defined by z^2 - y + To: Function field in zz defined by zz^2 - y + Defn: z |--> zz + y |--> y + x |--> x) + sage: M.change_variable_name(('zz','yy')) + (Function field in zz defined by zz^2 - yy, Function Field morphism: + From: Function field in zz defined by zz^2 - yy + To: Function field in z defined by z^2 - y + Defn: zz |--> z + yy |--> y + x |--> x, Function Field morphism: + From: Function field in z defined by z^2 - y + To: Function field in zz defined by zz^2 - yy + Defn: z |--> zz + y |--> yy + x |--> x) + sage: M.change_variable_name(('zz','yy','xx')) + (Function field in zz defined by zz^2 - yy, + Function Field morphism: + From: Function field in zz defined by zz^2 - yy + To: Function field in z defined by z^2 - y + Defn: zz |--> z + yy |--> y + xx |--> x, + Function Field morphism: + From: Function field in z defined by z^2 - y + To: Function field in zz defined by zz^2 - yy + Defn: z |--> zz + y |--> yy + x |--> xx) + + """ + if not isinstance(name, tuple): + name = (name,) + if len(name) == 0: + raise ValueError("name must contain at least one string") + elif len(name) == 1: + base = self.base_field() + from_base = to_base = Hom(base,base).identity() + else: + base, from_base, to_base = self.base_field().change_variable_name(name[1:]) + + ret = base.extension(self.polynomial().map_coefficients(to_base), names=(name[0],)) + f = ret.hom( [k.gen() for k in self._intermediate_fields(self.rational_function_field())] ) + t = self.hom( [k.gen() for k in ret._intermediate_fields(ret.rational_function_field())] ) + return ret, f, t + + +class FunctionField_simple(FunctionField_polymod): + """ + Function fields defined by irreducible and separable polynomials + over rational function fields. + """ + @cached_method + def _inversion_isomorphism(self): + r""" + Return an inverted function field isomorphic to ``self`` and isomorphisms + between them. + + An *inverted* function field `M` is an extension of the base rational + function field `k(x)` of ``self``, and isomorphic to ``self`` by an + isomorphism sending `x` to `1/x`, which we call an *inversion* + isomorphism. Also the defining polynomial of the function field `M` is + required to be monic and integral. + + The inversion isomorphism is for internal use to reposition infinite + places to finite places. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: F._inversion_isomorphism() # optional - sage.rings.finite_rings + (Function field in s defined by s^3 + x^16 + x^14 + x^12, Composite map: + From: Function field in s defined by s^3 + x^16 + x^14 + x^12 + To: Function field in y defined by y^3 + x^6 + x^4 + x^2 + Defn: Function Field morphism: + From: Function field in s defined by s^3 + x^16 + x^14 + x^12 + To: Function field in T defined by T^3 + (x^4 + x^2 + 1)/x^6 + Defn: s |--> x^6*T + x |--> x + then + Function Field morphism: + From: Function field in T defined by T^3 + (x^4 + x^2 + 1)/x^6 + To: Function field in y defined by y^3 + x^6 + x^4 + x^2 + Defn: T |--> y + x |--> 1/x, Composite map: + From: Function field in y defined by y^3 + x^6 + x^4 + x^2 + To: Function field in s defined by s^3 + x^16 + x^14 + x^12 + Defn: Function Field morphism: + From: Function field in y defined by y^3 + x^6 + x^4 + x^2 + To: Function field in T defined by T^3 + (x^4 + x^2 + 1)/x^6 + Defn: y |--> T + x |--> 1/x + then + Function Field morphism: + From: Function field in T defined by T^3 + (x^4 + x^2 + 1)/x^6 + To: Function field in s defined by s^3 + x^16 + x^14 + x^12 + Defn: T |--> 1/x^6*s + x |--> x) + """ + K = self.base_field() + R = PolynomialRing(K,'T') + x = K.gen() + xinv = 1/x + + h = K.hom(xinv) + F_poly = R([h(c) for c in self.polynomial().list()]) + F = K.extension(F_poly) + + self2F = self.hom([F.gen(),xinv]) + F2self = F.hom([self.gen(),xinv]) + + M, M2F, F2M = F.monic_integral_model('s') + + return M, F2self*M2F, F2M*self2F + + def places_above(self, p): + """ + Return places lying above ``p``. + + INPUT: + + - ``p`` -- place of the base rational function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: all(q.place_below() == p # optional - sage.rings.finite_rings + ....: for p in K.places() for q in F.places_above(p)) + True + + sage: K. = FunctionField(QQ); _. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: O = K.maximal_order() + sage: pls = [O.ideal(x - c).place() for c in [-2, -1, 0, 1, 2]] + sage: all(q.place_below() == p + ....: for p in pls for q in F.places_above(p)) + True + + sage: K. = FunctionField(QQbar); _. = K[] # optional - sage.rings.number_field + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.number_field + sage: O = K.maximal_order() # optional - sage.rings.number_field + sage: pls = [O.ideal(x - QQbar(sqrt(c))).place() # optional - sage.rings.number_field + ....: for c in [-2, -1, 0, 1, 2]] + sage: all(q.place_below() == p # long time (4s) # optional - sage.rings.number_field + ....: for p in pls for q in F.places_above(p)) + True + """ + R = self.base_field() + + if p not in R.place_set(): + raise TypeError("not a place of the base rational function field") + + if p.is_infinite_place(): + dec = self.maximal_order_infinite().decomposition() + else: + dec = self.maximal_order().decomposition(p.prime_ideal()) + + return tuple([q.place() for q, deg, exp in dec]) + + def constant_field(self): + """ + Return the algebraic closure of the base constant field in the function + field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # optional - sage.rings.finite_rings + sage: L.constant_field() # optional - sage.rings.finite_rings + 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: + + - ``name`` -- name (default: `t`) of the generator of the exact constant field + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)); _. = K[] # optional - sage.rings.finite_rings + sage: f = Y^2 - x*Y + x^2 + 1 # irreducible but not absolutely irreducible # optional - sage.rings.finite_rings + sage: L. = K.extension(f) # optional - sage.rings.finite_rings + sage: L.genus() # optional - sage.rings.finite_rings + 0 + sage: L.exact_constant_field() # optional - sage.rings.finite_rings + (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() # optional - sage.rings.finite_rings + 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() + + 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 the genus of the function field. + + EXAMPLES:: + + sage: F. = GF(16) # optional - sage.rings.finite_rings + sage: K. = FunctionField(F); K # optional - sage.rings.finite_rings + Rational function field in x over Finite Field in a of size 2^4 + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings + sage: L.genus() # optional - sage.rings.finite_rings + 6 + + The genus is computed by the Hurwitz genus formula. + """ + k, _ = self.exact_constant_field() + different_degree = self.different().degree() # must be even + return Integer(different_degree // 2 - self.degree() / k.degree()) + 1 + + 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[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings + sage: R, fr_R, to_R = L.residue_field(p) # optional - sage.rings.finite_rings + sage: R # optional - sage.rings.finite_rings + Finite Field of size 2 + sage: f = 1 + y # optional - sage.rings.finite_rings + sage: f.valuation(p) # optional - sage.rings.finite_rings + -1 + sage: to_R(f) # optional - sage.rings.finite_rings + Traceback (most recent call last): + ... + TypeError: ... + sage: (1+1/f).valuation(p) # optional - sage.rings.finite_rings + 0 + sage: to_R(1 + 1/f) # optional - sage.rings.finite_rings + 1 + sage: [fr_R(e) for e in R] # optional - sage.rings.finite_rings + [0, 1] + """ + return place.residue_field(name=name) + + +class FunctionField_char_zero(FunctionField_simple): + """ + Function fields of characteristic zero. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) + sage: L + Function field in y defined by y^3 + (-x^3 + 1)/(x^3 - 2) + sage: L.characteristic() + 0 + """ + @cached_method + def higher_derivation(self): + """ + Return the higher derivation (also called the Hasse-Schmidt derivation) + for the function field. + + The higher derivation of the function field is uniquely determined with + respect to the separating element `x` of the base rational function + field `k(x)`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) + sage: L.higher_derivation() # optional - sage.modules + Higher derivation map: + From: Function field in y defined by y^3 + (-x^3 + 1)/(x^3 - 2) + To: Function field in y defined by y^3 + (-x^3 + 1)/(x^3 - 2) + """ + from .derivations_polymod import FunctionFieldHigherDerivation_char_zero + return FunctionFieldHigherDerivation_char_zero(self) + + +class FunctionField_global(FunctionField_simple): + """ + Global function fields. + + INPUT: + + - ``polynomial`` -- monic irreducible and separable polynomial + + - ``names`` -- name of the generator of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings + sage: L # optional - sage.rings.finite_rings + Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) + + The defining equation needs not be monic:: + + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension((1 - x)*Y^7 - x^3) # optional - sage.rings.finite_rings + sage: L.gaps() # long time (6s) # optional - sage.rings.finite_rings + [1, 2, 3] + + or may define a trivial extension:: + + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y-1) # optional - sage.rings.finite_rings + sage: L.genus() # optional - sage.rings.finite_rings + 0 + """ + _differentials_space = LazyImport('sage.rings.function_field.differential', 'DifferentialsSpace_global') + + def __init__(self, polynomial, names): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings + sage: TestSuite(L).run() # long time (7s) # optional - sage.rings.finite_rings + """ + FunctionField_polymod.__init__(self, polynomial, names) + + def maximal_order(self): + """ + Return the maximal order of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: O.basis() # optional - sage.rings.finite_rings + (1, 1/x^4*y, 1/x^11*y^2 + 1/x^2, 1/x^15*y^3 + 1/x^6*y) + """ + from .order_polymod import FunctionFieldMaximalOrder_global + return FunctionFieldMaximalOrder_global(self) + + @cached_method + def higher_derivation(self): + """ + Return the higher derivation (also called the Hasse-Schmidt derivation) + for the function field. + + The higher derivation of the function field is uniquely determined with + respect to the separating element `x` of the base rational function + field `k(x)`. + + EXAMPLES:: + + sage: K. = FunctionField(GF(5)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 - (x^3 - 1)/(x^3 - 2)) # optional - sage.rings.finite_rings + sage: L.higher_derivation() # optional - sage.rings.finite_rings sage.modules + Higher derivation map: + From: Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) + To: Function field in y defined by y^3 + (4*x^3 + 1)/(x^3 + 3) + """ + from .derivations_polymod import FunctionFieldHigherDerivation_global + return FunctionFieldHigherDerivation_global(self) + + def get_place(self, degree): + """ + Return a place of ``degree``. + + INPUT: + + - ``degree`` -- a positive integer + + OUTPUT: a place of ``degree`` if any exists; otherwise ``None`` + + EXAMPLES:: + + sage: F. = GF(2) # optional - sage.rings.finite_rings + sage: K. = FunctionField(F) # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^4 + Y - x^5) # optional - sage.rings.finite_rings + sage: L.get_place(1) # optional - sage.rings.finite_rings + Place (x, y) + sage: L.get_place(2) # optional - sage.rings.finite_rings + Place (x, y^2 + y + 1) + sage: L.get_place(3) # optional - sage.rings.finite_rings + Place (x^3 + x^2 + 1, y + x^2 + x) + sage: L.get_place(4) # optional - sage.rings.finite_rings + Place (x + 1, x^5 + 1) + sage: L.get_place(5) # optional - sage.rings.finite_rings + Place (x^5 + x^3 + x^2 + x + 1, y + x^4 + 1) + sage: L.get_place(6) # optional - sage.rings.finite_rings + Place (x^3 + x^2 + 1, y^2 + y + x^2) + sage: L.get_place(7) # optional - sage.rings.finite_rings + Place (x^7 + x + 1, y + x^6 + x^5 + x^4 + x^3 + x) + sage: L.get_place(8) # optional - sage.rings.finite_rings + + """ + for p in self._places_finite(degree): + return p + + for p in self._places_infinite(degree): + return p + + return None + + def places(self, degree=1): + """ + Return a list of the places with ``degree``. + + INPUT: + + - ``degree`` -- positive integer (default: `1`) + + EXAMPLES:: + + sage: F. = GF(2) # optional - sage.rings.finite_rings + sage: K. = FunctionField(F) # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings + sage: L.places(1) # optional - sage.rings.finite_rings + [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) # optional - sage.rings.finite_rings + sage: K. = FunctionField(F) # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings + sage: L.places_finite(1) # optional - sage.rings.finite_rings + [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) # optional - sage.rings.finite_rings + sage: K. = FunctionField(F) # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings + sage: L._places_finite(1) # optional - sage.rings.finite_rings + + """ + O = self.maximal_order() + K = self.base_field() + + 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) # optional - sage.rings.finite_rings + sage: K. = FunctionField(F) # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings + sage: L.places_infinite(1) # optional - sage.rings.finite_rings + [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) # optional - sage.rings.finite_rings + sage: K. = FunctionField(F) # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: L. = K.extension(t^4 + t - x^5) # optional - sage.rings.finite_rings + sage: L._places_infinite(1) # optional - sage.rings.finite_rings + + """ + Oinf = self.maximal_order_infinite() + for prime,_,_ in Oinf.decomposition(): + place = prime.place() + if place.degree() == degree: + yield place + + def gaps(self): + """ + Return the gaps of the function field. + + These are the gaps at the ordinary places, that is, places which are + not Weierstrass places. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings + sage: L.gaps() # optional - sage.rings.finite_rings sage.modules + [1, 2, 3] + """ + return self._weierstrass_places()[1] + + def weierstrass_places(self): + """ + Return all Weierstrass places of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings + sage: L.weierstrass_places() # optional - sage.rings.finite_rings sage.modules + [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 + 1, (x^3 + 1)*y + x + 1), + Place (x^3 + x + 1, y + 1), + Place (x^3 + x + 1, y + x^2), + Place (x^3 + x + 1, y + x^2 + 1), + Place (x^3 + x^2 + 1, y + x), + Place (x^3 + x^2 + 1, y + x^2 + 1), + Place (x^3 + x^2 + 1, y + x^2 + x + 1)] + """ + return self._weierstrass_places()[0].support() + + @cached_method + def _weierstrass_places(self): + """ + Return the Weierstrass places together with the gap sequence for + ordinary places. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings + sage: len(L.weierstrass_places()) # indirect doctest # optional - sage.rings.finite_rings sage.modules + 10 + + This method implements Algorithm 30 in [Hes2002b]_. + """ + from sage.matrix.constructor import matrix + from sage.modules.free_module_element import vector + + W = self(self.base_field().gen()).differential().divisor() + basis = W._basis() + + if not basis: + return [], [] + d = len(basis) + + der = self.higher_derivation() + M = matrix([basis]) + e = 1 + gaps = [1] + while M.nrows() < d: + row = vector([der._derive(basis[i], e) for i in range(d)]) + if row not in M.row_space(): + M = matrix(M.rows() + [row]) + gaps.append(e + 1) + e += 1 + + # This is faster than M.determinant(). Note that Mx + # is a matrix over univariate polynomial ring. + Mx = matrix(M.nrows(), [c._x for c in M.list()]) + detM = self(Mx.determinant() % self._polynomial) + + R = detM.divisor() + sum(gaps)*W # ramification divisor + + return R, gaps + + @cached_method + def L_polynomial(self, name='t'): + """ + Return the L-polynomial of the function field. + + INPUT: + + - ``name`` -- (default: ``t``) name of the variable of the polynomial + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: F.L_polynomial() # optional - sage.rings.finite_rings + 2*t^2 + t + 1 + """ + from sage.rings.integer_ring import ZZ + q = self.constant_field().order() + g = self.genus() + + B = [len(self.places(i+1)) for i in range(g)] + N = [sum(d * B[d-1] for d in ZZ(i+1).divisors()) for i in range(g)] + S = [N[i] - q**(i+1) - 1 for i in range(g)] + + a = [1] + for i in range(1, g+1): + a.append(sum(S[j] * a[i-j-1] for j in range(i)) / i) + for j in range(1, g+1): + a.append(q**j * a[g-j]) + + return ZZ[name](a) + + def number_of_rational_places(self, r=1): + """ + Return the number of rational places of the function field whose + constant field extended by degree ``r``. + + INPUT: + + - ``r`` -- positive integer (default: `1`) + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: F.number_of_rational_places() # optional - sage.rings.finite_rings + 4 + sage: [F.number_of_rational_places(r) for r in [1..10]] # optional - sage.rings.finite_rings + [4, 8, 4, 16, 44, 56, 116, 288, 508, 968] + """ + from sage.rings.integer_ring import IntegerRing + + q = self.constant_field().order() + L = self.L_polynomial() + Lp = L.derivative() + + R = IntegerRing()[[L.parent().gen()]] # power series ring + + f = R(Lp / L, prec=r) + n = f[r-1] + q**r + 1 + + return n + + +@handle_AA_and_QQbar +def _singular_normal(ideal): + r""" + Compute the normalization of the affine algebra defined by ``ideal`` using + Singular. + + The affine algebra is the quotient algebra of a multivariate polynomial + ring `R` by the ideal. The normalization is by definition the integral + closure of the algebra in its total ring of fractions. + + INPUT: + + - ``ideal`` -- a radical ideal in a multivariate polynomial ring + + OUTPUT: + + a list of lists, one list for each ideal in the equidimensional + decomposition of the ``ideal``, each list giving a set of generators of the + normalization of each ideal as an R-module by dividing all elements of the + list by the final element. Thus the list ``[x, y]`` means that `\{x/y, 1\}` + is the set of generators of the normalization of `R/(x,y)`. + + ALGORITHM: + + Singular's implementation of the normalization algorithm described in G.-M. + Greuel, S. Laplagne, F. Seelisch: Normalization of Rings (2009). + + EXAMPLES:: + + sage: from sage.rings.function_field.function_field_polymod import _singular_normal + sage: R. = QQ[] + + sage: f = (x^2 - y^3) * x + sage: _singular_normal(ideal(f)) + [[x, y], [1]] + + sage: f = y^2 - x + sage: _singular_normal(ideal(f)) + [[1]] + """ + from sage.libs.singular.function import singular_function, lib + lib('normal.lib') + normal = singular_function('normal') + execute = singular_function('execute') + + try: + get_printlevel = singular_function('get_printlevel') + except NameError: + execute('proc get_printlevel {return (printlevel);}') + get_printlevel = singular_function('get_printlevel') + + # It's fairly verbose unless printlevel is -1. + saved_printlevel = get_printlevel() + execute('printlevel=-1') + nor = normal(ideal) + execute('printlevel={}'.format(saved_printlevel)) + + return nor[1] + + +class FunctionField_integral(FunctionField_simple): + """ + Integral function fields. + + A function field is integral if it is defined by an irreducible separable + polynomial, which is integral over the maximal order of the base rational + function field. + """ + def _maximal_order_basis(self): + """ + Return a basis of the maximal order of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) # optional - sage.rings.finite_rings + sage: F._maximal_order_basis() # optional - sage.rings.finite_rings + [1, 1/x^4*y, 1/x^11*y^2 + 1/x^2, 1/x^15*y^3 + 1/x^6*y] + + The basis of the maximal order *always* starts with 1. This is assumed + in some algorithms. + """ + from sage.matrix.constructor import matrix + from .hermite_form_polynomial import reversed_hermite_form + + k = self.constant_base_field() + K = self.base_field() # rational function field + n = self.degree() + + # Construct the defining polynomial of the function field as a + # two-variate polynomial g in the ring k[y,x] where k is the constant + # base field. + S,(y,x) = PolynomialRing(k, names='y,x', order='lex').objgens() + v = self.polynomial().list() + g = sum([v[i].numerator().subs(x) * y**i for i in range(len(v))]) + + if self.is_global(): + from sage.libs.singular.function import singular_function, lib + from sage.env import SAGE_EXTCODE + lib(SAGE_EXTCODE + '/singular/function_field/core.lib') + normalize = singular_function('core_normalize') + + # Singular "normalP" algorithm assumes affine domain over + # a prime field. So we construct gflat lifting g as in + # k_prime[yy,xx,zz]/(k_poly) where k = k_prime[zz]/(k_poly) + R = PolynomialRing(k.prime_subfield(), names='yy,xx,zz') + gflat = R.zero() + for m in g.monomials(): + c = g.monomial_coefficient(m).polynomial('zz') + gflat += R(c) * R(m) # R(m) is a monomial in yy and xx + + k_poly = R(k.polynomial('zz')) + + # invoke Singular + pols_in_R = normalize(R.ideal([k_poly, gflat])) + + # reconstruct polynomials in S + h = R.hom([y,x,k.gen()],S) + pols_in_S = [h(f) for f in pols_in_R] + else: + # Call Singular. Singular's "normal" function returns a basis + # of the integral closure of k(x,y)/(g) as a k[x,y]-module. + pols_in_S = _singular_normal(S.ideal(g))[0] + + # reconstruct the polynomials in the function field + x = K.gen() + y = self.gen() + pols = [] + for f in pols_in_S: + p = f.polynomial(S.gen(0)) + s = 0 + for i in range(p.degree()+1): + s += p[i].subs(x) * y**i + pols.append(s) + + # Now if pols = [g1,g2,...gn,g0], then the g1/g0,g2/g0,...,gn/g0, + # and g0/g0=1 are the module generators of the integral closure + # of the equation order Sb = k[xb,yb] in its fraction field, + # that is, the function field. The integral closure of k[x] + # is then obtained by multiplying these generators with powers of y + # as the equation order itself is an integral extension of k[x]. + d = ~ pols[-1] + _basis = [] + for f in pols: + b = d * f + for i in range(n): + _basis.append(b) + b *= y + + # Finally we reduce _basis to get a basis over k[x]. This is done of + # course by Hermite normal form computation. Here we apply a trick to + # get a basis that starts with 1 and is ordered in increasing + # y-degrees. The trick is to use the reversed Hermite normal form. + # Note that it is important that the overall denominator l lies in k[x]. + V, fr_V, to_V = self.free_module() + basis_V = [to_V(bvec) for bvec in _basis] + l = lcm([vvec.denominator() for vvec in basis_V]) + + _mat = matrix([[coeff.numerator() for coeff in l*v] for v in basis_V]) + reversed_hermite_form(_mat) + + basis = [fr_V(v) / l for v in _mat if not v.is_zero()] + return basis + + @cached_method + def equation_order(self): + """ + Return the equation order of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: F.equation_order() # optional - sage.rings.finite_rings + Order in Function field in y defined by y^3 + x^6 + x^4 + x^2 + + sage: K. = FunctionField(QQ); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: F.equation_order() + Order in Function field in y defined by y^3 - x^6 - 2*x^5 - 3*x^4 - 2*x^3 - x^2 + """ + from .order_basis import FunctionFieldOrder_basis + a = self.gen() + basis = [a**i for i in range(self.degree())] + return FunctionFieldOrder_basis(tuple(basis)) + + @cached_method + def primitive_integal_element_infinite(self): + """ + Return a primitive integral element over the base maximal infinite order. + + This element is integral over the maximal infinite order of the base + rational function field and the function field is a simple extension by + this element over the base order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: b = F.primitive_integal_element_infinite(); b # optional - sage.rings.finite_rings + 1/x^2*y + sage: b.minimal_polynomial('t') # optional - sage.rings.finite_rings + t^3 + (x^4 + x^2 + 1)/x^4 + """ + f = self.polynomial() + n = f.degree() + y = self.gen() + x = self.base_field().gen() + + cf = max([(f[i].numerator().degree()/(n-i)).ceil() for i in range(n) + if f[i] != 0]) + return y*x**(-cf) + + @cached_method + def equation_order_infinite(self): + """ + Return the infinite equation order of the function field. + + This is by definition `o[b]` where `b` is the primitive integral + element from :meth:`primitive_integal_element_infinite()` and `o` is + the maximal infinite order of the base rational function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: F.equation_order_infinite() # optional - sage.rings.finite_rings + Infinite order in Function field in y defined by y^3 + x^6 + x^4 + x^2 + + sage: K. = FunctionField(QQ); R. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: F.equation_order_infinite() + Infinite order in Function field in y defined by y^3 - x^6 - 2*x^5 - 3*x^4 - 2*x^3 - x^2 + """ + from .order_basis import FunctionFieldOrderInfinite_basis + b = self.primitive_integal_element_infinite() + basis = [b**i for i in range(self.degree())] + return FunctionFieldOrderInfinite_basis(tuple(basis)) + + +class FunctionField_char_zero_integral(FunctionField_char_zero, FunctionField_integral): + """ + Function fields of characteristic zero, defined by an irreducible and + separable polynomial, integral over the maximal order of the base rational + function field with a finite constant field. + """ + pass + + +class FunctionField_global_integral(FunctionField_global, FunctionField_integral): + """ + Global function fields, defined by an irreducible and separable polynomial, + integral over the maximal order of the base rational function field with a + finite constant field. + """ + pass diff --git a/src/sage/rings/function_field/function_field_rational.py b/src/sage/rings/function_field/function_field_rational.py new file mode 100644 index 00000000000..8ada23fec93 --- /dev/null +++ b/src/sage/rings/function_field/function_field_rational.py @@ -0,0 +1,996 @@ +r""" +Function Fields: rational +""" + +#***************************************************************************** +# Copyright (C) 2023 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 sage.arith.functions import lcm +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_import import LazyImport +from sage.structure.category_object import CategoryObject +from sage.rings.integer import Integer +from sage.categories.homset import Hom +from sage.categories.function_fields import FunctionFields + +from .element import FunctionFieldElement +from .element_rational import FunctionFieldElement_rational +from .function_field import FunctionField + + +class RationalFunctionField(FunctionField): + """ + Rational function field in one variable, over an arbitrary base field. + + INPUT: + + - ``constant_field`` -- arbitrary field + + - ``names`` -- string or tuple of length 1 + + EXAMPLES:: + + sage: K. = FunctionField(GF(3)); K # optional - sage.rings.finite_rings + Rational function field in t over Finite Field of size 3 + sage: K.gen() # optional - sage.rings.finite_rings + t + sage: 1/t + t^3 + 5 # optional - sage.rings.finite_rings + (t^4 + 2*t + 1)/t + + sage: K. = FunctionField(QQ); K + Rational function field in t over Rational Field + sage: K.gen() + t + sage: 1/t + t^3 + 5 + (t^4 + 5*t + 1)/t + + There are various ways to get at the underlying fields and rings + associated to a rational function field:: + + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: K.base_field() # optional - sage.rings.finite_rings + Rational function field in t over Finite Field of size 7 + sage: K.field() # optional - sage.rings.finite_rings + Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 + sage: K.constant_field() # optional - sage.rings.finite_rings + Finite Field of size 7 + sage: K.maximal_order() # optional - sage.rings.finite_rings + Maximal order of Rational function field in t over Finite Field of size 7 + + sage: K. = FunctionField(QQ) + sage: K.base_field() + Rational function field in t over Rational Field + sage: K.field() + Fraction Field of Univariate Polynomial Ring in t over Rational Field + sage: K.constant_field() + Rational Field + sage: K.maximal_order() + Maximal order of Rational function field in t over Rational Field + + We define a morphism:: + + sage: K. = FunctionField(QQ) + sage: L = FunctionField(QQ, 'tbar') # give variable name as second input + sage: K.hom(L.gen()) + Function Field morphism: + From: Rational function field in t over Rational Field + To: Rational function field in tbar over Rational Field + Defn: t |--> tbar + + Here are some calculations over a number field:: + + sage: R. = FunctionField(QQ) + sage: L. = R[] + sage: F. = R.extension(y^2 - (x^2+1)) # optional - sage.rings.function_field + sage: (y/x).divisor() # optional - sage.rings.function_field sage.modules + - Place (x, y - 1) + - Place (x, y + 1) + + Place (x^2 + 1, y) + + sage: A. = QQ[] # optional - sage.rings.number_field + sage: NF. = NumberField(z^2 + 1) # optional - sage.rings.number_field + sage: R. = FunctionField(NF) # optional - sage.rings.number_field + sage: L. = R[] # optional - sage.rings.number_field + sage: F. = R.extension(y^2 - (x^2+1)) # optional - sage.rings.function_field sage.modules sage.rings.number_field + + sage: (x/y*x.differential()).divisor() # optional - sage.rings.function_field sage.modules sage.rings.number_field + -2*Place (1/x, 1/x*y - 1) + - 2*Place (1/x, 1/x*y + 1) + + Place (x, y - 1) + + Place (x, y + 1) + + sage: (x/y).divisor() # optional - sage.rings.function_field sage.modules sage.rings.number_field + - Place (x - i, y) + + Place (x, y - 1) + + Place (x, y + 1) + - Place (x + i, y) + + """ + Element = FunctionFieldElement_rational + + def __init__(self, constant_field, names, category=None): + """ + Initialize. + + EXAMPLES:: + + sage: K. = FunctionField(CC); K + Rational function field in t over Complex Field with 53 bits of precision + sage: TestSuite(K).run() # long time (5s) + + sage: FunctionField(QQ[I], 'alpha') # optional - sage.rings.number_field + Rational function field in alpha over + Number Field in I with defining polynomial x^2 + 1 with I = 1*I + + Must be over a field:: + + sage: FunctionField(ZZ, 't') + Traceback (most recent call last): + ... + TypeError: constant_field must be a field + """ + if names is None: + raise ValueError("variable name must be specified") + elif not isinstance(names, tuple): + names = (names, ) + if not constant_field.is_field(): + raise TypeError("constant_field must be a field") + + self._constant_field = constant_field + + FunctionField.__init__(self, self, names=names, category=FunctionFields().or_subcategory(category)) + + from .place_rational 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())) + + from sage.categories.sets_with_partial_maps import SetsWithPartialMaps + from sage.categories.morphism import SetMorphism + R.register_conversion(SetMorphism(self.Hom(R, SetsWithPartialMaps()), self._to_polynomial)) + + self._gen = self(R.gen()) + + def __reduce__(self): + """ + Return the arguments which were used to create this instance. The + rationale for this is explained in the documentation of + :class:`UniqueRepresentation`. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: clazz,args = K.__reduce__() + sage: clazz(*args) + Rational function field in x over Rational Field + """ + from .constructor import FunctionField + return FunctionField, (self._constant_field, self._names) + + def __hash__(self): + """ + Return hash of the function field. + + The hash is formed from the constant field and the variable names. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: hash(K) == hash((K.constant_base_field(), K.variable_names())) + True + + """ + return self._hash + + def _repr_(self): + """ + Return string representation of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K._repr_() + 'Rational function field in t over Rational Field' + """ + return "Rational function field in %s over %s"%( + self.variable_name(), self._constant_field) + + def _element_constructor_(self, x): + r""" + Coerce ``x`` into an element of the function field, possibly not canonically. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: a = K._element_constructor_(K.maximal_order().gen()); a + t + sage: a.parent() + Rational function field in t over Rational Field + + TESTS: + + Conversion of a string:: + + sage: K('t') + t + sage: K('1/t') + 1/t + + Conversion of a constant polynomial over the function field:: + + sage: K(K.polynomial_ring().one()) + 1 + + Some indirect test of conversion:: + + sage: S. = K[] + sage: I = S * [x^2 - y^2, y - t] # optional - sage.rings.function_field + sage: I.groebner_basis() # optional - sage.rings.function_field + [x^2 - t^2, y - t] + + """ + if isinstance(x, FunctionFieldElement): + return self.element_class(self, self._field(x._x)) + try: + x = self._field(x) + except TypeError as Err: + try: + if x.parent() is self.polynomial_ring(): + return x[0] + except AttributeError: + pass + raise Err + return self.element_class(self, x) + + def _to_constant_base_field(self, f): + r""" + Return ``f`` as an element of the constant base field. + + INPUT: + + - ``f`` -- element of the rational function field which is a + constant of the underlying rational function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K._to_constant_base_field(K(1)) + 1 + sage: K._to_constant_base_field(K(x)) + Traceback (most recent call last): + ... + ValueError: only constants can be converted into the constant base field but x is not a constant + + TESTS: + + Verify that :trac:`21872` has been resolved:: + + sage: K(1) in QQ + True + sage: x in QQ + False + + """ + K = self.constant_base_field() + if f.denominator() in K and f.numerator() in K: + # When K is not exact, f.denominator() might not be an exact 1, so + # we need to divide explicitly to get the correct precision + return K(f.numerator()) / K(f.denominator()) + raise ValueError("only constants can be converted into the constant base field but %r is not a constant"%(f,)) + + def _to_polynomial(self, f): + """ + If ``f`` is integral, return it as a polynomial. + + INPUT: + + - ``f`` -- an element of this rational function field whose denominator is a constant. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K._ring(x) # indirect doctest + x + """ + K = f.parent().constant_base_field() + if f.denominator() in K: + return f.numerator()/K(f.denominator()) + raise ValueError("only polynomials can be converted to the underlying polynomial ring") + + def _to_bivariate_polynomial(self, f): + """ + Convert ``f`` from a univariate polynomial over the rational function + field into a bivariate polynomial and a denominator. + + INPUT: + + - ``f`` -- univariate polynomial over the function field + + OUTPUT: + + - bivariate polynomial, denominator + + EXAMPLES:: + + sage: R. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: S. = R[] # optional - sage.rings.finite_rings + sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) # optional - sage.rings.finite_rings + sage: R._to_bivariate_polynomial(f) # optional - sage.rings.finite_rings + (X^7*t^2 - X^4*t^5 - X^3 + t^3, t^3) + """ + v = f.list() + denom = lcm([a.denominator() for a in v]) + S = denom.parent() + x,t = S.base_ring()['%s,%s'%(f.parent().variable_name(),self.variable_name())].gens() + phi = S.hom([t]) + return sum([phi((denom * v[i]).numerator()) * x**i for i in range(len(v))]), denom + + def _factor_univariate_polynomial(self, f, proof=None): + """ + Factor the univariate polynomial f over the function field. + + INPUT: + + - ``f`` -- univariate polynomial over the function field + + EXAMPLES: + + We do a factorization over the function field over the rationals:: + + sage: R. = FunctionField(QQ) + sage: S. = R[] + sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) + sage: f.factor() # indirect doctest # optional - sage.libs.singular + (1/t) * (X - t) * (X^2 - 1/t) * (X^2 + 1/t) * (X^2 + t*X + t^2) + sage: f.factor().prod() == f # optional - sage.libs.singular + True + + We do a factorization over a finite prime field:: + + sage: R. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: S. = R[] # optional - sage.rings.finite_rings + sage: f = (1/t)*(X^4 - 1/t^2)*(X^3 - t^3) # optional - sage.rings.finite_rings + sage: f.factor() # optional - sage.rings.finite_rings + (1/t) * (X + 3*t) * (X + 5*t) * (X + 6*t) * (X^2 + 1/t) * (X^2 + 6/t) + sage: f.factor().prod() == f # optional - sage.rings.finite_rings + True + + Factoring over a function field over a non-prime finite field:: + + sage: k. = GF(9) # optional - sage.rings.finite_rings + sage: R. = FunctionField(k) # optional - sage.rings.finite_rings + sage: S. = R[] # optional - sage.rings.finite_rings + sage: f = (1/t)*(X^3 - a*t^3) # optional - sage.rings.finite_rings + sage: f.factor() # optional - sage.rings.finite_rings + (1/t) * (X + (a + 2)*t)^3 + sage: f.factor().prod() == f # optional - sage.rings.finite_rings + True + + Factoring over a function field over a tower of finite fields:: + + sage: k. = GF(4) # optional - sage.rings.finite_rings + sage: R. = k[] # optional - sage.rings.finite_rings + sage: l. = k.extension(b^2 + b + a) # optional - sage.rings.finite_rings + sage: K. = FunctionField(l) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: F = t*x # optional - sage.rings.finite_rings + sage: F.factor(proof=False) # optional - sage.rings.finite_rings + (x) * t + + """ + old_variable_name = f.variable_name() + # the variables of the bivariate polynomial must be distinct + if self.variable_name() == f.variable_name(): + # replace x with xx to make the variable names distinct + f = f.change_variable_name(old_variable_name + old_variable_name) + + F, d = self._to_bivariate_polynomial(f) + fac = F.factor(proof=proof) + x = f.parent().gen() + t = f.parent().base_ring().gen() + phi = F.parent().hom([x, t]) + v = [(phi(P),e) for P, e in fac] + unit = phi(fac.unit())/d + w = [] + for a, e in v: + c = a.leading_coefficient() + a = a/c + # undo any variable substitution that we introduced for the bivariate polynomial + if old_variable_name != a.variable_name(): + a = a.change_variable_name(old_variable_name) + unit *= (c**e) + if a.is_unit(): + unit *= a**e + else: + w.append((a,e)) + from sage.structure.factorization import Factorization + return Factorization(w, unit=unit) + + def extension(self, f, names=None): + """ + Create an extension `L = K[y]/(f(y))` of the rational function field. + + INPUT: + + - ``f`` -- univariate polynomial over self + + - ``names`` -- string or length-1 tuple + + OUTPUT: + + - a function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: K.extension(y^5 - x^3 - 3*x + x*y) # optional - sage.rings.function_field + Function field in y defined by y^5 + x*y - x^3 - 3*x + + A nonintegral defining polynomial:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: K.extension(y^3 + (1/t)*y + t^3/(t+1)) # optional - sage.rings.function_field + Function field in y defined by y^3 + 1/t*y + t^3/(t + 1) + + The defining polynomial need not be monic or integral:: + + sage: K.extension(t*y^3 + (1/t)*y + t^3/(t+1)) # optional - sage.rings.function_field + Function field in y defined by t*y^3 + 1/t*y + t^3/(t + 1) + """ + from . import constructor + return constructor.FunctionFieldExtension(f, names) + + @cached_method + def polynomial_ring(self, var='x'): + """ + Return a polynomial ring in one variable over the rational function field. + + INPUT: + + - ``var`` -- string; name of the variable + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.polynomial_ring() + Univariate Polynomial Ring in x over Rational function field in x over Rational Field + sage: K.polynomial_ring('T') + Univariate Polynomial Ring in T over Rational function field in x over Rational Field + """ + return self[var] + + @cached_method(key=lambda self, base, basis, map: map) + def free_module(self, base=None, basis=None, map=True): + """ + Return a vector space `V` and isomorphisms from the field to `V` and + from `V` to the field. + + This function allows us to identify the elements of this field with + elements of a one-dimensional vector space over the field itself. This + method exists so that all function fields (rational or not) have the + same interface. + + INPUT: + + - ``base`` -- the base field of the vector space; must be the function + field itself (the default) + + - ``basis`` -- (ignored) a basis for the vector space + + - ``map`` -- (default ``True``), whether to return maps to and from the vector space + + OUTPUT: + + - a vector space `V` over base field + + - an isomorphism from `V` to the field + + - the inverse isomorphism from the field to `V` + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.free_module() # optional - sage.modules + (Vector space of dimension 1 over Rational function field in x over Rational Field, Isomorphism: + From: Vector space of dimension 1 over Rational function field in x over Rational Field + To: Rational function field in x over Rational Field, Isomorphism: + From: Rational function field in x over Rational Field + To: Vector space of dimension 1 over Rational function field in x over Rational Field) + + TESTS:: + + sage: K.free_module() # optional - sage.modules + (Vector space of dimension 1 over Rational function field in x over Rational Field, Isomorphism: + From: Vector space of dimension 1 over Rational function field in x over Rational Field + To: Rational function field in x over Rational Field, Isomorphism: + From: Rational function field in x over Rational Field + To: Vector space of dimension 1 over Rational function field in x over Rational Field) + + """ + if basis is not None: + raise NotImplementedError + from .maps import MapVectorSpaceToFunctionField, MapFunctionFieldToVectorSpace + if base is None: + base = self + elif base is not self: + raise ValueError("base must be the rational function field itself") + V = base**1 + if not map: + return V + from_V = MapVectorSpaceToFunctionField(V, self) + to_V = MapFunctionFieldToVectorSpace(self, V) + return (V, from_V, to_V) + + def random_element(self, *args, **kwds): + """ + Create a random element of the rational function field. + + Parameters are passed to the random_element method of the + underlying fraction field. + + EXAMPLES:: + + sage: FunctionField(QQ,'alpha').random_element() # random + (-1/2*alpha^2 - 4)/(-12*alpha^2 + 1/2*alpha - 1/95) + """ + return self(self._field.random_element(*args, **kwds)) + + def degree(self, base=None): + """ + Return the degree over the base field of the rational function + field. Since the base field is the rational function field itself, the + degree is 1. + + INPUT: + + - ``base`` -- the base field of the vector space; must be the function + field itself (the default) + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.degree() + 1 + """ + if base is None: + base = self + elif base is not self: + raise ValueError("base must be the rational function field itself") + from sage.rings.integer_ring import ZZ + return ZZ(1) + + def gen(self, n=0): + """ + Return the ``n``-th generator of the function field. If ``n`` is not + 0, then an IndexError is raised. + + EXAMPLES:: + + sage: K. = FunctionField(QQ); K.gen() + t + sage: K.gen().parent() + Rational function field in t over Rational Field + sage: K.gen(1) + Traceback (most recent call last): + ... + IndexError: Only one generator. + """ + if n != 0: + raise IndexError("Only one generator.") + return self._gen + + def ngens(self): + """ + Return the number of generators, which is 1. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.ngens() + 1 + """ + return 1 + + def base_field(self): + """ + Return the base field of the rational function field, which is just + the function field itself. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: K.base_field() # optional - sage.rings.finite_rings + Rational function field in t over Finite Field of size 7 + """ + return self + + def hom(self, im_gens, base_morphism=None): + """ + Create a homomorphism from ``self`` to another ring. + + INPUT: + + - ``im_gens`` -- exactly one element of some ring. It must be + invertible and transcendental over the image of + ``base_morphism``; this is not checked. + + - ``base_morphism`` -- a homomorphism from the base field into the + other ring. If ``None``, try to use a coercion map. + + OUTPUT: + + - a map between function fields + + EXAMPLES: + + We make a map from a rational function field to itself:: + + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: K.hom((x^4 + 2)/x) # optional - sage.rings.finite_rings + Function Field endomorphism of Rational function field in x over Finite Field of size 7 + Defn: x |--> (x^4 + 2)/x + + We construct a map from a rational function field into a + non-rational extension field:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = K.hom(y^2 + y + 2); f # optional - sage.rings.finite_rings sage.rings.function_field + Function Field morphism: + From: Rational function field in x over Finite Field of size 7 + To: Function field in y defined by y^3 + 6*x^3 + x + Defn: x |--> y^2 + y + 2 + sage: f(x) # optional - sage.rings.finite_rings sage.rings.function_field + y^2 + y + 2 + sage: f(x^2) # optional - sage.rings.finite_rings sage.rings.function_field + 5*y^2 + (x^3 + 6*x + 4)*y + 2*x^3 + 5*x + 4 + """ + if isinstance(im_gens, CategoryObject): + return self.Hom(im_gens).natural_map() + if not isinstance(im_gens, (list,tuple)): + im_gens = [im_gens] + if len(im_gens) != 1: + raise ValueError("there must be exactly one generator") + x = im_gens[0] + R = x.parent() + if base_morphism is None and not R.has_coerce_map_from(self.constant_field()): + raise ValueError("you must specify a morphism on the base field") + from .maps import FunctionFieldMorphism_rational + return FunctionFieldMorphism_rational(self.Hom(R), x, base_morphism) + + def field(self): + """ + Return the underlying field, forgetting the function field + structure. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: K.field() # optional - sage.rings.finite_rings + Fraction Field of Univariate Polynomial Ring in t over Finite Field of size 7 + + .. SEEALSO:: + + :meth:`sage.rings.fraction_field.FractionField_1poly_field.function_field` + + """ + return self._field + + @cached_method + def maximal_order(self): + """ + Return the maximal order of the function field. + + Since this is a rational function field it is of the form `K(t)`, and the + maximal order is by definition `K[t]`, where `K` is the constant field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.maximal_order() + Maximal order of Rational function field in t over Rational Field + sage: K.equation_order() + Maximal order of Rational function field in t over Rational Field + """ + from .order_rational import FunctionFieldMaximalOrder_rational + return FunctionFieldMaximalOrder_rational(self) + + equation_order = maximal_order + + @cached_method + def maximal_order_infinite(self): + """ + Return the maximal infinite order of the function field. + + By definition, this is the valuation ring of the degree valuation of + the rational function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.maximal_order_infinite() + Maximal infinite order of Rational function field in t over Rational Field + sage: K.equation_order_infinite() + Maximal infinite order of Rational function field in t over Rational Field + """ + from .order_rational import FunctionFieldMaximalOrderInfinite_rational + return FunctionFieldMaximalOrderInfinite_rational(self) + + equation_order_infinite = maximal_order_infinite + + def constant_base_field(self): + """ + Return the field of which the rational function field is a + transcendental extension. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.constant_base_field() + Rational Field + """ + return self._constant_field + + 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() # optional - sage.modules + 0 + """ + return self.divisor_group().zero() + + def genus(self): + """ + Return the genus of the function field, namely 0. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.genus() + 0 + """ + return Integer(0) + + def change_variable_name(self, name): + r""" + Return a field isomorphic to this field with variable ``name``. + + INPUT: + + - ``name`` -- a string or a tuple consisting of a single string, the + name of the new variable + + OUTPUT: + + A triple ``F,f,t`` where ``F`` is a rational function field, ``f`` is + an isomorphism from ``F`` to this field, and ``t`` is the inverse of + ``f``. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: L,f,t = K.change_variable_name('y') + sage: L,f,t + (Rational function field in y over Rational Field, + Function Field morphism: + From: Rational function field in y over Rational Field + To: Rational function field in x over Rational Field + Defn: y |--> x, + Function Field morphism: + From: Rational function field in x over Rational Field + To: Rational function field in y over Rational Field + Defn: x |--> y) + sage: L.change_variable_name('x')[0] is K + True + + """ + if isinstance(name, tuple): + if len(name) != 1: + raise ValueError("names must be a tuple with a single string") + name = name[0] + if name == self.variable_name(): + id = Hom(self,self).identity() + return self,id,id + else: + from .constructor import FunctionField + ret = FunctionField(self.constant_base_field(), name) + return ret, ret.hom(self.gen()), self.hom(ret.gen()) + + 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)) # optional - sage.rings.finite_rings + sage: p = F.places_finite(2)[0] # optional - sage.rings.finite_rings + sage: R, fr_R, to_R = F.residue_field(p) # optional - sage.rings.finite_rings + sage: R # optional - sage.rings.finite_rings + Finite Field in z2 of size 5^2 + sage: to_R(x) in R # optional - sage.rings.finite_rings + True + """ + return place.residue_field(name=name) + + +class RationalFunctionField_char_zero(RationalFunctionField): + """ + Rational function fields of characteristic zero. + """ + @cached_method + def higher_derivation(self): + """ + Return the higher derivation for the function field. + + This is also called the Hasse-Schmidt derivation. + + EXAMPLES:: + + sage: F. = FunctionField(QQ) + sage: d = F.higher_derivation() # optional - sage.modules + sage: [d(x^5,i) for i in range(10)] # optional - sage.modules + [x^5, 5*x^4, 10*x^3, 10*x^2, 5*x, 1, 0, 0, 0, 0] + sage: [d(x^9,i) for i in range(10)] # optional - sage.modules + [x^9, 9*x^8, 36*x^7, 84*x^6, 126*x^5, 126*x^4, 84*x^3, 36*x^2, 9*x, 1] + """ + from .derivations_polymod import FunctionFieldHigherDerivation_char_zero + return FunctionFieldHigherDerivation_char_zero(self) + + +class RationalFunctionField_global(RationalFunctionField): + """ + Rational function field over finite fields. + """ + _differentials_space = LazyImport('sage.rings.function_field.differential', 'DifferentialsSpace_global') + + def places(self, degree=1): + """ + Return all places of the degree. + + INPUT: + + - ``degree`` -- (default: 1) a positive integer + + EXAMPLES:: + + sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: F.places() # optional - sage.rings.finite_rings + [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)) # optional - sage.rings.finite_rings + sage: F.places_finite() # optional - sage.rings.finite_rings + [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)) # optional - sage.rings.finite_rings + sage: F._places_finite() # optional - sage.rings.finite_rings + + """ + O = self.maximal_order() + R = O._ring + G = R.polynomials(max_degree=degree - 1) + lm = R.monomial(degree) + for g in G: + h = lm + g + if h.is_irreducible(): + yield O.ideal(h).place() + + def place_infinite(self): + """ + Return the unique place at infinity. + + EXAMPLES:: + + sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: F.place_infinite() # optional - sage.rings.finite_rings + Place (1/x) + """ + return self.maximal_order_infinite().prime_ideal().place() + + def get_place(self, degree): + """ + Return a place of ``degree``. + + INPUT: + + - ``degree`` -- a positive integer + + EXAMPLES:: + + sage: F. = GF(2) # optional - sage.rings.finite_rings + sage: K. = FunctionField(F) # optional - sage.rings.finite_rings + sage: K.get_place(1) # optional - sage.rings.finite_rings + Place (x) + sage: K.get_place(2) # optional - sage.rings.finite_rings + Place (x^2 + x + 1) + sage: K.get_place(3) # optional - sage.rings.finite_rings + Place (x^3 + x + 1) + sage: K.get_place(4) # optional - sage.rings.finite_rings + Place (x^4 + x + 1) + sage: K.get_place(5) # optional - sage.rings.finite_rings + Place (x^5 + x^2 + 1) + + """ + for p in self._places_finite(degree): + return p + + assert False, "there is a bug around" + + @cached_method + def higher_derivation(self): + """ + Return the higher derivation for the function field. + + This is also called the Hasse-Schmidt derivation. + + EXAMPLES:: + + sage: F. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: d = F.higher_derivation() # optional - sage.rings.finite_rings + sage: [d(x^5,i) for i in range(10)] # optional - sage.rings.finite_rings + [x^5, 0, 0, 0, 0, 1, 0, 0, 0, 0] + sage: [d(x^7,i) for i in range(10)] # optional - sage.rings.finite_rings + [x^7, 2*x^6, x^5, 0, 0, x^2, 2*x, 1, 0, 0] + """ + from .derivations_polymod import RationalFunctionFieldHigherDerivation_global + return RationalFunctionFieldHigherDerivation_global(self) diff --git a/src/sage/rings/function_field/ideal.py b/src/sage/rings/function_field/ideal.py index 1ce0b885f56..8d203e9f019 100644 --- a/src/sage/rings/function_field/ideal.py +++ b/src/sage/rings/function_field/ideal.py @@ -26,46 +26,46 @@ Ideals in the equation order of an extension of a rational function field:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y); I + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y); I # optional - sage.rings.function_field Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: I^2 + sage: I^2 # optional - sage.rings.function_field Ideal (x^3 + 1, (-x^3 - 1)*y) of Order in Function field in y defined by y^2 - x^3 - 1 Ideals in the maximal order of a global function field:: - 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(y) - sage: I^2 + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I^2 # optional - sage.rings.finite_rings sage.rings.function_field Ideal (x) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: ~I + sage: ~I # optional - sage.rings.finite_rings sage.rings.function_field Ideal (1/x*y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: ~I * I + sage: ~I * I # optional - sage.rings.finite_rings sage.rings.function_field Ideal (1) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: J = O.ideal(x+y) * I - sage: J.factor() + sage: J = O.ideal(x + y) * I # optional - sage.rings.finite_rings sage.rings.function_field + sage: J.factor() # optional - sage.rings.finite_rings sage.rings.function_field (Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x)^2 * (Ideal (x^3 + x + 1, y + x) of Maximal order of Function field in y defined by y^2 + x^3*y + x) Ideals in the maximal infinite order of a global function field:: - sage: K. = FunctionField(GF(3^2)); R. = K[] - sage: F. = K.extension(t^3 + t^2 - x^4) - sage: Oinf = F.maximal_order_infinite() - sage: I = Oinf.ideal(1/y) - sage: I + I == I + sage: K. = FunctionField(GF(3^2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings sage.rings.function_field + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I + I == I # optional - sage.rings.finite_rings sage.rings.function_field True - sage: I^2 + sage: I^2 # optional - sage.rings.finite_rings sage.rings.function_field Ideal (1/x^4*y) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: ~I + sage: ~I # optional - sage.rings.finite_rings sage.rings.function_field Ideal (y) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: ~I * I + sage: ~I * I # optional - sage.rings.finite_rings sage.rings.function_field Ideal (1) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: I.factor() + sage: I.factor() # optional - sage.rings.finite_rings sage.rings.function_field (Ideal (1/x^3*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4)^4 AUTHORS: @@ -77,43 +77,31 @@ - Kwankyu Lee (2017-04-30): added ideals for global function fields """ + # **************************************************************************** -# Copyright (C) 2010 William Stein -# Copyright (C) 2011 Maarten Derickx +# Copyright (C) 2010 William Stein +# 2011 Maarten Derickx +# 2017-2021 Kwankyu Lee +# 2018 Frédéric Chapoton +# 2019 Brent Baccala +# 2021 Jonathan Kliem # # 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. # https://www.gnu.org/licenses/ # **************************************************************************** -import itertools -from sage.misc.cachefunc import cached_method -from sage.misc.lazy_attribute import lazy_attribute from sage.misc.latex import latex from sage.misc.misc import powerset - from sage.structure.parent import Parent from sage.structure.element import Element from sage.structure.richcmp import richcmp from sage.structure.factorization import Factorization from sage.structure.unique_representation import UniqueRepresentation - -from sage.arith.power import generic_power - -from sage.modules.free_module_element import vector - from sage.categories.monoids import Monoids - -from sage.rings.infinity import infinity from sage.rings.ideal import Ideal_generic -from sage.matrix.constructor import matrix - -from .divisor import divisor - -from .hermite_form_polynomial import reversed_hermite_form - class FunctionFieldIdeal(Element): """ @@ -125,9 +113,9 @@ class FunctionFieldIdeal(Element): EXAMPLES:: - sage: K. = FunctionField(GF(7)) - sage: O = K.equation_order() - sage: O.ideal(x^3+1) + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: O = K.equation_order() # optional - sage.rings.finite_rings + sage: O.ideal(x^3 + 1) # optional - sage.rings.finite_rings Ideal (x^3 + 1) of Maximal order of Rational function field in x over Finite Field of size 7 """ def __init__(self, ring): @@ -136,10 +124,10 @@ def __init__(self, ring): TESTS:: - sage: K. = FunctionField(GF(7)) - sage: O = K.equation_order() - sage: I = O.ideal(x^3+1) - sage: TestSuite(I).run() + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: O = K.equation_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^3 + 1) # optional - sage.rings.finite_rings + sage: TestSuite(I).run() # optional - sage.rings.finite_rings """ Element.__init__(self, ring.ideal_monoid()) self._ring = ring @@ -151,11 +139,11 @@ def _repr_short(self): EXAMPLES:: - sage: K. = FunctionField(GF(3^2)); R. = K[] - sage: F. = K.extension(t^3 + t^2 - x^4) - sage: Oinf = F.maximal_order_infinite() - sage: I = Oinf.ideal(1/y) - sage: I._repr_short() + sage: K. = FunctionField(GF(3^2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings sage.rings.function_field + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I._repr_short() # optional - sage.rings.finite_rings sage.rings.function_field '(1/x^4*y^2)' """ if self.is_zero(): @@ -171,45 +159,45 @@ def _repr_(self): sage: K. = FunctionField(QQ) sage: O = K.maximal_order() - sage: I = O.ideal(x,1/(x+1)); I + sage: I = O.ideal(x, 1/(x+1)); I Ideal (1/(x + 1)) of Maximal order of Rational function field in x over Rational Field sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: O.ideal(x^2 + 1) + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.ideal(x^2 + 1) # optional - sage.rings.finite_rings sage.rings.function_field Ideal (x^2 + 1) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(y); I + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y); I # optional - sage.rings.finite_rings sage.rings.function_field Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(y); I + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y); I # optional - sage.rings.finite_rings sage.rings.function_field Ideal (y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x/(x^2+1)) - sage: I + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x/(x^2+1)) # optional - sage.rings.finite_rings + sage: I # optional - sage.rings.finite_rings Ideal (1/x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) - sage: F. = K.extension(t^3 + t^2 - x^4) - sage: Oinf = F.maximal_order_infinite() - sage: Oinf.ideal(1/y) + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings sage.rings.function_field + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: Oinf.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field Ideal (1/x^4*y^2) of Maximal infinite order of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf.ideal(1/y) + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: Oinf.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field Ideal (1/x*y) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x """ @@ -224,11 +212,11 @@ def _latex_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: latex(I) + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: latex(I) # optional - sage.rings.finite_rings sage.rings.function_field \left(y\right) """ return '\\left(' + ', '.join(latex(g) for g in self.gens_reduced()) + '\\right)' @@ -243,10 +231,10 @@ def _div_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(7)) - sage: O = K.equation_order() - sage: I = O.ideal(x^3+1) - sage: I / I + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: O = K.equation_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^3 + 1) # optional - sage.rings.finite_rings + sage: I / I # optional - sage.rings.finite_rings Ideal (1) of Maximal order of Rational function field in x over Finite Field of size 7 """ @@ -267,10 +255,10 @@ def gens_reduced(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) - sage: O = K.equation_order() - sage: I = O.ideal(x,x^2,x^2+x) - sage: I.gens_reduced() + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: O = K.equation_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, x^2, x^2 + x) # optional - sage.rings.finite_rings + sage: I.gens_reduced() # optional - sage.rings.finite_rings (x,) """ gens = self.gens() @@ -289,10 +277,10 @@ def ring(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) - sage: O = K.equation_order() - sage: I = O.ideal(x,x^2,x^2+x) - sage: I.ring() + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: O = K.equation_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, x^2, x^2 + x) # optional - sage.rings.finite_rings + sage: I.ring() # optional - sage.rings.finite_rings Maximal order of Rational function field in x over Finite Field of size 7 """ return self._ring @@ -304,10 +292,10 @@ def base_ring(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(x^2 + 1) - sage: I.base_ring() + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field + sage: I.base_ring() # optional - sage.rings.function_field Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ring() @@ -318,63 +306,63 @@ def place(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) - sage: O = K.maximal_order() - sage: I = O.ideal(x^2 + x + 1) - sage: I.place() + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: I.place() # optional - sage.rings.finite_rings Traceback (most recent call last): ... TypeError: not a prime ideal - sage: I = O.ideal(x^3+x+1) - sage: I.place() + sage: I = O.ideal(x^3 + x + 1) # optional - sage.rings.finite_rings + sage: I.place() # optional - sage.rings.finite_rings 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() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings + sage: p = I.factor()[0][0] # optional - sage.rings.finite_rings + sage: p.place() # optional - sage.rings.finite_rings 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()] + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: [f.place() for f,_ in I.factor()] # optional - sage.rings.finite_rings sage.rings.function_field [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()] + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: [f.place() for f,_ in I.factor()] # optional - sage.rings.finite_rings sage.rings.function_field [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() + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings sage.rings.function_field + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.factor() # optional - sage.rings.finite_rings sage.rings.function_field (Ideal (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() + sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: J.is_prime() # optional - sage.rings.finite_rings sage.rings.function_field True - sage: J.place() + sage: J.place() # optional - sage.rings.finite_rings sage.rings.function_field 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() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.factor() # optional - sage.rings.finite_rings sage.rings.function_field (Ideal (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() + sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: J.is_prime() # optional - sage.rings.finite_rings sage.rings.function_field True - sage: J.place() + sage: J.place() # optional - sage.rings.finite_rings sage.rings.function_field Place (1/x, 1/x*y) """ if not self.is_prime(): @@ -392,49 +380,49 @@ def factor(self): EXAMPLES:: - sage: K. = FunctionField(GF(4)) - sage: O = K.maximal_order() - sage: I = O.ideal(x^3*(x + 1)^2) - sage: I.factor() + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^3*(x + 1)^2) # optional - sage.rings.finite_rings + sage: I.factor() # optional - sage.rings.finite_rings (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() + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings + sage: I.factor() # optional - sage.rings.finite_rings (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() + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(T^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I == I.factor().prod() # optional - sage.rings.finite_rings sage.rings.function_field True - sage: Oinf = F.maximal_order_infinite() - sage: f= 1/x - sage: I = Oinf.ideal(f) - sage: I.factor() + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: f= 1/x # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = Oinf.ideal(f) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.factor() # optional - sage.rings.finite_rings sage.rings.function_field (Ideal ((1/(x^4 + x^3 + x^2))*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^4 + x^3 + x^2))*y^2 + 1) of Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2) sage: K. = FunctionField(QQ); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: O = F.maximal_order() - sage: I = O.ideal(y) - sage: I == I.factor().prod() + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: I == I.factor().prod() # optional - sage.rings.function_field True sage: K. = FunctionField(QQ); _. = 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() + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: I == I.factor().prod() # optional - sage.rings.function_field True """ @@ -446,44 +434,46 @@ def divisor(self): 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() + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) # optional - sage.rings.finite_rings + sage: I.divisor() # optional - sage.rings.finite_rings 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() + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings + sage: I.divisor() # optional - sage.rings.finite_rings 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() + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(T^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field 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() + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field -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() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field - Place (x, x*y) + 2*Place (x + 1, x*y) - sage: Oinf = L.maximal_order_infinite() - sage: I = Oinf.ideal(y) - sage: I.divisor() + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field - Place (1/x, 1/x*y) """ + from .divisor import divisor + if self.is_zero(): raise ValueError("not defined for zero ideal") @@ -497,25 +487,27 @@ def divisor_of_zeros(self): 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() + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) # optional - sage.rings.finite_rings + sage: I.divisor_of_zeros() # optional - sage.rings.finite_rings 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() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings + sage: I.divisor_of_zeros() # optional - sage.rings.finite_rings 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() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.divisor_of_zeros() # optional - sage.rings.finite_rings sage.rings.function_field 2*Place (x + 1, x*y) """ + from .divisor import divisor + if self.is_zero(): raise ValueError("not defined for zero ideal") @@ -529,25 +521,27 @@ def divisor_of_poles(self): 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() + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x*(x + 1)^2/(x^2 + x + 1)) # optional - sage.rings.finite_rings + sage: I.divisor_of_poles() # optional - sage.rings.finite_rings 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() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x + 1)/(x^3 + 1)) # optional - sage.rings.finite_rings + sage: I.divisor_of_poles() # optional - sage.rings.finite_rings 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() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I.divisor_of_poles() # optional - sage.rings.finite_rings sage.rings.function_field Place (x, x*y) """ + from .divisor import divisor + if self.is_zero(): raise ValueError("not defined for zero ideal") @@ -556,329 +550,6 @@ def divisor_of_poles(self): return divisor(F, data) -class FunctionFieldIdeal_rational(FunctionFieldIdeal): - """ - Fractional ideals of the maximal order of a rational function field. - - INPUT: - - - ``ring`` -- the maximal order of the rational function field. - - - ``gen`` -- generator of the ideal, an element of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(1/(x^2+x)); I - Ideal (1/(x^2 + x)) of Maximal order of Rational function field in x over Rational Field - """ - def __init__(self, ring, gen): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(1/(x^2+x)) - sage: TestSuite(I).run() - """ - FunctionFieldIdeal.__init__(self, ring) - self._gen = gen - - def __hash__(self): - """ - Return the hash computed from the data. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(1/(x^2+x)) - sage: d = { I: 1, I^2: 2 } - """ - return hash( (self._ring, self._gen) ) - - def __contains__(self, element): - """ - Test if ``element`` is in this ideal. - - INPUT: - - - ``element`` -- element of the function field - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(1/(x+1)) - sage: x in I - True - """ - return (element / self._gen) in self._ring - - def _richcmp_(self, other, op): - """ - Compare the element with the other element with respect - to the comparison operator. - - INPUT: - - - ``other`` -- element - - - ``op`` -- comparison operator - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(x,x^2+1) - sage: J = O.ideal(x^2+x+1,x) - sage: I == J - True - sage: I = O.ideal(x) - sage: J = O.ideal(x+1) - sage: I < J - True - """ - return richcmp(self._gen, other._gen, op) - - def _add_(self, other): - """ - Add this ideal with the ``other`` ideal. - - INPUT: - - - ``other`` -- ideal - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(x,x^2+1) - sage: J = O.ideal(x^2+x+1,x) - sage: I + J == J + I - True - """ - return self._ring.ideal([self._gen, other._gen]) - - def _mul_(self, other): - """ - Multiply this ideal with the ``other`` ideal. - - INPUT: - - - ``other`` -- ideal - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(x,x^2+x) - sage: J = O.ideal(x^2,x) - sage: I * J == J * I - True - """ - return self._ring.ideal([self._gen * other._gen]) - - def _acted_upon_(self, other, on_left): - """ - Multiply ``other`` with this ideal on the right. - - INPUT: - - - ``other`` -- ideal - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(x^3+x^2) - sage: 2 * I - Ideal (x^3 + x^2) of Maximal order of Rational function field in x over Rational Field - sage: x * I - Ideal (x^4 + x^3) of Maximal order of Rational function field in x over Rational Field - """ - return self._ring.ideal([other * self._gen]) - - def __invert__(self): - """ - Return the ideal inverse of this fractional ideal. - - EXAMPLES:: - - sage: F. = FunctionField(QQ) - sage: O = F.maximal_order() - sage: I = O.ideal(x/(x^2+1)) - sage: ~I - Ideal ((x^2 + 1)/x) of Maximal order of Rational function field - in x over Rational Field - """ - return self._ring.ideal([~(self._gen)]) - - def denominator(self): - """ - Return the denominator of this fractional ideal. - - EXAMPLES:: - - sage: F. = FunctionField(QQ) - sage: O = F.maximal_order() - sage: I = O.ideal(x/(x^2+1)) - sage: I.denominator() - x^2 + 1 - """ - return self._gen.denominator() - - def is_prime(self): - """ - Return ``True`` if this is a prime ideal. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(x^3+x^2) - sage: [f.is_prime() for f,m in I.factor()] - [True, True] - """ - return self._gen.denominator() == 1 and self._gen.numerator().is_prime() - - @cached_method - def module(self): - """ - Return the module, that is the ideal viewed as a module over the ring. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal(x^3+x^2) - sage: I.module() - Free module of degree 1 and rank 1 over Maximal order of Rational - function field in x over Rational Field - Echelon basis matrix: - [x^3 + x^2] - sage: J = 0*I - sage: J.module() - Free module of degree 1 and rank 0 over Maximal order of Rational - function field in x over Rational Field - Echelon basis matrix: - [] - """ - V, fr, to = self.ring().fraction_field().vector_space() - return V.span([to(g) for g in self.gens()], base_ring=self.ring()) - - def gen(self): - """ - Return the unique generator of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)) - sage: O = K.maximal_order() - sage: I = O.ideal(x^2+x) - sage: I.gen() - x^2 + x - """ - return self._gen - - def gens(self): - """ - Return the tuple of the unique generator of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)) - sage: O = K.maximal_order() - sage: I = O.ideal(x^2+x) - sage: I.gens() - (x^2 + x,) - """ - return (self._gen,) - - def gens_over_base(self): - """ - Return the generator of this ideal as a rank one module over the maximal - order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)) - sage: O = K.maximal_order() - sage: I = O.ideal(x^2+x) - sage: I.gens_over_base() - (x^2 + x,) - """ - 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 list of prime and multiplicity pairs of 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() # indirect doctest - (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 - """ - return [(self.ring().ideal(f), m) for f, m in self._gen.factor()] - - class FunctionFieldIdeal_module(FunctionFieldIdeal, Ideal_generic): """ A fractional ideal specified by a finitely generated module over @@ -895,12 +566,12 @@ class FunctionFieldIdeal_module(FunctionFieldIdeal, Ideal_generic): An ideal in an extension of a rational function field:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y) - sage: I + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: I # optional - sage.rings.function_field Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: I^2 + sage: I^2 # optional - sage.rings.function_field Ideal (x^3 + 1, (-x^3 - 1)*y) of Order in Function field in y defined by y^2 - x^3 - 1 """ def __init__(self, ring, module): @@ -910,10 +581,10 @@ def __init__(self, ring, module): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y) - sage: TestSuite(I).run() + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: TestSuite(I).run() # optional - sage.rings.function_field """ FunctionFieldIdeal.__init__(self, ring) @@ -933,15 +604,15 @@ def __contains__(self, x): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y); I + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y); I # optional - sage.rings.function_field Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: y in I + sage: y in I # optional - sage.rings.function_field True - sage: y/x in I + sage: y/x in I # optional - sage.rings.function_field False - sage: y^2 - 2 in I + sage: y^2 - 2 in I # optional - sage.rings.function_field False """ return self._structure[2](x) in self._module @@ -953,10 +624,10 @@ def __hash__(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y) - sage: d = {I: 1} # indirect doctest + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: d = {I: 1} # indirect doctest # optional - sage.rings.function_field """ return hash((self._ring,self._module)) @@ -967,18 +638,18 @@ def _richcmp_(self, other, op): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(x, y); J = O.ideal(y^2 - 2) - sage: I + J == J + I # indirect test + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(x, y); J = O.ideal(y^2 - 2) # optional - sage.rings.function_field + sage: I + J == J + I # indirect test # optional - sage.rings.function_field True - sage: I + I == I # indirect doctest + sage: I + I == I # indirect doctest # optional - sage.rings.function_field True - sage: I == J + sage: I == J # optional - sage.rings.function_field False - sage: I < J + sage: I < J # optional - sage.rings.function_field True - sage: J < I + sage: J < I # optional - sage.rings.function_field False """ return richcmp(self.module().basis(), other.module().basis(), op) @@ -998,20 +669,20 @@ def module(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order(); O + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order(); O # optional - sage.rings.function_field Order in Function field in y defined by y^2 - x^3 - 1 - sage: I = O.ideal(x^2 + 1) - sage: I.gens() + sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field + sage: I.gens() # optional - sage.rings.function_field (x^2 + 1, (x^2 + 1)*y) - sage: I.module() + sage: I.module() # optional - sage.rings.function_field Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Rational Field Echelon basis matrix: [x^2 + 1 0] [ 0 x^2 + 1] - sage: V, from_V, to_V = L.vector_space(); V + sage: V, from_V, to_V = L.vector_space(); V # optional - sage.rings.function_field Vector space of dimension 2 over Rational function field in x over Rational Field - sage: I.module().is_submodule(V) + sage: I.module().is_submodule(V) # optional - sage.rings.function_field True """ return self._module @@ -1023,10 +694,10 @@ def gens(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(x^2 + 1) - sage: I.gens() + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field + sage: I.gens() # optional - sage.rings.function_field (x^2 + 1, (x^2 + 1)*y) """ return self._gens @@ -1038,10 +709,10 @@ def gen(self, i): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(x^2 + 1) - sage: I.gen(1) + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field + sage: I.gen(1) # optional - sage.rings.function_field (x^2 + 1)*y """ return self._gens[i] @@ -1053,10 +724,10 @@ def ngens(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(x^2 + 1) - sage: I.ngens() + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(x^2 + 1) # optional - sage.rings.function_field + sage: I.ngens() # optional - sage.rings.function_field 2 """ return len(self._gens) @@ -1068,11 +739,11 @@ def _add_(self, other): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y) - sage: J = O.ideal(x+y) - sage: I + J + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: J = O.ideal(x+y) # optional - sage.rings.function_field + sage: I + J # optional - sage.rings.function_field Ideal ((-x^2 + x)*y + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ring().ideal(self.gens() + other.gens()) @@ -1084,11 +755,11 @@ def _mul_(self, other): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y) - sage: J = O.ideal(x+y) - sage: I * J + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: J = O.ideal(x+y) # optional - sage.rings.function_field + sage: I * J # optional - sage.rings.function_field Ideal ((-x^5 + x^4 - x^2 + x)*y + x^3 + 1, (x^3 - x^2 + 1)*y) of Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ring().ideal([x*y for x in self.gens() for y in other.gens()]) @@ -1100,10 +771,10 @@ def _acted_upon_(self, other, on_left): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y) - sage: x * I + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: x * I # optional - sage.rings.function_field Ideal (x^4 + x, -x*y) of Order in Function field in y defined by y^2 - x^3 - 1 """ return self.ring().ideal([other * x for x in self.gens()]) @@ -1115,14 +786,14 @@ def intersection(self, other): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y^3); J = O.ideal(y^2) - sage: Z = I.intersection(J); Z + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y^3); J = O.ideal(y^2) # optional - sage.rings.function_field + sage: Z = I.intersection(J); Z # optional - sage.rings.function_field Ideal (x^6 + 2*x^3 + 1, (-x^3 - 1)*y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: y^2 in Z + sage: y^2 in Z # optional - sage.rings.function_field False - sage: y^3 in Z + sage: y^3 in Z # optional - sage.rings.function_field True """ if not isinstance(other, FunctionFieldIdeal): @@ -1145,14 +816,14 @@ def __invert__(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y) - sage: ~I + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: ~I # optional - sage.rings.function_field Ideal (-1, (1/(x^3 + 1))*y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: I^-1 + sage: I^-1 # optional - sage.rings.function_field Ideal (-1, (1/(x^3 + 1))*y) of Order in Function field in y defined by y^2 - x^3 - 1 - sage: ~I * I + sage: ~I * I # optional - sage.rings.function_field Ideal (1) of Order in Function field in y defined by y^2 - x^3 - 1 """ if len(self.gens()) == 0: @@ -1170,1577 +841,91 @@ def __invert__(self): return inv -class FunctionFieldIdeal_polymod(FunctionFieldIdeal): +class FunctionFieldIdealInfinite(FunctionFieldIdeal): """ - Fractional ideals of algebraic function fields + Base class of ideals of maximal infinite orders + """ + pass - INPUT: - - ``ring`` -- order in a function field +class FunctionFieldIdealInfinite_module(FunctionFieldIdealInfinite, Ideal_generic): + """ + A fractional ideal specified by a finitely generated module over + the integers of the base field. - - ``hnf`` -- matrix in hermite normal form + INPUT: - - ``denominator`` -- denominator + - ``ring`` -- order in a function field - The rows of ``hnf`` is a basis of the ideal, which itself is - ``denominator`` times the fractional ideal. + - ``module`` -- module EXAMPLES:: - sage: K. = FunctionField(GF(2)); R. = K[] - sage: L. = K.extension(y^2 - x^3*y - x) - sage: O = L.maximal_order() - sage: O.ideal(y) - Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: O.ideal(y) # optional - sage.rings.function_field + Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 """ - def __init__(self, ring, hnf, denominator=1): + def __init__(self, ring, module): """ Initialize. TESTS:: - 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(y) - sage: TestSuite(I).run() + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: I = O.ideal(y) # optional - sage.rings.function_field + sage: TestSuite(I).run() # optional - sage.rings.function_field """ - FunctionFieldIdeal.__init__(self, ring) + FunctionFieldIdealInfinite.__init__(self, ring) - # the denominator and the entries of the hnf are - # univariate polynomials. - self._hnf = hnf - self._denominator = denominator + self._module = module + self._structure = ring.fraction_field().vector_space() - # for prime ideals - self._relative_degree = None - self._ramification_index = None - self._prime_below = None - self._beta = None + V, from_V, to_V = self._structure + gens = tuple([from_V(a) for a in module.basis()]) + self._gens = gens - # beta in matrix form for fast multiplication - self._beta_matrix = None + # module generators are still ideal generators + Ideal_generic.__init__(self, ring, self._gens, coerce=False) - # (p, q) with irreducible polynomial p and q an element of O in vector - # form, together generating the prime ideal. This data is obtained by - # Kummer's theorem when this prime ideal is constructed. This is used - # for fast multiplication with other ideal. - self._kummer_form = None + def __contains__(self, x): + """ + Return ``True`` if ``x`` is in this ideal. - # tuple of at most two gens: - # the first gen is an element of the base ring of the maximal order - # the second gen is the vector form of an element of the maximal order - # if the second gen is zero, the tuple has only the first gen. - self._gens_two_vecs = None + INPUT: - def __bool__(self): - """ - Test if this ideal is zero. + - ``x`` -- element of the function field 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(y); I - Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: I.is_zero() - False - sage: J = 0*I; J - Zero ideal of Maximal order of Function field in y defined by y^2 + x^3*y + x - sage: J.is_zero() + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal_with_gens_over_base([1, y]); I # optional - sage.rings.finite_rings sage.rings.function_field + Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: y in I # optional - sage.rings.finite_rings sage.rings.function_field 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); I - Ideal (y) of Maximal order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - sage: I.is_zero() + sage: y/x in I # optional - sage.rings.finite_rings sage.rings.function_field False - sage: J = 0*I; J - Zero ideal of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: J.is_zero() + sage: y^2 - 2 in I # optional - sage.rings.finite_rings sage.rings.function_field True """ - return self._hnf.nrows() != 0 - - + return self._structure[2](x) in self._module def __hash__(self): """ - Return the hash of this ideal. + Return the hash of this ideal EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(1/y) - sage: { I: 2 }[I] == 2 - True - - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(1/y) - sage: { I: 2 }[I] == 2 - True - """ - return hash((self._ring, self._hnf, self._denominator)) - - def __contains__(self, x): - """ - Return ``True`` if ``x`` is in this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); _. = K[] - sage: L. = K.extension(Y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal([y]); I - Ideal (y) of Maximal order of Function field in y - defined by y^2 + 6*x^3 + 6 - sage: x * y in I - True - sage: y / x in I - False - sage: y^2 - 2 in I - False - - 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]); I - Ideal (y) of Maximal order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - sage: x * y in I - True - sage: y / x in I - False - sage: y^2 - 2 in I - False - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal([y]); I - Ideal (y) of Maximal order of Function field in y - defined by y^2 - x^3 - 1 - sage: x * y in I - True - sage: y / x in I - False - sage: y^2 - 2 in I - False - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal([y]); I - Ideal (y) of Maximal order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - sage: x * y in I - True - sage: y / x in I - False - sage: y^2 - 2 in I - False - """ - vec = self.ring().coordinate_vector(self._denominator * x) - v = [] - for e in vec: - if e.denominator() != 1: - return False - v.append(e.numerator()) - vec = vector(v) - return vec in self._hnf.image() - - def __invert__(self): - """ - Return the inverse fractional ideal of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); _. = K[] - sage: L. = K.extension(Y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: ~I - Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I^(-1) - Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: ~I * I - Ideal (1) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - - :: - - 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 - Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order - of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: I^(-1) - Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order - of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: ~I * I - Ideal (1) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - - :: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: ~I - Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 - x^3 - 1 - sage: I^(-1) - Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 - x^3 - 1 - sage: ~I * I - Ideal (1) of Maximal order of Function field in y defined by y^2 - x^3 - 1 - - :: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: ~I - Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order - of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: I^(-1) - Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order - of Function field in y defined by y^2 + y + (x^2 + 1)/x - sage: ~I * I - Ideal (1) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - """ - R = self.ring() - T = R._codifferent_matrix() - I = self * R.codifferent() - J = I._denominator * (I._hnf * T).inverse() - return R._ideal_from_vectors(J.columns()) - - def _richcmp_(self, other, op): - """ - Compare this ideal with the other ideal with respect to ``op``. - - INPUT: - - - ``other`` -- ideal - - - ``op`` -- comparison operator - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(1/y) - sage: I == I + I - True - sage: I == I * I - False - - :: - - 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(1/y) - sage: I == I + I - True - sage: I == I * I - False - sage: I < I * I - True - sage: I > I * I - False - """ - return richcmp((self._denominator, self._hnf), (other._denominator, other._hnf), op) - - def _add_(self, other): - """ - Add with other ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: J = O.ideal(x+y) - sage: I + J - Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: J = O.ideal(x+y) - sage: I + J - Ideal (1, y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x - """ - ds = self._denominator - do = other._denominator - vecs1 = [do * r for r in self._hnf] - vecs2 = [ds * r for r in other._hnf] - return self._ring._ideal_from_vectors_and_denominator(vecs1 + vecs2, ds * do) - - def _mul_(self, other): - """ - Multiply with other ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: J = O.ideal(x+y) - sage: I * J - Ideal (x^4 + x^2 + x, x*y + x^2) of Maximal order - of Function field in y defined by y^2 + x^3*y + x - - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: J = O.ideal(x+y) - sage: I * J - Ideal ((x + 1)*y + (x^2 + 1)/x) of Maximal order - of Function field in y defined by y^2 + y + (x^2 + 1)/x - """ - O = self._ring - mul = O._mul_vecs - - if self._kummer_form is not None: - p, q = self._kummer_form - vecs = list(p * other._hnf) + [mul(q, v) for v in other._hnf] - elif other._kummer_form is not None: - p, q = other._kummer_form - vecs = list(p * self._hnf) + [mul(q, v) for v in self._hnf] - elif self._gens_two_vecs is not None: - if len(self._gens_two_vecs) == 1: - g1, = self._gens_two_vecs - vecs = list(g1 * other._hnf) - else: - g1, g2 = self._gens_two_vecs - vecs = list(g1 * other._hnf) + [mul(g2, v) for v in other._hnf] - elif other._gens_two_vecs is not None: - if len(other._gens_two_vecs) == 1: - g1, = other._gens_two_vecs - vecs = list(g1 * self._hnf) - else: - g1, g2 = other._gens_two_vecs - vecs = list(g1 * self._hnf) + [mul(g2, v) for v in self._hnf] - else: - vecs = [mul(r1,r2) for r1 in self._hnf for r2 in other._hnf] - - return O._ideal_from_vectors_and_denominator(vecs, self._denominator * other._denominator) - - def _acted_upon_(self, other, on_left): - """ - Multiply ``other`` and this ideal on the right. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - sage: J = O.ideal(x) - sage: x * I == I * J - True - - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - sage: J = O.ideal(x) - sage: x * I == I * J - True - """ - O = self._ring - mul = O._mul_vecs - - # compute the vector form of other element - v = O.coordinate_vector(other) - d = v.denominator() - vec = vector([(d * c).numerator() for c in v]) - - # multiply with the ideal - vecs = [mul(vec, r) for r in self._hnf] - - return O._ideal_from_vectors_and_denominator(vecs, d * self._denominator) - - def intersect(self, other): - """ - Intersect this ideal with the other ideal as fractional ideals. - - INPUT: - - - ``other`` -- ideal - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - sage: J = O.ideal(x) - sage: I.intersect(J) == I * J * (I + J)^-1 - True - - """ - from sage.matrix.special import block_matrix - - A = self._hnf - B = other._hnf - - ds = self.denominator() - do = other.denominator() - d = ds.lcm(do) - if not d.is_one(): - A = (d // ds) * A - B = (d // do) * B - - MS = A.matrix_space() - I = MS.identity_matrix() - O = MS.zero() - n = A.ncols() - - # intersect the row spaces of A and B - M = block_matrix([[I,I],[A,O],[O,B]]) - - # reversed Hermite form - U = reversed_hermite_form(M, transformation=True) - - vecs = [U[i][:n] for i in range(n)] - - return self._ring._ideal_from_vectors_and_denominator(vecs, d) - - def hnf(self): - """ - Return the matrix in hermite normal form representing this ideal. - - See also :meth:`denominator` - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal(y*(y+1)); I.hnf() - [x^6 + x^3 0] - [ x^3 + 1 1] - - :: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal(y*(y+1)); I.hnf() - [x^6 + x^3 0] - [ x^3 + 1 1] - """ - return self._hnf - - def denominator(self): - """ - Return the denominator of this fractional ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal(y/(y+1)) - sage: d = I.denominator(); d - x^3 - sage: d in O - True - - :: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal(y/(y+1)) - sage: d = I.denominator(); d - x^3 - sage: d in O - True - """ - return self._denominator - - @cached_method - def module(self): - """ - Return the module, that is the ideal viewed as a module - over the base maximal order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: F. = K.extension(y^2 - x^3 - 1) - sage: O = F.maximal_order() - sage: I = O.ideal(x,1/y) - sage: I.module() - Free module of degree 2 and rank 2 over Maximal order - of Rational function field in x over Finite Field of size 7 - Echelon basis matrix: - [ 1 0] - [ 0 1/(x^3 + 1)] - """ - F = self.ring().fraction_field() - V, fr, to = F.vector_space() - O = F.base_field().maximal_order() - return V.span([to(g) for g in self.gens_over_base()], base_ring=O) - - @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. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - sage: I.gens_over_base() - (x^4 + x^2 + x, y + x) - - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - 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())])) - return gens, self._denominator - - def gens(self): - """ - Return a set of generators of this ideal. - - This provides whatever set of generators as quickly - as possible. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - sage: I.gens() - (x^4 + x^2 + x, y + x) - - sage: L. = K.extension(Y^2 +Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - sage: I.gens() - (x^3 + 1, y + x) - """ - return self.gens_over_base() - - @cached_method - def basis_matrix(self): - """ - Return the matrix of basis vectors of this ideal as a module. - - The basis matrix is by definition the hermite norm form of the ideal - divided by the denominator. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) - sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) - sage: O = F.maximal_order() - sage: I = O.ideal(x,1/y) - sage: I.denominator() * I.basis_matrix() == I.hnf() - True - """ - d = self.denominator() - m = (d * self).hnf() - if d != 1: - m = ~d * m - m.set_immutable() - return m - - def is_integral(self): - """ - Return ``True`` if this is an integral 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(x,1/y) - sage: I.is_integral() - False - sage: J = I.denominator() * I - sage: J.is_integral() - 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(x,1/y) - sage: I.is_integral() - False - sage: J = I.denominator() * I - sage: J.is_integral() - True - - sage: K. = FunctionField(QQ); _. = PolynomialRing(K) - sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) - sage: O = F.maximal_order() - sage: I = O.ideal(x,1/y) - sage: I.is_integral() - False - sage: J = I.denominator() * I - sage: J.is_integral() - True - """ - return self.denominator() == 1 - - def ideal_below(self): - """ - Return the ideal below this ideal. - - This is defined only for integral ideals. - - 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(x,1/y) - sage: I.ideal_below() - Traceback (most recent call last): - ... - TypeError: not an integral ideal - sage: J = I.denominator() * I - sage: J.ideal_below() - Ideal (x^3 + x^2 + x) of Maximal order of Rational function field - in x over Finite Field of size 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(x,1/y) - sage: I.ideal_below() - Traceback (most recent call last): - ... - TypeError: not an integral ideal - sage: J = I.denominator() * I - sage: J.ideal_below() - Ideal (x^3 + x) of Maximal order of Rational function field - in x over Finite Field of size 2 - - sage: K. = FunctionField(QQ); _. = K[] - sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) - sage: O = F.maximal_order() - sage: I = O.ideal(x,1/y) - sage: I.ideal_below() - Traceback (most recent call last): - ... - TypeError: not an integral ideal - sage: J = I.denominator() * I - sage: J.ideal_below() - Ideal (x^3 + x^2 + x) of Maximal order of Rational function field - in x over Rational Field - """ - if not self.is_integral(): - raise TypeError("not an integral ideal") - - K = self.ring().fraction_field().base_field().maximal_order() - - # self._hnf is in reversed hermite normal form, that is, lower - # triangular form. Thus the generator of the ideal below is - # just the (0,0) entry of the normal form. When self._hnf was in - # hermite normal form, that is, upper triangular form, then the - # generator can be computed in the following way: - # - # m = matrix([hnf[0].parent().gen(0)] + list(hnf)) - # _,T = m.hermite_form(transformation=True) - # return T[-1][0] - # - # This is certainly less efficient! This is an argument for using - # reversed hermite normal form for ideal representation. - l = self._hnf[0][0] - - return K.ideal(l) - - def norm(self): - """ - Return the norm of this fractional 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: i1 = O.ideal(x) - sage: i2 = O.ideal(y) - sage: i3 = i1 * i2 - sage: i3.norm() == i1.norm() * i2.norm() - True - sage: i1.norm() - x^3 - sage: i1.norm() == x ** F.degree() - True - sage: i2.norm() - x^6 + x^4 + x^2 - sage: i2.norm() == y.norm() - True - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: O = L.maximal_order() - sage: i1 = O.ideal(x) - sage: i2 = O.ideal(y) - sage: i3 = i1 * i2 - sage: i3.norm() == i1.norm() * i2.norm() - True - sage: i1.norm() - x^2 - sage: i1.norm() == x ** L.degree() - True - sage: i2.norm() - (x^2 + 1)/x - sage: i2.norm() == y.norm() - True - """ - n = 1 - for e in self.basis_matrix().diagonal(): - n *= e - return n - - @cached_method - def is_prime(self): - """ - Return ``True`` if this ideal is a 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.is_prime() for f,_ in I.factor()] - [True, 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: [f.is_prime() for f,_ in I.factor()] - [True, True] - - sage: K. = FunctionField(QQ); _. = 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.is_prime() for f,_ in I.factor()] - [True, True] - """ - factors = self.factor() - if len(factors) == 1 and factors[0][1] == 1: # prime! - prime = factors[0][0] - assert self == prime - self._relative_degree = prime._relative_degree - self._ramification_index = prime._ramification_index - self._prime_below = prime._prime_below - self._beta = prime._beta - self._beta_matrix = prime._beta_matrix - return True - else: - return False - - ################################################### - # The following methods are only for prime ideals # - ################################################### - - def valuation(self, ideal): - """ - Return the valuation of ``ideal`` at this prime ideal. - - INPUT: - - - ``ideal`` -- fractional 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(x, (1/(x^3 + x^2 + x))*y^2) - sage: I.is_prime() - True - sage: J = O.ideal(y) - sage: I.valuation(J) - 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.valuation(I) for f,_ in I.factor()] - [-1, 2] - - The method closely follows Algorithm 4.8.17 of [Coh1993]_. - """ - if ideal.is_zero(): - return infinity - - O = self.ring() - F = O.fraction_field() - n = F.degree() - - # beta_matrix is for fast multiplication with beta. For performance, - # this is computed here rather than when the prime is constructed. - if self._beta_matrix is None: - beta = self._beta - m = [] - for i in range(n): - mtable_row = O._mtable[i] - c = sum(beta[j] * mtable_row[j] for j in range(n)) - m.append(c) - self._beta_matrix = matrix(m) - - B = self._beta_matrix - - # Step 1: compute the valuation of the denominator times the ideal - # - # This part is highly optimized as it is critical for - # overall performance of the function field machinery. - p = self.prime_below().gen().numerator() - h = ideal._hnf.list() - val = min([c.valuation(p) for c in h]) - i = self._ramification_index * val - while True: - ppow = p ** val - h = (matrix(n, [c // ppow for c in h]) * B).list() - val = min([c.valuation(p) for c in h]) - if val.is_zero(): - break - i += self._ramification_index * (val - 1) + 1 - - # Step 2: compute the valuation of the denominator - j = self._ramification_index * ideal.denominator().valuation(p) - - # Step 3: return the valuation - return i - j - - def prime_below(self): - """ - Return the prime lying below this prime ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: O = F.maximal_order() - sage: I = O.ideal(y) - sage: [f.prime_below() for f,_ in I.factor()] - [Ideal (x) of Maximal order of Rational function field in x - over Finite Field of size 2, Ideal (x^2 + x + 1) of Maximal order - of Rational function field in x over Finite Field of size 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.prime_below() for f,_ in I.factor()] - [Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2, - Ideal (x + 1) of Maximal order of Rational function field in x over Finite Field of size 2] - - sage: K. = FunctionField(QQ); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: O = F.maximal_order() - sage: I = O.ideal(y) - sage: [f.prime_below() for f,_ in I.factor()] - [Ideal (x) of Maximal order of Rational function field in x over Rational Field, - Ideal (x^2 + x + 1) of Maximal order of Rational function field in x over Rational Field] - """ - return self._prime_below - - 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 - - -class FunctionFieldIdeal_global(FunctionFieldIdeal_polymod): - """ - Fractional ideals of canonical function fields - - INPUT: - - - ``ring`` -- order in a function field - - - ``hnf`` -- matrix in hermite normal form - - - ``denominator`` -- denominator - - The rows of ``hnf`` is a basis of the ideal, which itself is - ``denominator`` times the fractional ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); R. = K[] - sage: L. = K.extension(y^2 - x^3*y - x) - sage: O = L.maximal_order() - sage: O.ideal(y) - Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x - """ - def __init__(self, ring, hnf, denominator=1): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(5)); R. = K[] - sage: L. = K.extension(y^2 - x^3*y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: TestSuite(I).run() - """ - FunctionFieldIdeal_polymod.__init__(self, ring, hnf, denominator) - - def __pow__(self, mod): - """ - Return ``self`` to the power of ``mod``. - - If a two-generators representation of ``self`` is known, it is used - to speed up powering. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^7 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(y) - sage: J = O.ideal(x + y) - sage: S = I / J - sage: a = S^100 - sage: _ = S.gens_two() - sage: b = S^100 # faster - sage: b == I^100 / J^100 - True - sage: b == a - True - """ - if mod > 2 and self._gens_two_vecs is not None: - O = self._ring - mul = O._mul_vecs - R = self._hnf.base_ring() - n = self._hnf.ncols() - - I = matrix.identity(R, n) - - if len(self._gens_two_vecs) == 1: - p, = self._gens_two_vecs - ppow = p**mod - J = [ppow * v for v in I] - else: - p, q = self._gens_two_vecs - q = sum(e1 * e2 for e1,e2 in zip(O.basis(), q)) - ppow = p**mod - qpow = O._coordinate_vector(q**mod) - J = [ppow * v for v in I] + [mul(qpow,v) for v in I] - - return O._ideal_from_vectors_and_denominator(J, self._denominator**mod) - - return generic_power(self, mod) - - def gens(self): - """ - Return a set of generators of this ideal. - - This provides whatever set of generators as quickly - as possible. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 - x^3*Y - x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - sage: I.gens() - (x^4 + x^2 + x, y + x) - - sage: L. = K.extension(Y^2 +Y + x + 1/x) - sage: O = L.maximal_order() - sage: I = O.ideal(x+y) - sage: I.gens() - (x^3 + 1, y + x) - """ - if self._gens_two.is_in_cache(): - return self._gens_two.cache - else: - return self.gens_over_base() - - def gens_two(self): - r""" - Return two generators of this fractional ideal. - - If the ideal is principal, one generator *may* be returned. - - 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 # indirect doctest - Ideal (y) of Maximal order of Function field - in y defined by y^3 + x^6 + x^4 + x^2 - sage: ~I # indirect doctest - Ideal ((1/(x^6 + x^4 + x^2))*y^2) of Maximal order of Function field - in y defined by y^3 + x^6 + x^4 + x^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: I # indirect doctest - Ideal (y) of Maximal order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - sage: ~I # indirect doctest - Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order - of Function field in y defined by y^2 + y + (x^2 + 1)/x - """ - d = self.denominator() - return tuple(e/d for e in self._gens_two()) - - @cached_method - def _gens_two(self): - r""" - Return a set of two generators of the integral ideal, that is - the denominator times this fractional ideal. - - ALGORITHM: - - At most two generators are required to generate ideals in - Dedekind domains. - - Lemma 4.7.9, algorithm 4.7.10, and exercise 4.29 of [Coh1993]_ - tell us that for an integral ideal `I` in a number field, if - we pick `a` such that `\gcd(N(I), N(a)/N(I)) = 1`, then `a` - and `N(I)` generate the ideal. `N()` is the norm, and this - result (presumably) generalizes to function fields. - - After computing `N(I)`, we search exhaustively to find `a`. - - .. TODO:: - - Always return a single generator for a principal ideal. - - Testing for principality is not trivial. Algorithm 6.5.10 - of [Coh1993]_ could probably be adapted for function fields. - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)); _. = K[] - sage: F. = K.extension(Y^3 + x^3*Y + x) - sage: O = F.maximal_order() - sage: I = O.ideal(x^2,x*y,x+y) - sage: I._gens_two() - (x, y) - - sage: K. = FunctionField(GF(3)) - sage: _. = K[] - sage: L. = K.extension(Y-x) - sage: y.zeros()[0].prime_ideal()._gens_two() - (x,) - """ - O = self.ring() - F = O.fraction_field() - - if self._kummer_form is not None: # prime ideal - _g1, _g2 = self._kummer_form - g1 = F(_g1) - g2 = sum([c1*c2 for c1,c2 in zip(_g2, O.basis())]) - if g2: - self._gens_two_vecs = (_g1, _g2) - return (g1,g2) - else: - self._gens_two_vecs = (_g1,) - return (g1,) - - ### start to search for two generators - - hnf = self._hnf - - norm = 1 - for e in hnf.diagonal(): - norm *= e - - if norm.is_constant(): # unit ideal - self._gens_two_vecs = (1,) - return (F(1),) - - # one generator; see .ideal_below() - _l = hnf[0][0] - p = _l.degree() - l = F(_l) - - if self._hnf == O.ideal(l)._hnf: # principal ideal - self._gens_two_vecs = (_l,) - return (l,) - - R = hnf.base_ring() - - basis = [] - for row in hnf: - basis.append(sum([c1*c2 for c1,c2 in zip(row, O.basis())])) - - n = len(basis) - alpha = None - - def check(alpha): - alpha_norm = alpha.norm().numerator() # denominator is 1 - return norm.gcd(alpha_norm // norm) == 1 - - # Trial 1: search for alpha among generators - for alpha in basis: - if check(alpha): - self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) - return (l, alpha) - - # Trial 2: exhaustive search for alpha using only polynomials - # with coefficients 0 or 1 - for d in range(p): - G = itertools.product(itertools.product([0,1],repeat=d+1), repeat=n) - for g in G: - alpha = sum([R(c1)*c2 for c1,c2 in zip(g, basis)]) - if check(alpha): - self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) - return (l, alpha) - - # Trial 3: exhaustive search for alpha using all polynomials - for d in range(p): - 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 - - alpha = sum([c1*c2 for c1,c2 in zip(g, basis)]) - if check(alpha): - self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) - return (l, alpha) - - # should not reach here - raise ValueError("no two generators found") - - -class FunctionFieldIdealInfinite(FunctionFieldIdeal): - """ - Base class of ideals of maximal infinite orders - """ - pass - - -class FunctionFieldIdealInfinite_rational(FunctionFieldIdealInfinite): - """ - Fractional ideal of the maximal order of rational function field. - - INPUT: - - - ``ring`` -- infinite maximal order - - - ``gen``-- generator - - Note that the infinite maximal order is a principal ideal domain. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: Oinf.ideal(x) - Ideal (x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 - """ - def __init__(self, ring, gen): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x) - sage: TestSuite(I).run() - """ - FunctionFieldIdealInfinite.__init__(self, ring) - self._gen = gen - - def __hash__(self): - """ - Return the hash of this fractional ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x) - sage: J = Oinf.ideal(1/x) - sage: d = { I: 1, J: 2 } - """ - return hash( (self.ring(), self._gen) ) - - def __contains__(self, element): - """ - Test if ``element`` is in this ideal. - - INPUT: - - - ``element`` -- element of the function field - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order_infinite() - sage: I = O.ideal(1/(x+1)) - sage: x in I - False - sage: 1/x in I - True - sage: x/(x+1) in I - False - sage: 1/(x*(x+1)) in I - True - """ - return (element / self._gen) in self._ring - - def _richcmp_(self, other, op): - """ - Compare this ideal and ``other`` with respect to ``op``. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x+1) - sage: J = Oinf.ideal(x^2+x) - sage: I + J == J - True - """ - return richcmp(self._gen, other._gen, op) - - def _add_(self, other): - """ - Add this ideal with the other ideal. - - INPUT: - - - ``other`` -- ideal - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x/(x^2+1)) - sage: J = Oinf.ideal(1/(x+1)) - sage: I + J - Ideal (1/x) of Maximal infinite order of Rational function field - in x over Finite Field of size 2 - """ - return self._ring.ideal([self._gen, other._gen]) - - def _mul_(self, other): - """ - Multiply this ideal with the ``other`` ideal. - - INPUT: - - - ``other`` -- ideal - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x/(x^2+1)) - sage: J = Oinf.ideal(1/(x+1)) - sage: I * J - Ideal (1/x^2) of Maximal infinite order of Rational function field - in x over Finite Field of size 2 - """ - return self._ring.ideal([self._gen * other._gen]) - - def _acted_upon_(self, other, on_left): - """ - Multiply this ideal with the ``other`` ideal. - - INPUT: - - - ``other`` -- ideal - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x/(x^2+1)) - sage: x * I - Ideal (1) of Maximal infinite order of Rational function field - in x over Finite Field of size 2 - """ - return self._ring.ideal([other * self._gen]) - - def __invert__(self): - """ - Return the multiplicative inverse of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x/(x^2 + 1)) - sage: ~I # indirect doctest - Ideal (x) of Maximal infinite order of Rational function field in x - over Finite Field of size 2 - """ - return self._ring.ideal([~self._gen]) - - def is_prime(self): - """ - Return ``True`` if this ideal is a prime ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal(x/(x^2 + 1)) - sage: I.is_prime() - True - """ - x = self._ring.fraction_field().gen() - return self._gen == 1/x - - def gen(self): - """ - Return the generator of this principal ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal((x+1)/(x^3+x),(x^2+1)/x^4) - sage: I.gen() - 1/x^2 - """ - return self._gen - - def gens(self): - """ - Return the generator of this principal ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal((x+1)/(x^3+x),(x^2+1)/x^4) - sage: I.gens() - (1/x^2,) - """ - return (self._gen,) - - def gens_over_base(self): - """ - Return the generator of this ideal as a rank one module - over the infinite maximal order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: Oinf = K.maximal_order_infinite() - sage: I = Oinf.ideal((x+1)/(x^3+x),(x^2+1)/x^4) - sage: I.gens_over_base() - (1/x^2,) - """ - return (self._gen,) - - def valuation(self, ideal): - """ - Return the valuation of ``ideal`` at this prime ideal. - - INPUT: - - - ``ideal`` -- fractional ideal - - EXAMPLES:: - - sage: F. = FunctionField(QQ) - sage: O = F.maximal_order_infinite() - sage: p = O.ideal(1/x) - sage: p.valuation(O.ideal(x/(x+1))) - 0 - sage: p.valuation(O.ideal(0)) - +Infinity - """ - if not self.is_prime(): - raise TypeError("not a prime ideal") - - f = ideal.gen() - if f == 0: - return infinity - 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): - """ - A fractional ideal specified by a finitely generated module over - the integers of the base field. - - INPUT: - - - ``ring`` -- order in a function field - - - ``module`` -- module - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: O.ideal(y) - Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 - """ - def __init__(self, ring, module): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal(y) - sage: TestSuite(I).run() - """ - FunctionFieldIdealInfinite.__init__(self, ring) - - self._module = module - self._structure = ring.fraction_field().vector_space() - - V, from_V, to_V = self._structure - gens = tuple([from_V(a) for a in module.basis()]) - self._gens = gens - - # module generators are still ideal generators - Ideal_generic.__init__(self, ring, self._gens, coerce=False) - - def __contains__(self, x): - """ - Return ``True`` if ``x`` is in this ideal. - - INPUT: - - - ``x`` -- element of the function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal_with_gens_over_base([1, y]); I - Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: y in I - True - sage: y/x in I - False - sage: y^2 - 2 in I - True - """ - return self._structure[2](x) in self._module - - def __hash__(self): - """ - Return the hash of this ideal - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal_with_gens_over_base([1, y]) - sage: d = {I: 2} # indirect doctest + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal_with_gens_over_base([1, y]) # optional - sage.rings.finite_rings sage.rings.function_field + sage: d = {I: 2} # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field """ return hash((self._ring,self._module)) @@ -2754,11 +939,11 @@ def __eq__(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal_with_gens_over_base([1, y]) - sage: I == I + I # indirect doctest + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal_with_gens_over_base([1, y]) # optional - sage.rings.finite_rings sage.rings.function_field + sage: I == I + I # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field True """ if not isinstance(other, FunctionFieldIdeal_module): @@ -2782,473 +967,26 @@ def module(self): EXAMPLES:: - sage: K. = FunctionField(GF(7)) - sage: O = K.maximal_order(); O + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order(); O # optional - sage.rings.finite_rings Maximal order of Rational function field in x over Finite Field of size 7 - sage: K.polynomial_ring() + sage: K.polynomial_ring() # optional - sage.rings.finite_rings Univariate Polynomial Ring in x over Rational function field in x over Finite Field of size 7 - sage: I = O.ideal([x^2 + 1, x*(x^2+1)]) - sage: I.gens() + sage: I = O.ideal([x^2 + 1, x*(x^2+1)]) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings (x^2 + 1,) - sage: I.module() + sage: I.module() # optional - sage.rings.finite_rings Free module of degree 1 and rank 1 over Maximal order of Rational function field in x over Finite Field of size 7 Echelon basis matrix: [x^2 + 1] - sage: V, from_V, to_V = K.vector_space(); V + sage: V, from_V, to_V = K.vector_space(); V # optional - sage.rings.finite_rings Vector space of dimension 1 over Rational function field in x over Finite Field of size 7 - sage: I.module().is_submodule(V) + sage: I.module().is_submodule(V) # optional - sage.rings.finite_rings True """ return self._module -class FunctionFieldIdealInfinite_polymod(FunctionFieldIdealInfinite): - """ - Ideals of the infinite maximal order of an algebraic function field. - - INPUT: - - - ``ring`` -- infinite maximal order of the function field - - - ``ideal`` -- ideal in the inverted function field - - 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: Oinf.ideal(1/y) - Ideal (1/x^4*y^2) of Maximal infinite order of Function field - in y defined by y^3 + y^2 + 2*x^4 - """ - def __init__(self, ring, ideal): - """ - Initialize this ideal. - - TESTS:: - - 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/y) - sage: TestSuite(I).run() - """ - FunctionFieldIdealInfinite.__init__(self, ring) - self._ideal = ideal - - def __hash__(self): - """ - Return the hash of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) - sage: F. = K.extension(t^3 + t^2 - x^4) - sage: Oinf = F.maximal_order_infinite() - sage: I = Oinf.ideal(1/y) - sage: d = { I: 1 } - - 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/y) - sage: d = { I: 1 } - """ - return hash((self.ring(), self._ideal)) - - def __contains__(self, x): - """ - Return ``True`` if ``x`` is in this ideal. - - INPUT: - - - ``x`` -- element of the function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) - sage: F. = K.extension(t^3 + t^2 - x^4) - sage: Oinf = F.maximal_order_infinite() - sage: I = Oinf.ideal(1/y) - sage: 1/y in I - True - sage: 1/x in I - False - sage: 1/x^2 in I - True - """ - F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() - return to_iF(x) in self._ideal - - def _add_(self, other): - """ - Add this ideal with the ``other`` ideal. - - INPUT: - - - ``ideal`` -- 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^2*1/y) - sage: J = Oinf.ideal(1/x) - sage: I + J - Ideal (1/x) of Maximal infinite order of Function field in y - defined by y^3 + y^2 + 2*x^4 - - 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^2*1/y) - sage: J = Oinf.ideal(1/x) - sage: I + J - Ideal (1/x) of Maximal infinite order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - """ - return FunctionFieldIdealInfinite_polymod(self._ring, self._ideal + other._ideal) - - def _mul_(self, other): - """ - Multiply this ideal with the ``other`` ideal. - - INPUT: - - - ``other`` -- 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^2*1/y) - sage: J = Oinf.ideal(1/x) - sage: I * J - Ideal (1/x^7*y^2) of Maximal infinite order of Function field - in y defined by y^3 + y^2 + 2*x^4 - - 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^2*1/y) - sage: J = Oinf.ideal(1/x) - sage: I * J - Ideal (1/x^4*y) of Maximal infinite order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - """ - return FunctionFieldIdealInfinite_polymod(self._ring, self._ideal * other._ideal) - - def __pow__(self, n): - """ - Raise this ideal to ``n``-th power. - - 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: J = Oinf.ideal(1/x) - sage: J^3 - Ideal (1/x^3) of Maximal infinite order of Function field - in y defined by y^3 + y^2 + 2*x^4 - """ - return FunctionFieldIdealInfinite_polymod(self._ring, self._ideal ** n) - - def __invert__(self): - """ - Return the inverted ideal of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) - sage: F. = K.extension(t^3 + t^2 - x^4) - sage: Oinf = F.maximal_order_infinite() - sage: J = Oinf.ideal(y) - sage: ~J - Ideal (1/x^4*y^2) of Maximal infinite order - of Function field in y defined by y^3 + y^2 + 2*x^4 - sage: J * ~J - Ideal (1) of Maximal infinite order of Function field - in y defined by y^3 + y^2 + 2*x^4 - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: Oinf = L.maximal_order_infinite() - sage: J = Oinf.ideal(y) - sage: ~J - Ideal (1/x*y) of Maximal infinite order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - sage: J * ~J - Ideal (1) of Maximal infinite order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - """ - return FunctionFieldIdealInfinite_polymod(self._ring, ~ self._ideal) - - def _richcmp_(self, other, op): - """ - Compare this ideal with the ``other`` ideal with respect to ``op``. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) - sage: F. = K.extension(t^3+t^2-x^4) - sage: Oinf = F.maximal_order_infinite() - sage: I = Oinf.ideal(1/x^2*1/y) - sage: J = Oinf.ideal(1/x) - sage: I * J == J * I - True - sage: I + J == J - True - sage: I + J == I - False - sage: (I < J) == (not J < I) - True - - 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^2*1/y) - sage: J = Oinf.ideal(1/x) - sage: I * J == J * I - True - sage: I + J == J - True - sage: I + J == I - False - sage: (I < J) == (not J < I) - True - """ - return richcmp(self._ideal, other._ideal, op) - - @property - def _relative_degree(self): - """ - Return the relative degree of this 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(1/x) - sage: [J._relative_degree for J,_ in I.factor()] - [1] - """ - if not self.is_prime(): - raise TypeError("not a prime ideal") - - return self._ideal._relative_degree - - def gens(self): - """ - Return a set of generators of this 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(x+y) - sage: I.gens() - (x, y, 1/x^2*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(x+y) - sage: I.gens() - (x, y) - """ - F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() - return tuple(from_iF(b) for b in self._ideal.gens()) - - def gens_two(self): - """ - Return a set of at most two generators of this 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(x+y) - sage: I.gens_two() - (x, y) - - 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(x+y) - sage: I.gens_two() - (x,) - """ - F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() - return tuple(from_iF(b) for b in self._ideal.gens_two()) - - def gens_over_base(self): - """ - Return a set of generators of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3^2)); _. = K[] - sage: F. = K.extension(t^3 + t^2 - x^4) - sage: Oinf = F.maximal_order_infinite() - sage: I = Oinf.ideal(x + y) - sage: I.gens_over_base() - (x, y, 1/x^2*y^2) - """ - F = self.ring().fraction_field() - iF,from_iF,to_iF = F._inversion_isomorphism() - return tuple(from_iF(g) for g in self._ideal.gens_over_base()) - - def ideal_below(self): - """ - Return a set of generators of this ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3^2)); _. = K[] - sage: F. = K.extension(t^3 + t^2 - x^4) - sage: Oinf = F.maximal_order_infinite() - sage: I = Oinf.ideal(1/y^2) - sage: I.ideal_below() - Ideal (x^3) of Maximal order of Rational function field - in x over Finite Field in z2 of size 3^2 - """ - return self._ideal.ideal_below() - - def is_prime(self): - """ - Return ``True`` if this ideal is a prime ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3^2)); _. = 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^3*y^2) of Maximal infinite order of Function field - in y defined by y^3 + y^2 + 2*x^4)^3 - sage: I.is_prime() - False - sage: J = I.factor()[0][0] - sage: J.is_prime() - True - - 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*y) of Maximal infinite order of Function field in y - defined by y^2 + y + (x^2 + 1)/x)^2 - sage: I.is_prime() - False - sage: J = I.factor()[0][0] - sage: J.is_prime() - True - """ - return self._ideal.is_prime() - - @cached_method - def prime_below(self): - """ - Return the prime of the base order that underlies this prime ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3^2)); _. = 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^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.prime_below() - Ideal (1/x) of Maximal infinite order of Rational function field - in x over Finite Field in z2 of size 3^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*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.prime_below() - Ideal (1/x) of Maximal infinite order of Rational function field in x - over Finite Field of size 2 - """ - if not self.is_prime(): - raise TypeError("not a prime ideal") - - F = self.ring().fraction_field() - K = F.base_field() - return K.maximal_order_infinite().prime_ideal() - - 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)) - - def _factor(self): - """ - Return factorization of the ideal. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); R. = 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, 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)] - """ - 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_polymod(O, iprime) - factors.append((prime, exp)) - return factors - - class IdealMonoid(UniqueRepresentation, Parent): r""" The monoid of ideals in orders of function fields. @@ -3259,9 +997,9 @@ class IdealMonoid(UniqueRepresentation, Parent): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: O = K.maximal_order() - sage: M = O.ideal_monoid(); M + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: M = O.ideal_monoid(); M # optional - sage.rings.finite_rings Monoid of ideals of Maximal order of Rational function field in x over Finite Field of size 2 """ @@ -3271,10 +1009,10 @@ def __init__(self, R): TESTS:: - sage: K. = FunctionField(GF(2)) - sage: O = K.maximal_order() - sage: M = O.ideal_monoid() - sage: TestSuite(M).run() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: M = O.ideal_monoid() # optional - sage.rings.finite_rings + sage: TestSuite(M).run() # optional - sage.rings.finite_rings """ self.Element = R._ideal_class Parent.__init__(self, category = Monoids()) @@ -3288,9 +1026,9 @@ def _repr_(self): TESTS:: - sage: K. = FunctionField(GF(2)) - sage: O = K.maximal_order() - sage: M = O.ideal_monoid(); M._repr_() + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: M = O.ideal_monoid(); M._repr_() # optional - sage.rings.finite_rings 'Monoid of ideals of Maximal order of Rational function field in x over Finite Field of size 2' """ return "Monoid of ideals of %s" % self.__R @@ -3301,9 +1039,9 @@ def ring(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: O = K.maximal_order() - sage: M = O.ideal_monoid(); M.ring() is O + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: M = O.ideal_monoid(); M.ring() is O # optional - sage.rings.finite_rings True """ return self.__R @@ -3314,12 +1052,12 @@ def _element_constructor_(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: O = K.maximal_order() - sage: M = O.ideal_monoid() - sage: M(x) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: M = O.ideal_monoid() # optional - sage.rings.finite_rings + sage: M(x) # optional - sage.rings.finite_rings Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2 - sage: M([x-4, 1/x]) + sage: M([x-4, 1/x]) # optional - sage.rings.finite_rings Ideal (1/x) of Maximal order of Rational function field in x over Finite Field of size 2 """ try: # x is an ideal @@ -3334,12 +1072,12 @@ def _coerce_map_from_(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: O = K.maximal_order() - sage: M = O.ideal_monoid() - sage: M.has_coerce_map_from(O) # indirect doctest + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: M = O.ideal_monoid() # optional - sage.rings.finite_rings + sage: M.has_coerce_map_from(O) # indirect doctest # optional - sage.rings.finite_rings True - sage: M.has_coerce_map_from(O.ideal_monoid()) + sage: M.has_coerce_map_from(O.ideal_monoid()) # optional - sage.rings.finite_rings True """ if isinstance(x, IdealMonoid): @@ -3353,10 +1091,10 @@ def _an_element_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: O = K.maximal_order() - sage: M = O.ideal_monoid() - sage: M.an_element() # indirect doctest; random + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: M = O.ideal_monoid() # optional - sage.rings.finite_rings + sage: M.an_element() # indirect doctest; random # optional - sage.rings.finite_rings Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2 """ diff --git a/src/sage/rings/function_field/ideal_polymod.py b/src/sage/rings/function_field/ideal_polymod.py new file mode 100644 index 00000000000..b105c6047b1 --- /dev/null +++ b/src/sage/rings/function_field/ideal_polymod.py @@ -0,0 +1,1696 @@ +# sage.doctest: optional - sage.rings.function_field +r""" +Ideals of function fields: extension +""" + +#***************************************************************************** +# Copyright (C) 2023 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/ +#***************************************************************************** + +import itertools +from sage.rings.infinity import infinity +from sage.arith.power import generic_power +from sage.misc.cachefunc import cached_method +from sage.misc.lazy_attribute import lazy_attribute +from sage.structure.richcmp import richcmp +from sage.matrix.constructor import matrix + +from .ideal import FunctionFieldIdeal, FunctionFieldIdealInfinite + +class FunctionFieldIdeal_polymod(FunctionFieldIdeal): + """ + Fractional ideals of algebraic function fields + + INPUT: + + - ``ring`` -- order in a function field + + - ``hnf`` -- matrix in hermite normal form + + - ``denominator`` -- denominator + + The rows of ``hnf`` is a basis of the ideal, which itself is + ``denominator`` times the fractional ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: O.ideal(y) # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x + """ + def __init__(self, ring, hnf, denominator=1): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: TestSuite(I).run() # optional - sage.rings.finite_rings + """ + FunctionFieldIdeal.__init__(self, ring) + + # the denominator and the entries of the hnf are + # univariate polynomials. + self._hnf = hnf + self._denominator = denominator + + # for prime ideals + self._relative_degree = None + self._ramification_index = None + self._prime_below = None + self._beta = None + + # beta in matrix form for fast multiplication + self._beta_matrix = None + + # (p, q) with irreducible polynomial p and q an element of O in vector + # form, together generating the prime ideal. This data is obtained by + # Kummer's theorem when this prime ideal is constructed. This is used + # for fast multiplication with other ideal. + self._kummer_form = None + + # tuple of at most two gens: + # the first gen is an element of the base ring of the maximal order + # the second gen is the vector form of an element of the maximal order + # if the second gen is zero, the tuple has only the first gen. + self._gens_two_vecs = None + + def __bool__(self): + """ + Test if this ideal is zero. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y); I # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x + sage: I.is_zero() # optional - sage.rings.finite_rings + False + sage: J = 0*I; J # optional - sage.rings.finite_rings + Zero ideal of Maximal order of Function field in y defined by y^2 + x^3*y + x + sage: J.is_zero() # optional - sage.rings.finite_rings + True + + sage: K. = FunctionField(GF(2)); _.=K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y); I # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + sage: I.is_zero() # optional - sage.rings.finite_rings + False + sage: J = 0*I; J # optional - sage.rings.finite_rings + Zero ideal of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x + sage: J.is_zero() # optional - sage.rings.finite_rings + True + """ + return self._hnf.nrows() != 0 + + + + def __hash__(self): + """ + Return the hash of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings + sage: { I: 2 }[I] == 2 # optional - sage.rings.finite_rings + True + + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings + sage: { I: 2 }[I] == 2 # optional - sage.rings.finite_rings + True + """ + return hash((self._ring, self._hnf, self._denominator)) + + def __contains__(self, x): + """ + Return ``True`` if ``x`` is in this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal([y]); I # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y + defined by y^2 + 6*x^3 + 6 + sage: x * y in I # optional - sage.rings.finite_rings + True + sage: y / x in I # optional - sage.rings.finite_rings + False + sage: y^2 - 2 in I # optional - sage.rings.finite_rings + False + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal([y]); I # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + sage: x * y in I # optional - sage.rings.finite_rings + True + sage: y / x in I # optional - sage.rings.finite_rings + False + sage: y^2 - 2 in I # optional - sage.rings.finite_rings + False + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal([y]); I + Ideal (y) of Maximal order of Function field in y + defined by y^2 - x^3 - 1 + sage: x * y in I + True + sage: y / x in I + False + sage: y^2 - 2 in I + False + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal([y]); I + Ideal (y) of Maximal order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + sage: x * y in I + True + sage: y / x in I + False + sage: y^2 - 2 in I + False + """ + from sage.modules.free_module_element import vector + + vec = self.ring().coordinate_vector(self._denominator * x) + v = [] + for e in vec: + if e.denominator() != 1: + return False + v.append(e.numerator()) + vec = vector(v) + return vec in self._hnf.image() + + def __invert__(self): + """ + Return the inverse fractional ideal of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: ~I # optional - sage.rings.finite_rings + Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 + sage: I^(-1) # optional - sage.rings.finite_rings + Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 + sage: ~I * I # optional - sage.rings.finite_rings + Ideal (1) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 + + :: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: ~I # optional - sage.rings.finite_rings + Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order + of Function field in y defined by y^2 + y + (x^2 + 1)/x + sage: I^(-1) # optional - sage.rings.finite_rings + Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order + of Function field in y defined by y^2 + y + (x^2 + 1)/x + sage: ~I * I # optional - sage.rings.finite_rings + Ideal (1) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x + + :: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: ~I + Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 - x^3 - 1 + sage: I^(-1) + Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 - x^3 - 1 + sage: ~I * I + Ideal (1) of Maximal order of Function field in y defined by y^2 - x^3 - 1 + + :: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: O = L.maximal_order() + sage: I = O.ideal(y) + sage: ~I + Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order + of Function field in y defined by y^2 + y + (x^2 + 1)/x + sage: I^(-1) + Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order + of Function field in y defined by y^2 + y + (x^2 + 1)/x + sage: ~I * I + Ideal (1) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x + """ + R = self.ring() + T = R._codifferent_matrix() + I = self * R.codifferent() + J = I._denominator * (I._hnf * T).inverse() + return R._ideal_from_vectors(J.columns()) + + def _richcmp_(self, other, op): + """ + Compare this ideal with the other ideal with respect to ``op``. + + INPUT: + + - ``other`` -- ideal + + - ``op`` -- comparison operator + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings + sage: I == I + I # optional - sage.rings.finite_rings + True + sage: I == I * I # optional - sage.rings.finite_rings + False + + :: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings + sage: I == I + I # optional - sage.rings.finite_rings + True + sage: I == I * I # optional - sage.rings.finite_rings + False + sage: I < I * I # optional - sage.rings.finite_rings + True + sage: I > I * I # optional - sage.rings.finite_rings + False + """ + return richcmp((self._denominator, self._hnf), (other._denominator, other._hnf), op) + + def _add_(self, other): + """ + Add with other ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I + J # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x + + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I + J # optional - sage.rings.finite_rings + Ideal (1, y) of Maximal order of Function field in y defined by y^2 + y + (x^2 + 1)/x + """ + ds = self._denominator + do = other._denominator + vecs1 = [do * r for r in self._hnf] + vecs2 = [ds * r for r in other._hnf] + return self._ring._ideal_from_vectors_and_denominator(vecs1 + vecs2, ds * do) + + def _mul_(self, other): + """ + Multiply with other ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I * J # optional - sage.rings.finite_rings + Ideal (x^4 + x^2 + x, x*y + x^2) of Maximal order + of Function field in y defined by y^2 + x^3*y + x + + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I * J # optional - sage.rings.finite_rings + Ideal ((x + 1)*y + (x^2 + 1)/x) of Maximal order + of Function field in y defined by y^2 + y + (x^2 + 1)/x + """ + O = self._ring + mul = O._mul_vecs + + if self._kummer_form is not None: + p, q = self._kummer_form + vecs = list(p * other._hnf) + [mul(q, v) for v in other._hnf] + elif other._kummer_form is not None: + p, q = other._kummer_form + vecs = list(p * self._hnf) + [mul(q, v) for v in self._hnf] + elif self._gens_two_vecs is not None: + if len(self._gens_two_vecs) == 1: + g1, = self._gens_two_vecs + vecs = list(g1 * other._hnf) + else: + g1, g2 = self._gens_two_vecs + vecs = list(g1 * other._hnf) + [mul(g2, v) for v in other._hnf] + elif other._gens_two_vecs is not None: + if len(other._gens_two_vecs) == 1: + g1, = other._gens_two_vecs + vecs = list(g1 * self._hnf) + else: + g1, g2 = other._gens_two_vecs + vecs = list(g1 * self._hnf) + [mul(g2, v) for v in self._hnf] + else: + vecs = [mul(r1,r2) for r1 in self._hnf for r2 in other._hnf] + + return O._ideal_from_vectors_and_denominator(vecs, self._denominator * other._denominator) + + def _acted_upon_(self, other, on_left): + """ + Multiply ``other`` and this ideal on the right. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: J = O.ideal(x) # optional - sage.rings.finite_rings + sage: x * I == I * J # optional - sage.rings.finite_rings + True + + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: J = O.ideal(x) # optional - sage.rings.finite_rings + sage: x * I == I * J # optional - sage.rings.finite_rings + True + """ + from sage.modules.free_module_element import vector + + O = self._ring + mul = O._mul_vecs + + # compute the vector form of other element + v = O.coordinate_vector(other) + d = v.denominator() + vec = vector([(d * c).numerator() for c in v]) + + # multiply with the ideal + vecs = [mul(vec, r) for r in self._hnf] + + return O._ideal_from_vectors_and_denominator(vecs, d * self._denominator) + + def intersect(self, other): + """ + Intersect this ideal with the other ideal as fractional ideals. + + INPUT: + + - ``other`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: J = O.ideal(x) # optional - sage.rings.finite_rings + sage: I.intersect(J) == I * J * (I + J)^-1 # optional - sage.rings.finite_rings + True + + """ + from sage.matrix.special import block_matrix + from .hermite_form_polynomial import reversed_hermite_form + + A = self._hnf + B = other._hnf + + ds = self.denominator() + do = other.denominator() + d = ds.lcm(do) + if not d.is_one(): + A = (d // ds) * A + B = (d // do) * B + + MS = A.matrix_space() + I = MS.identity_matrix() + O = MS.zero() + n = A.ncols() + + # intersect the row spaces of A and B + M = block_matrix([[I,I],[A,O],[O,B]]) + + # reversed Hermite form + U = reversed_hermite_form(M, transformation=True) + + vecs = [U[i][:n] for i in range(n)] + + return self._ring._ideal_from_vectors_and_denominator(vecs, d) + + def hnf(self): + """ + Return the matrix in hermite normal form representing this ideal. + + See also :meth:`denominator` + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y*(y+1)); I.hnf() # optional - sage.rings.finite_rings + [x^6 + x^3 0] + [ x^3 + 1 1] + + :: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y*(y+1)); I.hnf() + [x^6 + x^3 0] + [ x^3 + 1 1] + """ + return self._hnf + + def denominator(self): + """ + Return the denominator of this fractional ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y/(y+1)) # optional - sage.rings.finite_rings + sage: d = I.denominator(); d # optional - sage.rings.finite_rings + x^3 + sage: d in O # optional - sage.rings.finite_rings + True + + :: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) + sage: O = L.maximal_order() + sage: I = O.ideal(y/(y+1)) + sage: d = I.denominator(); d + x^3 + sage: d in O + True + """ + return self._denominator + + @cached_method + def module(self): + """ + Return the module, that is the ideal viewed as a module + over the base maximal order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings + sage: I.module() # optional - sage.rings.finite_rings + Free module of degree 2 and rank 2 over Maximal order + of Rational function field in x over Finite Field of size 7 + Echelon basis matrix: + [ 1 0] + [ 0 1/(x^3 + 1)] + """ + F = self.ring().fraction_field() + V, fr, to = F.vector_space() + O = F.base_field().maximal_order() + return V.span([to(g) for g in self.gens_over_base()], base_ring=O) + + @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. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens_over_base() # optional - sage.rings.finite_rings + (x^4 + x^2 + x, y + x) + + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens_over_base() # optional - sage.rings.finite_rings + (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[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(1/y) # optional - sage.rings.finite_rings + sage: I._gens_over_base # optional - sage.rings.finite_rings + ([x, y], x) + """ + gens = [] + for row in self._hnf: + gens.append(sum([c1*c2 for c1,c2 in zip(row, self._ring.basis())])) + return gens, self._denominator + + def gens(self): + """ + Return a set of generators of this ideal. + + This provides whatever set of generators as quickly + as possible. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings + (x^4 + x^2 + x, y + x) + + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings + (x^3 + 1, y + x) + """ + return self.gens_over_base() + + @cached_method + def basis_matrix(self): + """ + Return the matrix of basis vectors of this ideal as a module. + + The basis matrix is by definition the hermite norm form of the ideal + divided by the denominator. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings + sage: I.denominator() * I.basis_matrix() == I.hnf() # optional - sage.rings.finite_rings + True + """ + d = self.denominator() + m = (d * self).hnf() + if d != 1: + m = ~d * m + m.set_immutable() + return m + + def is_integral(self): + """ + Return ``True`` if this is an integral ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings + sage: I.is_integral() # optional - sage.rings.finite_rings + False + sage: J = I.denominator() * I # optional - sage.rings.finite_rings + sage: J.is_integral() # optional - sage.rings.finite_rings + True + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings + sage: I.is_integral() # optional - sage.rings.finite_rings + False + sage: J = I.denominator() * I # optional - sage.rings.finite_rings + sage: J.is_integral() # optional - sage.rings.finite_rings + True + + sage: K. = FunctionField(QQ); _. = PolynomialRing(K) + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.is_integral() + False + sage: J = I.denominator() * I + sage: J.is_integral() + True + """ + return self.denominator() == 1 + + def ideal_below(self): + """ + Return the ideal below this ideal. + + This is defined only for integral ideals. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings + sage: I.ideal_below() # optional - sage.rings.finite_rings + Traceback (most recent call last): + ... + TypeError: not an integral ideal + sage: J = I.denominator() * I # optional - sage.rings.finite_rings + sage: J.ideal_below() # optional - sage.rings.finite_rings + Ideal (x^3 + x^2 + x) of Maximal order of Rational function field + in x over Finite Field of size 2 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, 1/y) # optional - sage.rings.finite_rings + sage: I.ideal_below() # optional - sage.rings.finite_rings + Traceback (most recent call last): + ... + TypeError: not an integral ideal + sage: J = I.denominator() * I # optional - sage.rings.finite_rings + sage: J.ideal_below() # optional - sage.rings.finite_rings + Ideal (x^3 + x) of Maximal order of Rational function field + in x over Finite Field of size 2 + + sage: K. = FunctionField(QQ); _. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(x, 1/y) + sage: I.ideal_below() + Traceback (most recent call last): + ... + TypeError: not an integral ideal + sage: J = I.denominator() * I + sage: J.ideal_below() + Ideal (x^3 + x^2 + x) of Maximal order of Rational function field + in x over Rational Field + """ + if not self.is_integral(): + raise TypeError("not an integral ideal") + + K = self.ring().fraction_field().base_field().maximal_order() + + # self._hnf is in reversed hermite normal form, that is, lower + # triangular form. Thus the generator of the ideal below is + # just the (0,0) entry of the normal form. When self._hnf was in + # hermite normal form, that is, upper triangular form, then the + # generator can be computed in the following way: + # + # m = matrix([hnf[0].parent().gen(0)] + list(hnf)) + # _,T = m.hermite_form(transformation=True) + # return T[-1][0] + # + # This is certainly less efficient! This is an argument for using + # reversed hermite normal form for ideal representation. + l = self._hnf[0][0] + + return K.ideal(l) + + def norm(self): + """ + Return the norm of this fractional ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: i1 = O.ideal(x) # optional - sage.rings.finite_rings + sage: i2 = O.ideal(y) # optional - sage.rings.finite_rings + sage: i3 = i1 * i2 # optional - sage.rings.finite_rings + sage: i3.norm() == i1.norm() * i2.norm() # optional - sage.rings.finite_rings + True + sage: i1.norm() # optional - sage.rings.finite_rings + x^3 + sage: i1.norm() == x ** F.degree() # optional - sage.rings.finite_rings + True + sage: i2.norm() # optional - sage.rings.finite_rings + x^6 + x^4 + x^2 + sage: i2.norm() == y.norm() # optional - sage.rings.finite_rings + True + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: i1 = O.ideal(x) # optional - sage.rings.finite_rings + sage: i2 = O.ideal(y) # optional - sage.rings.finite_rings + sage: i3 = i1 * i2 # optional - sage.rings.finite_rings + sage: i3.norm() == i1.norm() * i2.norm() # optional - sage.rings.finite_rings + True + sage: i1.norm() # optional - sage.rings.finite_rings + x^2 + sage: i1.norm() == x ** L.degree() # optional - sage.rings.finite_rings + True + sage: i2.norm() # optional - sage.rings.finite_rings + (x^2 + 1)/x + sage: i2.norm() == y.norm() # optional - sage.rings.finite_rings + True + """ + n = 1 + for e in self.basis_matrix().diagonal(): + n *= e + return n + + @cached_method + def is_prime(self): + """ + Return ``True`` if this ideal is a prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: [f.is_prime() for f,_ in I.factor()] # optional - sage.rings.finite_rings + [True, True] + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: [f.is_prime() for f,_ in I.factor()] # optional - sage.rings.finite_rings + [True, True] + + sage: K. = FunctionField(QQ); _. = 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.is_prime() for f,_ in I.factor()] + [True, True] + """ + factors = self.factor() + if len(factors) == 1 and factors[0][1] == 1: # prime! + prime = factors[0][0] + assert self == prime + self._relative_degree = prime._relative_degree + self._ramification_index = prime._ramification_index + self._prime_below = prime._prime_below + self._beta = prime._beta + self._beta_matrix = prime._beta_matrix + return True + else: + return False + + ################################################### + # The following methods are only for prime ideals # + ################################################### + + def valuation(self, ideal): + """ + Return the valuation of ``ideal`` at this prime ideal. + + INPUT: + + - ``ideal`` -- fractional ideal + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x, (1/(x^3 + x^2 + x))*y^2) # optional - sage.rings.finite_rings + sage: I.is_prime() # optional - sage.rings.finite_rings + True + sage: J = O.ideal(y) # optional - sage.rings.finite_rings + sage: I.valuation(J) # optional - sage.rings.finite_rings + 2 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: [f.valuation(I) for f,_ in I.factor()] # optional - sage.rings.finite_rings + [-1, 2] + + The method closely follows Algorithm 4.8.17 of [Coh1993]_. + """ + from sage.matrix.constructor import matrix + + if ideal.is_zero(): + return infinity + + O = self.ring() + F = O.fraction_field() + n = F.degree() + + # beta_matrix is for fast multiplication with beta. For performance, + # this is computed here rather than when the prime is constructed. + if self._beta_matrix is None: + beta = self._beta + m = [] + for i in range(n): + mtable_row = O._mtable[i] + c = sum(beta[j] * mtable_row[j] for j in range(n)) + m.append(c) + self._beta_matrix = matrix(m) + + B = self._beta_matrix + + # Step 1: compute the valuation of the denominator times the ideal + # + # This part is highly optimized as it is critical for + # overall performance of the function field machinery. + p = self.prime_below().gen().numerator() + h = ideal._hnf.list() + val = min([c.valuation(p) for c in h]) + i = self._ramification_index * val + while True: + ppow = p ** val + h = (matrix(n, [c // ppow for c in h]) * B).list() + val = min([c.valuation(p) for c in h]) + if val.is_zero(): + break + i += self._ramification_index * (val - 1) + 1 + + # Step 2: compute the valuation of the denominator + j = self._ramification_index * ideal.denominator().valuation(p) + + # Step 3: return the valuation + return i - j + + def prime_below(self): + """ + Return the prime lying below this prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: [f.prime_below() for f,_ in I.factor()] # optional - sage.rings.finite_rings + [Ideal (x) of Maximal order of Rational function field in x + over Finite Field of size 2, Ideal (x^2 + x + 1) of Maximal order + of Rational function field in x over Finite Field of size 2] + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: [f.prime_below() for f,_ in I.factor()] # optional - sage.rings.finite_rings + [Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2, + Ideal (x + 1) of Maximal order of Rational function field in x over Finite Field of size 2] + + sage: K. = FunctionField(QQ); _. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: O = F.maximal_order() + sage: I = O.ideal(y) + sage: [f.prime_below() for f,_ in I.factor()] + [Ideal (x) of Maximal order of Rational function field in x over Rational Field, + Ideal (x^2 + x + 1) of Maximal order of Rational function field in x over Rational Field] + """ + return self._prime_below + + def _factor(self): + """ + Return the factorization of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: I == I.factor().prod() # indirect doctest # optional - sage.rings.finite_rings + 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 + + +class FunctionFieldIdeal_global(FunctionFieldIdeal_polymod): + """ + Fractional ideals of canonical function fields + + INPUT: + + - ``ring`` -- order in a function field + + - ``hnf`` -- matrix in hermite normal form + + - ``denominator`` -- denominator + + The rows of ``hnf`` is a basis of the ideal, which itself is + ``denominator`` times the fractional ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: O.ideal(y) # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y defined by y^2 + x^3*y + x + """ + def __init__(self, ring, hnf, denominator=1): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(5)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3*y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: TestSuite(I).run() # optional - sage.rings.finite_rings + """ + FunctionFieldIdeal_polymod.__init__(self, ring, hnf, denominator) + + def __pow__(self, mod): + """ + Return ``self`` to the power of ``mod``. + + If a two-generators representation of ``self`` is known, it is used + to speed up powering. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^7 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: J = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: S = I / J # optional - sage.rings.finite_rings + sage: a = S^100 # optional - sage.rings.finite_rings + sage: _ = S.gens_two() # optional - sage.rings.finite_rings + sage: b = S^100 # faster # optional - sage.rings.finite_rings + sage: b == I^100 / J^100 # optional - sage.rings.finite_rings + True + sage: b == a # optional - sage.rings.finite_rings + True + """ + if mod > 2 and self._gens_two_vecs is not None: + O = self._ring + mul = O._mul_vecs + R = self._hnf.base_ring() + n = self._hnf.ncols() + + I = matrix.identity(R, n) + + if len(self._gens_two_vecs) == 1: + p, = self._gens_two_vecs + ppow = p**mod + J = [ppow * v for v in I] + else: + p, q = self._gens_two_vecs + q = sum(e1 * e2 for e1,e2 in zip(O.basis(), q)) + ppow = p**mod + qpow = O._coordinate_vector(q**mod) + J = [ppow * v for v in I] + [mul(qpow,v) for v in I] + + return O._ideal_from_vectors_and_denominator(J, self._denominator**mod) + + return generic_power(self, mod) + + def gens(self): + """ + Return a set of generators of this ideal. + + This provides whatever set of generators as quickly + as possible. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x^3*Y - x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings + (x^4 + x^2 + x, y + x) + + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings + (x^3 + 1, y + x) + """ + if self._gens_two.is_in_cache(): + return self._gens_two.cache + else: + return self.gens_over_base() + + def gens_two(self): + r""" + Return two generators of this fractional ideal. + + If the ideal is principal, one generator *may* be returned. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: I # indirect doctest # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field + in y defined by y^3 + x^6 + x^4 + x^2 + sage: ~I # indirect doctest # optional - sage.rings.finite_rings + Ideal ((1/(x^6 + x^4 + x^2))*y^2) of Maximal order of Function field + in y defined by y^3 + x^6 + x^4 + x^2 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y) # optional - sage.rings.finite_rings + sage: I # indirect doctest # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + sage: ~I # indirect doctest # optional - sage.rings.finite_rings + Ideal ((x/(x^2 + 1))*y + x/(x^2 + 1)) of Maximal order + of Function field in y defined by y^2 + y + (x^2 + 1)/x + """ + d = self.denominator() + return tuple(e/d for e in self._gens_two()) + + @cached_method + def _gens_two(self): + r""" + Return a set of two generators of the integral ideal, that is + the denominator times this fractional ideal. + + ALGORITHM: + + At most two generators are required to generate ideals in + Dedekind domains. + + Lemma 4.7.9, algorithm 4.7.10, and exercise 4.29 of [Coh1993]_ + tell us that for an integral ideal `I` in a number field, if + we pick `a` such that `\gcd(N(I), N(a)/N(I)) = 1`, then `a` + and `N(I)` generate the ideal. `N()` is the norm, and this + result (presumably) generalizes to function fields. + + After computing `N(I)`, we search exhaustively to find `a`. + + .. TODO:: + + Always return a single generator for a principal ideal. + + Testing for principality is not trivial. Algorithm 6.5.10 + of [Coh1993]_ could probably be adapted for function fields. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^2, x*y, x + y) # optional - sage.rings.finite_rings + sage: I._gens_two() # optional - sage.rings.finite_rings + (x, y) + + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y - x) # optional - sage.rings.finite_rings + sage: y.zeros()[0].prime_ideal()._gens_two() # optional - sage.rings.finite_rings + (x,) + """ + O = self.ring() + F = O.fraction_field() + + if self._kummer_form is not None: # prime ideal + _g1, _g2 = self._kummer_form + g1 = F(_g1) + g2 = sum([c1*c2 for c1,c2 in zip(_g2, O.basis())]) + if g2: + self._gens_two_vecs = (_g1, _g2) + return (g1,g2) + else: + self._gens_two_vecs = (_g1,) + return (g1,) + + ### start to search for two generators + + hnf = self._hnf + + norm = 1 + for e in hnf.diagonal(): + norm *= e + + if norm.is_constant(): # unit ideal + self._gens_two_vecs = (1,) + return (F(1),) + + # one generator; see .ideal_below() + _l = hnf[0][0] + p = _l.degree() + l = F(_l) + + if self._hnf == O.ideal(l)._hnf: # principal ideal + self._gens_two_vecs = (_l,) + return (l,) + + R = hnf.base_ring() + + basis = [] + for row in hnf: + basis.append(sum([c1*c2 for c1,c2 in zip(row, O.basis())])) + + n = len(basis) + alpha = None + + def check(alpha): + alpha_norm = alpha.norm().numerator() # denominator is 1 + return norm.gcd(alpha_norm // norm) == 1 + + # Trial 1: search for alpha among generators + for alpha in basis: + if check(alpha): + self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) + return (l, alpha) + + # Trial 2: exhaustive search for alpha using only polynomials + # with coefficients 0 or 1 + for d in range(p): + G = itertools.product(itertools.product([0,1],repeat=d+1), repeat=n) + for g in G: + alpha = sum([R(c1)*c2 for c1,c2 in zip(g, basis)]) + if check(alpha): + self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) + return (l, alpha) + + # Trial 3: exhaustive search for alpha using all polynomials + for d in range(p): + 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 + + alpha = sum([c1*c2 for c1,c2 in zip(g, basis)]) + if check(alpha): + self._gens_two_vecs = (_l, O._coordinate_vector(alpha)) + return (l, alpha) + + # should not reach here + raise ValueError("no two generators found") + + +class FunctionFieldIdealInfinite_polymod(FunctionFieldIdealInfinite): + """ + Ideals of the infinite maximal order of an algebraic function field. + + INPUT: + + - ``ring`` -- infinite maximal order of the function field + + - ``ideal`` -- ideal in the inverted function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.ideal(1/y) # optional - sage.rings.finite_rings + Ideal (1/x^4*y^2) of Maximal infinite order of Function field + in y defined by y^3 + y^2 + 2*x^4 + """ + def __init__(self, ring, ideal): + """ + Initialize this ideal. + + TESTS:: + + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings + sage: TestSuite(I).run() # optional - sage.rings.finite_rings + """ + FunctionFieldIdealInfinite.__init__(self, ring) + self._ideal = ideal + + def __hash__(self): + """ + Return the hash of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings + sage: d = { I: 1 } # optional - sage.rings.finite_rings + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings + sage: d = { I: 1 } # optional - sage.rings.finite_rings + """ + return hash((self.ring(), self._ideal)) + + def __contains__(self, x): + """ + Return ``True`` if ``x`` is in this ideal. + + INPUT: + + - ``x`` -- element of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/y) # optional - sage.rings.finite_rings + sage: 1/y in I # optional - sage.rings.finite_rings + True + sage: 1/x in I # optional - sage.rings.finite_rings + False + sage: 1/x^2 in I # optional - sage.rings.finite_rings + True + """ + F = self.ring().fraction_field() + iF,from_iF,to_iF = F._inversion_isomorphism() + return to_iF(x) in self._ideal + + def _add_(self, other): + """ + Add this ideal with the ``other`` ideal. + + INPUT: + + - ``ideal`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I + J # optional - sage.rings.finite_rings + Ideal (1/x) of Maximal infinite order of Function field in y + defined by y^3 + y^2 + 2*x^4 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I + J # optional - sage.rings.finite_rings + Ideal (1/x) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + """ + return FunctionFieldIdealInfinite_polymod(self._ring, self._ideal + other._ideal) + + def _mul_(self, other): + """ + Multiply this ideal with the ``other`` ideal. + + INPUT: + + - ``other`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I * J # optional - sage.rings.finite_rings + Ideal (1/x^7*y^2) of Maximal infinite order of Function field + in y defined by y^3 + y^2 + 2*x^4 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I * J # optional - sage.rings.finite_rings + Ideal (1/x^4*y) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + """ + return FunctionFieldIdealInfinite_polymod(self._ring, self._ideal * other._ideal) + + def __pow__(self, n): + """ + Raise this ideal to ``n``-th power. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: J^3 # optional - sage.rings.finite_rings + Ideal (1/x^3) of Maximal infinite order of Function field + in y defined by y^3 + y^2 + 2*x^4 + """ + return FunctionFieldIdealInfinite_polymod(self._ring, self._ideal ** n) + + def __invert__(self): + """ + Return the inverted ideal of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(y) # optional - sage.rings.finite_rings + sage: ~J # optional - sage.rings.finite_rings + Ideal (1/x^4*y^2) of Maximal infinite order + of Function field in y defined by y^3 + y^2 + 2*x^4 + sage: J * ~J # optional - sage.rings.finite_rings + Ideal (1) of Maximal infinite order of Function field + in y defined by y^3 + y^2 + 2*x^4 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(y) # optional - sage.rings.finite_rings + sage: ~J # optional - sage.rings.finite_rings + Ideal (1/x*y) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + sage: J * ~J # optional - sage.rings.finite_rings + Ideal (1) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + """ + return FunctionFieldIdealInfinite_polymod(self._ring, ~ self._ideal) + + def _richcmp_(self, other, op): + """ + Compare this ideal with the ``other`` ideal with respect to ``op``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I * J == J * I # optional - sage.rings.finite_rings + True + sage: I + J == J # optional - sage.rings.finite_rings + True + sage: I + J == I # optional - sage.rings.finite_rings + False + sage: (I < J) == (not J < I) # optional - sage.rings.finite_rings + True + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x^2*1/y) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I * J == J * I # optional - sage.rings.finite_rings + True + sage: I + J == J # optional - sage.rings.finite_rings + True + sage: I + J == I # optional - sage.rings.finite_rings + False + sage: (I < J) == (not J < I) # optional - sage.rings.finite_rings + True + """ + return richcmp(self._ideal, other._ideal, op) + + @property + def _relative_degree(self): + """ + Return the relative degree of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: [J._relative_degree for J,_ in I.factor()] # optional - sage.rings.finite_rings + [1] + """ + if not self.is_prime(): + raise TypeError("not a prime ideal") + + return self._ideal._relative_degree + + def gens(self): + """ + Return a set of generators of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings + (x, y, 1/x^2*y^2) + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings + (x, y) + """ + F = self.ring().fraction_field() + iF,from_iF,to_iF = F._inversion_isomorphism() + return tuple(from_iF(b) for b in self._ideal.gens()) + + def gens_two(self): + """ + Return a set of at most two generators of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens_two() # optional - sage.rings.finite_rings + (x, y) + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens_two() # optional - sage.rings.finite_rings + (x,) + """ + F = self.ring().fraction_field() + iF,from_iF,to_iF = F._inversion_isomorphism() + return tuple(from_iF(b) for b in self._ideal.gens_two()) + + def gens_over_base(self): + """ + Return a set of generators of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x + y) # optional - sage.rings.finite_rings + sage: I.gens_over_base() # optional - sage.rings.finite_rings + (x, y, 1/x^2*y^2) + """ + F = self.ring().fraction_field() + iF,from_iF,to_iF = F._inversion_isomorphism() + return tuple(from_iF(g) for g in self._ideal.gens_over_base()) + + def ideal_below(self): + """ + Return a set of generators of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/y^2) # optional - sage.rings.finite_rings + sage: I.ideal_below() # optional - sage.rings.finite_rings + Ideal (x^3) of Maximal order of Rational function field + in x over Finite Field in z2 of size 3^2 + """ + return self._ideal.ideal_below() + + def is_prime(self): + """ + Return ``True`` if this ideal is a prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I.factor() # optional - sage.rings.finite_rings + (Ideal (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: I.is_prime() # optional - sage.rings.finite_rings + False + sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings + sage: J.is_prime() # optional - sage.rings.finite_rings + True + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I.factor() # optional - sage.rings.finite_rings + (Ideal (1/x*y) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x)^2 + sage: I.is_prime() # optional - sage.rings.finite_rings + False + sage: J = I.factor()[0][0] # optional - sage.rings.finite_rings + sage: J.is_prime() # optional - sage.rings.finite_rings + True + """ + return self._ideal.is_prime() + + @cached_method + def prime_below(self): + """ + Return the prime of the base order that underlies this prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(3^2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 + t^2 - x^4) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I.factor() # optional - sage.rings.finite_rings + (Ideal (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] # optional - sage.rings.finite_rings + sage: J.is_prime() # optional - sage.rings.finite_rings + True + sage: J.prime_below() # optional - sage.rings.finite_rings + Ideal (1/x) of Maximal infinite order of Rational function field + in x over Finite Field in z2 of size 3^2 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: I.factor() # optional - sage.rings.finite_rings + (Ideal (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] # optional - sage.rings.finite_rings + sage: J.is_prime() # optional - sage.rings.finite_rings + True + sage: J.prime_below() # optional - sage.rings.finite_rings + Ideal (1/x) of Maximal infinite order of Rational function field in x + over Finite Field of size 2 + """ + if not self.is_prime(): + raise TypeError("not a prime ideal") + + F = self.ring().fraction_field() + K = F.base_field() + return K.maximal_order_infinite().prime_ideal() + + 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[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings + sage: [f.valuation(I) for f,_ in I.factor()] # optional - sage.rings.finite_rings + [-1] + """ + if not self.is_prime(): + raise TypeError("not a prime ideal") + + return self._ideal.valuation(self.ring()._to_iF(ideal)) + + def _factor(self): + """ + Return factorization of the ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: f = 1/x # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(f) # optional - sage.rings.finite_rings + sage: I._factor() # optional - sage.rings.finite_rings + [(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)] + """ + 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_polymod(O, iprime) + factors.append((prime, exp)) + return factors diff --git a/src/sage/rings/function_field/ideal_rational.py b/src/sage/rings/function_field/ideal_rational.py new file mode 100644 index 00000000000..9f110034c55 --- /dev/null +++ b/src/sage/rings/function_field/ideal_rational.py @@ -0,0 +1,607 @@ +r""" +Ideals of function fields: rational +""" + +#***************************************************************************** +# Copyright (C) 2023 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 sage.misc.cachefunc import cached_method +from sage.structure.richcmp import richcmp +from sage.rings.infinity import infinity + +from .ideal import FunctionFieldIdeal, FunctionFieldIdealInfinite + + +class FunctionFieldIdeal_rational(FunctionFieldIdeal): + """ + Fractional ideals of the maximal order of a rational function field. + + INPUT: + + - ``ring`` -- the maximal order of the rational function field. + + - ``gen`` -- generator of the ideal, an element of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(1/(x^2+x)); I + Ideal (1/(x^2 + x)) of Maximal order of Rational function field in x over Rational Field + """ + def __init__(self, ring, gen): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(1/(x^2+x)) + sage: TestSuite(I).run() + """ + FunctionFieldIdeal.__init__(self, ring) + self._gen = gen + + def __hash__(self): + """ + Return the hash computed from the data. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(1/(x^2+x)) + sage: d = { I: 1, I^2: 2 } + """ + return hash( (self._ring, self._gen) ) + + def __contains__(self, element): + """ + Test if ``element`` is in this ideal. + + INPUT: + + - ``element`` -- element of the function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(1/(x+1)) + sage: x in I + True + """ + return (element / self._gen) in self._ring + + def _richcmp_(self, other, op): + """ + Compare the element with the other element with respect + to the comparison operator. + + INPUT: + + - ``other`` -- element + + - ``op`` -- comparison operator + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(x, x^2 + 1) + sage: J = O.ideal(x^2 + x + 1, x) + sage: I == J + True + sage: I = O.ideal(x) + sage: J = O.ideal(x + 1) + sage: I < J + True + """ + return richcmp(self._gen, other._gen, op) + + def _add_(self, other): + """ + Add this ideal with the ``other`` ideal. + + INPUT: + + - ``other`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(x, x^2 + 1) + sage: J = O.ideal(x^2 + x + 1, x) + sage: I + J == J + I + True + """ + return self._ring.ideal([self._gen, other._gen]) + + def _mul_(self, other): + """ + Multiply this ideal with the ``other`` ideal. + + INPUT: + + - ``other`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(x, x^2 + x) + sage: J = O.ideal(x^2, x) + sage: I * J == J * I + True + """ + return self._ring.ideal([self._gen * other._gen]) + + def _acted_upon_(self, other, on_left): + """ + Multiply ``other`` with this ideal on the right. + + INPUT: + + - ``other`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(x^3 + x^2) + sage: 2 * I + Ideal (x^3 + x^2) of Maximal order of Rational function field in x over Rational Field + sage: x * I + Ideal (x^4 + x^3) of Maximal order of Rational function field in x over Rational Field + """ + return self._ring.ideal([other * self._gen]) + + def __invert__(self): + """ + Return the ideal inverse of this fractional ideal. + + EXAMPLES:: + + sage: F. = FunctionField(QQ) + sage: O = F.maximal_order() + sage: I = O.ideal(x/(x^2+1)) + sage: ~I + Ideal ((x^2 + 1)/x) of Maximal order of Rational function field + in x over Rational Field + """ + return self._ring.ideal([~(self._gen)]) + + def denominator(self): + """ + Return the denominator of this fractional ideal. + + EXAMPLES:: + + sage: F. = FunctionField(QQ) + sage: O = F.maximal_order() + sage: I = O.ideal(x/(x^2+1)) + sage: I.denominator() + x^2 + 1 + """ + return self._gen.denominator() + + def is_prime(self): + """ + Return ``True`` if this is a prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(x^3 + x^2) + sage: [f.is_prime() for f,m in I.factor()] # optional - sage.rings.finite_rings + [True, True] + """ + return self._gen.denominator() == 1 and self._gen.numerator().is_prime() + + @cached_method + def module(self): + """ + Return the module, that is the ideal viewed as a module over the ring. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal(x^3 + x^2) + sage: I.module() # optional - sage.modules + Free module of degree 1 and rank 1 over Maximal order of Rational + function field in x over Rational Field + Echelon basis matrix: + [x^3 + x^2] + sage: J = 0*I + sage: J.module() # optional - sage.modules + Free module of degree 1 and rank 0 over Maximal order of Rational + function field in x over Rational Field + Echelon basis matrix: + [] + """ + V, fr, to = self.ring().fraction_field().vector_space() + return V.span([to(g) for g in self.gens()], base_ring=self.ring()) + + def gen(self): + """ + Return the unique generator of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^2 + x) # optional - sage.rings.finite_rings + sage: I.gen() # optional - sage.rings.finite_rings + x^2 + x + """ + return self._gen + + def gens(self): + """ + Return the tuple of the unique generator of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^2 + x) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings + (x^2 + x,) + """ + return (self._gen,) + + def gens_over_base(self): + """ + Return the generator of this ideal as a rank one module over the maximal + order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^2 + x) # optional - sage.rings.finite_rings + sage: I.gens_over_base() # optional - sage.rings.finite_rings + (x^2 + x,) + """ + 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()] # optional - sage.rings.finite_rings + [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 # optional - sage.rings.finite_rings + 0 + sage: p.valuation(O.ideal(x^2)) # indirect doctest # optional - sage.rings.finite_rings + 2 + sage: p.valuation(O.ideal(1/x^3)) # indirect doctest # optional - sage.rings.finite_rings + -3 + sage: p.valuation(O.ideal(0)) # indirect doctest # optional - sage.rings.finite_rings + +Infinity + """ + return ideal.gen().valuation(self.gen()) + + def _factor(self): + """ + Return the list of prime and multiplicity pairs of the + factorization of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^3*(x+1)^2) # optional - sage.rings.finite_rings + sage: I.factor() # indirect doctest # optional - sage.rings.finite_rings + (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 + """ + return [(self.ring().ideal(f), m) for f, m in self._gen.factor()] + + +class FunctionFieldIdealInfinite_rational(FunctionFieldIdealInfinite): + """ + Fractional ideal of the maximal order of rational function field. + + INPUT: + + - ``ring`` -- infinite maximal order + + - ``gen``-- generator + + Note that the infinite maximal order is a principal ideal domain. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.ideal(x) # optional - sage.rings.finite_rings + Ideal (x) of Maximal infinite order of Rational function field in x over Finite Field of size 2 + """ + def __init__(self, ring, gen): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x) # optional - sage.rings.finite_rings + sage: TestSuite(I).run() # optional - sage.rings.finite_rings + """ + FunctionFieldIdealInfinite.__init__(self, ring) + self._gen = gen + + def __hash__(self): + """ + Return the hash of this fractional ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/x) # optional - sage.rings.finite_rings + sage: d = { I: 1, J: 2 } # optional - sage.rings.finite_rings + """ + return hash( (self.ring(), self._gen) ) + + def __contains__(self, element): + """ + Test if ``element`` is in this ideal. + + INPUT: + + - ``element`` -- element of the function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order_infinite() + sage: I = O.ideal(1/(x+1)) + sage: x in I + False + sage: 1/x in I + True + sage: x/(x+1) in I + False + sage: 1/(x*(x+1)) in I + True + """ + return (element / self._gen) in self._ring + + def _richcmp_(self, other, op): + """ + Compare this ideal and ``other`` with respect to ``op``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x + 1) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(x^2 + x) # optional - sage.rings.finite_rings + sage: I + J == J # optional - sage.rings.finite_rings + True + """ + return richcmp(self._gen, other._gen, op) + + def _add_(self, other): + """ + Add this ideal with the other ideal. + + INPUT: + + - ``other`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x/(x^2+1)) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/(x+1)) # optional - sage.rings.finite_rings + sage: I + J # optional - sage.rings.finite_rings + Ideal (1/x) of Maximal infinite order of Rational function field + in x over Finite Field of size 2 + """ + return self._ring.ideal([self._gen, other._gen]) + + def _mul_(self, other): + """ + Multiply this ideal with the ``other`` ideal. + + INPUT: + + - ``other`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x/(x^2+1)) # optional - sage.rings.finite_rings + sage: J = Oinf.ideal(1/(x+1)) # optional - sage.rings.finite_rings + sage: I * J # optional - sage.rings.finite_rings + Ideal (1/x^2) of Maximal infinite order of Rational function field + in x over Finite Field of size 2 + """ + return self._ring.ideal([self._gen * other._gen]) + + def _acted_upon_(self, other, on_left): + """ + Multiply this ideal with the ``other`` ideal. + + INPUT: + + - ``other`` -- ideal + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x/(x^2+1)) # optional - sage.rings.finite_rings + sage: x * I # optional - sage.rings.finite_rings + Ideal (1) of Maximal infinite order of Rational function field + in x over Finite Field of size 2 + """ + return self._ring.ideal([other * self._gen]) + + def __invert__(self): + """ + Return the multiplicative inverse of this ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x/(x^2 + 1)) # optional - sage.rings.finite_rings + sage: ~I # indirect doctest # optional - sage.rings.finite_rings + Ideal (x) of Maximal infinite order of Rational function field in x + over Finite Field of size 2 + """ + return self._ring.ideal([~self._gen]) + + def is_prime(self): + """ + Return ``True`` if this ideal is a prime ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x/(x^2 + 1)) # optional - sage.rings.finite_rings + sage: I.is_prime() # optional - sage.rings.finite_rings + True + """ + x = self._ring.fraction_field().gen() + return self._gen == 1/x + + def gen(self): + """ + Return the generator of this principal ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) # optional - sage.rings.finite_rings + sage: I.gen() # optional - sage.rings.finite_rings + 1/x^2 + """ + return self._gen + + def gens(self): + """ + Return the generator of this principal ideal. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) # optional - sage.rings.finite_rings + sage: I.gens() # optional - sage.rings.finite_rings + (1/x^2,) + """ + return (self._gen,) + + def gens_over_base(self): + """ + Return the generator of this ideal as a rank one module + over the infinite maximal order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x+1)/(x^3+x), (x^2+1)/x^4) # optional - sage.rings.finite_rings + sage: I.gens_over_base() # optional - sage.rings.finite_rings + (1/x^2,) + """ + return (self._gen,) + + def valuation(self, ideal): + """ + Return the valuation of ``ideal`` at this prime ideal. + + INPUT: + + - ``ideal`` -- fractional ideal + + EXAMPLES:: + + sage: F. = FunctionField(QQ) + sage: O = F.maximal_order_infinite() + sage: p = O.ideal(1/x) + sage: p.valuation(O.ideal(x/(x+1))) + 0 + sage: p.valuation(O.ideal(0)) + +Infinity + """ + if not self.is_prime(): + raise TypeError("not a prime ideal") + + f = ideal.gen() + if f == 0: + return infinity + 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)) # optional - sage.rings.finite_rings + sage: Oinf = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal((x+1)/(x^3+1)) # optional - sage.rings.finite_rings + sage: I._factor() # optional - sage.rings.finite_rings + [(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)] diff --git a/src/sage/rings/function_field/maps.py b/src/sage/rings/function_field/maps.py index fbf3ef9e15d..ce0a775c31e 100644 --- a/src/sage/rings/function_field/maps.py +++ b/src/sage/rings/function_field/maps.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Morphisms of function fields @@ -10,30 +9,21 @@ sage: K.hom(1/x) Function Field endomorphism of Rational function field in x over Rational Field Defn: x |--> 1/x - sage: L. = K.extension(y^2 - x) - sage: K.hom(y) + sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: K.hom(y) # optional - sage.rings.function_field Function Field morphism: From: Rational function field in x over Rational Field To: Function field in y defined by y^2 - x Defn: x |--> y - sage: L.hom([y,x]) + sage: L.hom([y,x]) # optional - sage.rings.function_field Function Field endomorphism of Function field in y defined by y^2 - x Defn: y |--> y x |--> x - sage: L.hom([x,y]) + sage: L.hom([x,y]) # optional - sage.rings.function_field Traceback (most recent call last): ... ValueError: invalid morphism -For global function fields, which have positive characteristics, the higher -derivation is available:: - - sage: K. = FunctionField(GF(2)); _.=K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: h = L.higher_derivation() - sage: h(y^2, 2) - ((x^7 + 1)/x^2)*y^2 + x^3*y - AUTHORS: - William Stein (2010): initial version @@ -45,8 +35,14 @@ """ # **************************************************************************** -# Copyright (C) 2010 William Stein -# Copyright (C) 2011-2017 Julian Rüth +# Copyright (C) 2010 William Stein +# 2011-2017 Julian Rüth +# 2017 Alyson Deines +# 2017-2019 Kwankyu Lee +# 2018-2019 Travis Scrimshaw +# 2019 Brent Baccala +# 2022 Xavier Caruso +# 2022 Frédéric Chapoton # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -54,1072 +50,18 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from sage.categories.morphism import Morphism, SetMorphism -from sage.categories.map import Map from sage.categories.homset import Hom -from sage.categories.sets_cat import Sets - +from sage.categories.map import Map +from sage.categories.morphism import Morphism, SetMorphism +from sage.misc.lazy_import import lazy_import from sage.rings.infinity import infinity from sage.rings.morphism import RingHomomorphism -from sage.rings.derivation import RingDerivationWithoutTwist - -from sage.modules.free_module_element import vector - -from sage.functions.other import binomial - -from sage.matrix.constructor import matrix - - -class FunctionFieldDerivation(RingDerivationWithoutTwist): - r""" - Base class for derivations on function fields. - - A derivation on `R` is a map `R \to R` with - `D(\alpha+\beta)=D(\alpha)+D(\beta)` and `D(\alpha\beta)=\beta - D(\alpha)+\alpha D(\beta)` for all `\alpha,\beta\in R`. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: d = K.derivation() - sage: d - d/dx - """ - def __init__(self, parent): - r""" - Initialize a derivation. - - INPUT: - - - ``parent`` -- the differential module in which this - derivation lives - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: d = K.derivation() - sage: TestSuite(d).run() - """ - RingDerivationWithoutTwist.__init__(self, parent) - self.__field = parent.domain() - - def is_injective(self) -> bool: - r""" - Return ``False`` since a derivation is never injective. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: d = K.derivation() - sage: d.is_injective() - False - """ - return False - - def _rmul_(self, factor): - """ - Return the product of this derivation by the scalar ``factor``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: d = K.derivation() - sage: d - d/dx - sage: x * d - x*d/dx - """ - return self._lmul_(factor) - - -class FunctionFieldDerivation_rational(FunctionFieldDerivation): - """ - Derivations on rational function fields. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.derivation() - d/dx - """ - def __init__(self, parent, u=None): - """ - Initialize a derivation. - - INPUT: - - - ``parent`` -- the parent of this derivation - - - ``u`` -- a parameter describing the derivation - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: d = K.derivation() - sage: TestSuite(d).run() - - The parameter ``u`` can be the name of the variable:: - - sage: K.derivation(x) - d/dx - - or a list of length one whose unique element is the image - of the generator of the underlying function field:: - - sage: K.derivation([x^2]) - x^2*d/dx - """ - FunctionFieldDerivation.__init__(self, parent) - if u is None or u == parent.domain().gen(): - self._u = parent.codomain().one() - elif u == 0 or isinstance(u, (list, tuple)): - if u == 0 or len(u) == 0: - self._u = parent.codomain().zero() - elif len(u) == 1: - self._u = parent.codomain()(u[0]) - else: - raise ValueError("the length does not match") - else: - raise ValueError("you must pass in either a name of a variable or a list of coefficients") - - def _call_(self, x): - """ - Compute the derivation of ``x``. - - INPUT: - - - ``x`` -- element of the rational function field - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: d = K.derivation() - sage: d(x) # indirect doctest - 1 - sage: d(x^3) - 3*x^2 - sage: d(1/x) - -1/x^2 - """ - f = x.numerator() - g = x.denominator() - numerator = f.derivative() * g - f * g.derivative() - if numerator.is_zero(): - return self.codomain().zero() - else: - v = numerator / g**2 - defining_morphism = self.parent()._defining_morphism - if defining_morphism is not None: - v = defining_morphism(v) - return self._u * v - - def _add_(self, other): - """ - Return the sum of this derivation and ``other``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: d = K.derivation() - sage: d + d - 2*d/dx - """ - return type(self)(self.parent(), [self._u + other._u]) - - def _lmul_(self, factor): - """ - Return the product of this derivation by the scalar ``factor``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: d = K.derivation() - sage: d - d/dx - sage: x * d - x*d/dx - """ - return type(self)(self.parent(), [factor * self._u]) - - -class FunctionFieldDerivation_separable(FunctionFieldDerivation): - """ - Derivations of separable extensions. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: L.derivation() - d/dx - """ - def __init__(self, parent, d): - """ - Initialize a derivation. - - INPUT: - - - ``parent`` -- the parent of this derivation - - - ``d`` -- a variable name or a derivation over - the base field (or something capable to create - such a derivation) - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: d = L.derivation() - sage: TestSuite(d).run() - - sage: L.derivation(y) # d/dy - 2*y*d/dx - - sage: dK = K.derivation([x]); dK - x*d/dx - sage: L.derivation(dK) - x*d/dx - """ - FunctionFieldDerivation.__init__(self, parent) - L = parent.domain() - C = parent.codomain() - dm = parent._defining_morphism - u = L.gen() - if d == L.gen(): - d = parent._base_derivation(None) - f = L.polynomial().change_ring(L) - coeff = -f.derivative()(u) / f.map_coefficients(d)(u) - if dm is not None: - coeff = dm(coeff) - self._d = parent._base_derivation([coeff]) - self._gen_image = C.one() - else: - if isinstance(d, RingDerivationWithoutTwist) and d.domain() is L.base_ring(): - self._d = d - else: - self._d = d = parent._base_derivation(d) - f = L.polynomial() - if dm is None: - denom = f.derivative()(u) - else: - u = dm(u) - denom = f.derivative().map_coefficients(dm, new_base_ring=C)(u) - num = f.map_coefficients(d, new_base_ring=C)(u) - self._gen_image = -num / denom - - def _call_(self, x): - r""" - Evaluate the derivation on ``x``. - - INPUT: - - - ``x`` -- element of the function field - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: d = L.derivation() - sage: d(x) # indirect doctest - 1 - sage: d(y) - 1/2/x*y - sage: d(y^2) - 1 - """ - parent = self.parent() - if x.is_zero(): - return parent.codomain().zero() - x = x._x - y = parent.domain().gen() - dm = parent._defining_morphism - tmp1 = x.map_coefficients(self._d, new_base_ring=parent.codomain()) - tmp2 = x.derivative()(y) - if dm is not None: - tmp2 = dm(tmp2) - y = dm(y) - return tmp1(y) + tmp2 * self._gen_image - - def _add_(self, other): - """ - Return the sum of this derivation and ``other``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: d = L.derivation() - sage: d - d/dx - sage: d + d - 2*d/dx - """ - return type(self)(self.parent(), self._d + other._d) - - def _lmul_(self, factor): - """ - Return the product of this derivation by the scalar ``factor``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: d = L.derivation() - sage: d - d/dx - sage: y * d - y*d/dx - """ - return type(self)(self.parent(), factor * self._d) - - -class FunctionFieldDerivation_inseparable(FunctionFieldDerivation): - def __init__(self, parent, u=None): - r""" - Initialize this derivation. - - INPUT: - - - ``parent`` -- the parent of this derivation - - - ``u`` -- a parameter describing the derivation - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: d = L.derivation() - - This also works for iterated non-monic extensions:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 - 1/x) - sage: R. = L[] - sage: M. = L.extension(z^2*y - x^3) - sage: M.derivation() - d/dz - - We can also create a multiple of the canonical derivation:: - - sage: M.derivation([x]) - x*d/dz - """ - FunctionFieldDerivation.__init__(self, parent) - if u is None: - self._u = parent.codomain().one() - elif u == 0 or isinstance(u, (list, tuple)): - if u == 0 or len(u) == 0: - self._u = parent.codomain().zero() - elif len(u) == 1: - self._u = parent.codomain()(u[0]) - else: - raise ValueError("the length does not match") - else: - raise ValueError("you must pass in either a name of a variable or a list of coefficients") - - def _call_(self, x): - r""" - Evaluate the derivation on ``x``. - - INPUT: - - - ``x`` -- an element of the function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: d = L.derivation() - sage: d(x) # indirect doctest - 0 - sage: d(y) - 1 - sage: d(y^2) - 0 - - """ - if x.is_zero(): - return self.codomain().zero() - parent = self.parent() - return self._u * parent._d(parent._t(x)) - - def _add_(self, other): - """ - Return the sum of this derivation and ``other``. - - EXAMPLES:: - - sage: K. = FunctionField(GF(3)) - sage: R. = K[] - sage: L. = K.extension(y^3 - x) - sage: d = L.derivation() - sage: d - d/dy - sage: d + d - 2*d/dy - """ - return type(self)(self.parent(), [self._u + other._u]) - - def _lmul_(self, factor): - """ - Return the product of this derivation by the scalar ``factor``. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: d = L.derivation() - sage: d - d/dy - sage: y * d - y*d/dy - """ - return type(self)(self.parent(), [factor * self._u]) - - -class FunctionFieldHigherDerivation(Map): - """ - Base class of higher derivations on function fields. - - INPUT: - - ``field`` -- function field on which the derivation operates - - EXAMPLES:: - - sage: F. = FunctionField(GF(2)) - sage: F.higher_derivation() - Higher derivation map: - From: Rational function field in x over Finite Field of size 2 - To: Rational function field in x over Finite Field of size 2 - """ - def __init__(self, field): - """ - Initialize. - - TESTS:: - - sage: F. = FunctionField(GF(4)) - sage: h = F.higher_derivation() - sage: TestSuite(h).run(skip='_test_category') - """ - Map.__init__(self, Hom(field, field, Sets())) - self._field = field - # elements of a prime finite field do not have pth_root method - if field.constant_base_field().is_prime_field(): - self._pth_root_func = _pth_root_in_prime_field - else: - self._pth_root_func = _pth_root_in_finite_field - - def _repr_type(self) -> str: - """ - Return a string containing the type of the map. - - EXAMPLES:: - - sage: F. = FunctionField(GF(2)) - sage: h = F.higher_derivation() - sage: h # indirect doctest - Higher derivation map: - From: Rational function field in x over Finite Field of size 2 - To: Rational function field in x over Finite Field of size 2 - """ - return 'Higher derivation' - - def __eq__(self, other) -> bool: - """ - Test if ``self`` equals ``other``. - - TESTS:: - - sage: F. = FunctionField(GF(2)) - sage: h = F.higher_derivation() - sage: loads(dumps(h)) == h - True - """ - if isinstance(other, FunctionFieldHigherDerivation): - return self._field == other._field - return False - - -def _pth_root_in_prime_field(e): - """ - Return the `p`-th root of element ``e`` in a prime finite field. - - TESTS:: - - sage: from sage.rings.function_field.maps import _pth_root_in_prime_field - sage: p = 5 - sage: F. = GF(p) - sage: e = F.random_element() - sage: _pth_root_in_prime_field(e)^p == e - True - """ - return e - - -def _pth_root_in_finite_field(e): - """ - Return the `p`-th root of element ``e`` in a finite field. - - TESTS:: - - sage: from sage.rings.function_field.maps import _pth_root_in_finite_field - sage: p = 3 - sage: F. = GF(p^2) - sage: e = F.random_element() - sage: _pth_root_in_finite_field(e)^p == e - True - """ - return e.pth_root() - - -class RationalFunctionFieldHigherDerivation_global(FunctionFieldHigherDerivation): - """ - Higher derivations of rational function fields over finite fields. - - INPUT: - - - ``field`` -- function field on which the derivation operates - - EXAMPLES:: - - sage: F. = FunctionField(GF(2)) - sage: h = F.higher_derivation() - sage: h - Higher derivation map: - From: Rational function field in x over Finite Field of size 2 - To: Rational function field in x over Finite Field of size 2 - sage: h(x^2,2) - 1 - """ - def __init__(self, field): - """ - Initialize. - - TESTS:: - - sage: F. = FunctionField(GF(2)) - sage: h = F.higher_derivation() - sage: TestSuite(h).run(skip='_test_category') - """ - FunctionFieldHigherDerivation.__init__(self, field) - - self._p = field.characteristic() - self._separating_element = field.gen() - - def _call_with_args(self, f, args=(), kwds={}): - """ - Call the derivation with args and kwds. - - EXAMPLES:: - - sage: F. = FunctionField(GF(2)) - sage: h = F.higher_derivation() - sage: h(x^2,2) # indirect doctest - 1 - """ - return self._derive(f, *args, **kwds) - - def _derive(self, f, i, separating_element=None): - """ - Return the `i`-th derivative of ``f`` with respect to the - separating element. - - This implements Hess' Algorithm 26 in [Hes2002b]_. - - EXAMPLES:: - - sage: F. = FunctionField(GF(2)) - sage: h = F.higher_derivation() - sage: h._derive(x^3,0) - x^3 - sage: h._derive(x^3,1) - x^2 - sage: h._derive(x^3,2) - x - sage: h._derive(x^3,3) - 1 - sage: h._derive(x^3,4) - 0 - """ - F = self._field - p = self._p - - if separating_element is None: - x = self._separating_element - - def derivative(f): - return f.derivative() - else: - x = separating_element - xderinv = ~(x.derivative()) - - def derivative(f): - return xderinv * f.derivative() - - prime_power_representation = self._prime_power_representation - - def derive(f, i): - # Step 1: zero-th derivative - if i == 0: - return f - # Step 2: - s = i % p - r = i // p - # Step 3: - e = f - while s > 0: - e = derivative(e) / F(s) - s -= 1 - # Step 4: - if r == 0: - return e - else: - # Step 5: - lambdas = prime_power_representation(e, x) - # Step 6 and 7: - der = 0 - for i in range(p): - mu = derive(lambdas[i], r) - der += mu**p * x**i - return der - - return derive(f, i) - - def _prime_power_representation(self, f, separating_element=None): - """ - Return `p`-th power representation of the element ``f``. - - Here `p` is the characteristic of the function field. - - This implements Hess' Algorithm 25. - - EXAMPLES:: - - sage: F. = FunctionField(GF(2)) - sage: h = F.higher_derivation() - sage: h._prime_power_representation(x^2 + x + 1) - [x + 1, 1] - sage: x^2 + x + 1 == _[0]^2 + _[1]^2 * x - True - """ - F = self._field - p = self._p - - if separating_element is None: - x = self._separating_element - - def derivative(f): - return f.derivative() - else: - x = separating_element - xderinv = ~(x.derivative()) - - def derivative(f): - return xderinv * f.derivative() - - # Step 1: - a = [f] - aprev = f - j = 1 - while j < p: - aprev = derivative(aprev) / F(j) - a.append(aprev) - j += 1 - # Step 2: - b = a - j = p - 2 - while j >= 0: - b[j] -= sum(binomial(i, j) * b[i] * x**(i - j) - for i in range(j + 1, p)) - j -= 1 - # Step 3 - return [self._pth_root(c) for c in b] - - def _pth_root(self, c): - """ - Return the `p`-th root of the rational function ``c``. - - INPUT: - - - ``c`` -- rational function - - EXAMPLES:: - - sage: F. = FunctionField(GF(2)) - sage: h = F.higher_derivation() - sage: h._pth_root((x^2+1)^2) - x^2 + 1 - """ - K = self._field - p = self._p - - R = K._field.ring() - - poly = c.numerator() - num = R([self._pth_root_func(poly[i]) - for i in range(0, poly.degree() + 1, p)]) - poly = c.denominator() - den = R([self._pth_root_func(poly[i]) - for i in range(0, poly.degree() + 1, p)]) - return K.element_class(K, num / den) - -class FunctionFieldHigherDerivation_global(FunctionFieldHigherDerivation): - """ - Higher derivations of global function fields. - - INPUT: - - - ``field`` -- function field on which the derivation operates - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: h - Higher derivation map: - From: Function field in y defined by y^3 + x^3*y + x - To: Function field in y defined by y^3 + x^3*y + x - sage: h(y^2, 2) - ((x^7 + 1)/x^2)*y^2 + x^3*y - """ - - def __init__(self, field): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: TestSuite(h).run(skip=['_test_category']) - """ - FunctionFieldHigherDerivation.__init__(self, field) - - self._p = field.characteristic() - self._separating_element = field(field.base_field().gen()) - - p = field.characteristic() - y = field.gen() - - # matrix for pth power map; used in _prime_power_representation method - self.__pth_root_matrix = matrix([(y**(i * p)).list() - for i in range(field.degree())]).transpose() - - # cache computed higher derivatives to speed up later computations - self._cache = {} - - def _call_with_args(self, f, args, kwds): - """ - Call the derivation with ``args`` and ``kwds``. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: h(y^2,2) # indirect doctest - ((x^7 + 1)/x^2)*y^2 + x^3*y - """ - return self._derive(f, *args, **kwds) - - def _derive(self, f, i, separating_element=None): - """ - Return ``i``-th derivative of ``f`` with respect to the separating - element. - - This implements Hess' Algorithm 26 in [Hes2002b]_. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: y^3 - x^3*y + x - sage: h._derive(y^3,0) - x^3*y + x - sage: h._derive(y^3,1) - x^4*y^2 + 1 - sage: h._derive(y^3,2) - x^10*y^2 + (x^8 + x)*y - sage: h._derive(y^3,3) - (x^9 + x^2)*y^2 + x^7*y - sage: h._derive(y^3,4) - (x^22 + x)*y^2 + ((x^21 + x^14 + x^7 + 1)/x)*y - """ - F = self._field - p = self._p - frob = F.frobenius_endomorphism() # p-th power map - - if separating_element is None: - x = self._separating_element - - def derivative(f): - return f.derivative() - else: - x = separating_element - xderinv = ~(x.derivative()) - - def derivative(f): - return xderinv * f.derivative() - - try: - cache = self._cache[separating_element] - except KeyError: - cache = self._cache[separating_element] = {} - - def derive(f, i): - # Step 1: zero-th derivative - if i == 0: - return f - - # Step 1.5: use cached result if available - try: - return cache[f, i] - except KeyError: - pass - - # Step 2: - s = i % p - r = i // p - # Step 3: - e = f - while s > 0: - e = derivative(e) / F(s) - s -= 1 - # Step 4: - if r == 0: - der = e - else: - # Step 5: inlined self._prime_power_representation - a = [e] - aprev = e - j = 1 - while j < p: - aprev = derivative(aprev) / F(j) - a.append(aprev) - j += 1 - b = a - j = p - 2 - while j >= 0: - b[j] -= sum(binomial(k, j) * b[k] * x**(k - j) - for k in range(j + 1, p)) - j -= 1 - lambdas = [self._pth_root(c) for c in b] - - # Step 6 and 7: - der = 0 - xpow = 1 - for k in range(p): - mu = derive(lambdas[k], r) - der += frob(mu) * xpow - xpow *= x - - cache[f, i] = der - return der - - return derive(f, i) - - def _prime_power_representation(self, f, separating_element=None): - """ - Return `p`-th power representation of the element ``f``. - - Here `p` is the characteristic of the function field. - - This implements Hess' Algorithm 25 in [Hes2002b]_. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: b = h._prime_power_representation(y) - sage: y == b[0]^2 + b[1]^2 * x - True - """ - F = self._field - p = self._p - - if separating_element is None: - x = self._separating_element - - def derivative(f): - return f.derivative() - else: - x = separating_element - xderinv = ~(x.derivative()) - - def derivative(f): - return xderinv * f.derivative() - - # Step 1: - a = [f] - aprev = f - j = 1 - while j < p: - aprev = derivative(aprev) / F(j) - a.append(aprev) - j += 1 - # Step 2: - b = a - j = p - 2 - while j >= 0: - b[j] -= sum(binomial(i, j) * b[i] * x**(i - j) - for i in range(j + 1, p)) - j -= 1 - # Step 3 - return [self._pth_root(c) for c in b] - - def _pth_root(self, c): - """ - Return the `p`-th root of function field element ``c``. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: h._pth_root((x^2 + y^2)^2) - y^2 + x^2 - """ - K = self._field.base_field() # rational function field - p = self._p - - coeffs = [] - for d in self.__pth_root_matrix.solve_right(vector(c.list())): - poly = d.numerator() - num = K([self._pth_root_func(poly[i]) - for i in range(0, poly.degree() + 1, p)]) - poly = d.denominator() - den = K([self._pth_root_func(poly[i]) - for i in range(0, poly.degree() + 1, p)]) - coeffs.append(num / den) - return self._field(coeffs) - - -class FunctionFieldHigherDerivation_char_zero(FunctionFieldHigherDerivation): - """ - Higher derivations of function fields of characteristic zero. - - INPUT: - - - ``field`` -- function field on which the derivation operates - - EXAMPLES:: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: h - Higher derivation map: - From: Function field in y defined by y^3 + x^3*y + x - To: Function field in y defined by y^3 + x^3*y + x - sage: h(y,1) == -(3*x^2*y+1)/(3*y^2+x^3) - True - sage: h(y^2,1) == -2*y*(3*x^2*y+1)/(3*y^2+x^3) - True - sage: e = L.random_element() - sage: h(h(e,1),1) == 2*h(e,2) - True - sage: h(h(h(e,1),1),1) == 3*2*h(e,3) - True - """ - def __init__(self, field): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: TestSuite(h).run(skip=['_test_category']) - """ - FunctionFieldHigherDerivation.__init__(self, field) - - self._separating_element = field(field.base_field().gen()) - - # cache computed higher derivatives to speed up later computations - self._cache = {} - - def _call_with_args(self, f, args, kwds): - """ - Call the derivation with ``args`` and ``kwds``. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: e = L.random_element() - sage: h(h(e,1),1) == 2*h(e,2) # indirect doctest - True - """ - return self._derive(f, *args, **kwds) - - def _derive(self, f, i, separating_element=None): - """ - Return ``i``-th derivative of ``f`` with respect to the separating - element. - - EXAMPLES:: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: h = L.higher_derivation() - sage: y^3 - -x^3*y - x - sage: h._derive(y^3,0) - -x^3*y - x - sage: h._derive(y^3,1) - (-21/4*x^4/(x^7 + 27/4))*y^2 + ((-9/2*x^9 - 45/2*x^2)/(x^7 + 27/4))*y + (-9/2*x^7 - 27/4)/(x^7 + 27/4) - """ - F = self._field - - if separating_element is None: - x = self._separating_element - xderinv = 1 - else: - x = separating_element - xderinv = ~(x.derivative()) - - try: - cache = self._cache[separating_element] - except KeyError: - cache = self._cache[separating_element] = {} - - if i == 0: - return f - - try: - return cache[f, i] - except KeyError: - pass - - s = i - e = f - while s > 0: - e = xderinv * e.derivative() / F(s) - s -= 1 - - der = e - - cache[f, i] = der - return der +lazy_import("sage.rings.function_field.derivations", ( + "FunctionFieldDerivation", + "FunctionFieldHigherDerivation", +), deprecation=35230) class FunctionFieldVectorSpaceIsomorphism(Morphism): @@ -1129,9 +71,9 @@ class FunctionFieldVectorSpaceIsomorphism(Morphism): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: isinstance(f, sage.rings.function_field.maps.FunctionFieldVectorSpaceIsomorphism) + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: isinstance(f, sage.rings.function_field.maps.FunctionFieldVectorSpaceIsomorphism) # optional - sage.modules sage.rings.function_field True """ def _repr_(self) -> str: @@ -1141,13 +83,13 @@ def _repr_(self) -> str: EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: f + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: f # optional - sage.modules sage.rings.function_field Isomorphism: From: Vector space of dimension 2 over Rational function field in x over Rational Field To: Function field in y defined by y^2 - x*y + 4*x^3 - sage: t + sage: t # optional - sage.modules sage.rings.function_field Isomorphism: From: Function field in y defined by y^2 - x*y + 4*x^3 To: Vector space of dimension 2 over Rational function field in x over Rational Field @@ -1164,9 +106,9 @@ def is_injective(self) -> bool: EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: f.is_injective() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: f.is_injective() # optional - sage.modules sage.rings.function_field True """ return True @@ -1178,9 +120,9 @@ def is_surjective(self) -> bool: EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: f.is_surjective() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: f.is_surjective() # optional - sage.modules sage.rings.function_field True """ return True @@ -1201,11 +143,11 @@ def _richcmp_(self, other, op): sage: L = K.function_field() sage: f = K.coerce_map_from(L) - sage: K = QQbar['x'].fraction_field() - sage: L = K.function_field() - sage: g = K.coerce_map_from(L) + sage: K = QQbar['x'].fraction_field() # optional - sage.rings.number_field + sage: L = K.function_field() # optional - sage.rings.number_field + sage: g = K.coerce_map_from(L) # optional - sage.rings.number_field - sage: f == g + sage: f == g # optional - sage.rings.number_field False sage: f == f True @@ -1244,8 +186,8 @@ class MapVectorSpaceToFunctionField(FunctionFieldVectorSpaceIsomorphism): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space(); f + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space(); f # optional - sage.modules sage.rings.function_field Isomorphism: From: Vector space of dimension 2 over Rational function field in x over Rational Field To: Function field in y defined by y^2 - x*y + 4*x^3 @@ -1255,8 +197,8 @@ def __init__(self, V, K): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space(); type(f) + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space(); type(f) # optional - sage.modules sage.rings.function_field """ self._V = V @@ -1276,18 +218,18 @@ def _call_(self, v): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: f(x*V.0 + (1/x^3)*V.1) # indirect doctest + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: f(x*V.0 + (1/x^3)*V.1) # indirect doctest # optional - sage.modules sage.rings.function_field 1/x^3*y + x TESTS: Test that this map is a bijection for some random inputs:: - sage: R. = L[] - sage: M. = L.extension(z^3 - y - x) - sage: for F in [K,L,M]: + sage: R. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z^3 - y - x) # optional - sage.rings.function_field + sage: for F in [K, L, M]: # optional - sage.modules sage.rings.function_field ....: for base in F._intermediate_fields(K): ....: V, f, t = F.vector_space(base) ....: for i in range(100): @@ -1319,9 +261,9 @@ def domain(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: f.domain() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: f.domain() # optional - sage.modules sage.rings.function_field Vector space of dimension 2 over Rational function field in x over Rational Field """ return self._V @@ -1333,9 +275,9 @@ def codomain(self): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: f.codomain() + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: f.codomain() # optional - sage.modules sage.rings.function_field Function field in y defined by y^2 - x*y + 4*x^3 """ return self._K @@ -1348,8 +290,8 @@ class MapFunctionFieldToVectorSpace(FunctionFieldVectorSpaceIsomorphism): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space(); t + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space(); t # optional - sage.modules sage.rings.function_field Isomorphism: From: Function field in y defined by y^2 - x*y + 4*x^3 To: Vector space of dimension 2 over Rational function field in x over Rational Field @@ -1367,9 +309,9 @@ def __init__(self, K, V): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: TestSuite(t).run(skip="_test_category") + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: TestSuite(t).run(skip="_test_category") # optional - sage.modules sage.rings.function_field """ self._V = V self._K = K @@ -1384,18 +326,18 @@ def _call_(self, x): EXAMPLES:: sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x*y + 4*x^3) - sage: V, f, t = L.vector_space() - sage: t(x + (1/x^3)*y) # indirect doctest + sage: L. = K.extension(y^2 - x*y + 4*x^3) # optional - sage.rings.function_field + sage: V, f, t = L.vector_space() # optional - sage.modules sage.rings.function_field + sage: t(x + (1/x^3)*y) # indirect doctest # optional - sage.modules sage.rings.function_field (x, 1/x^3) TESTS: Test that this map is a bijection for some random inputs:: - sage: R. = L[] - sage: M. = L.extension(z^3 - y - x) - sage: for F in [K,L,M]: + sage: R. = L[] # optional - sage.rings.function_field + sage: M. = L.extension(z^3 - y - x) # optional - sage.rings.function_field + sage: for F in [K, L, M]: # optional - sage.modules sage.rings.function_field ....: for base in F._intermediate_fields(K): ....: V, f, t = F.vector_space(base) ....: for i in range(100): @@ -1448,10 +390,10 @@ def _repr_type(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^3 + 6*x^3 + x) - sage: f = L.hom(y*2) - sage: f._repr_type() + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = L.hom(y*2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f._repr_type() # optional - sage.rings.finite_rings sage.rings.function_field 'Function Field' """ return "Function Field" @@ -1462,10 +404,10 @@ def _repr_defn(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^3 + 6*x^3 + x) - sage: f = L.hom(y*2) - sage: f._repr_defn() + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = L.hom(y*2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f._repr_defn() # optional - sage.rings.finite_rings sage.rings.function_field 'y |--> 2*y' """ a = '%s |--> %s' % (self.domain().variable_name(), self._im_gen) @@ -1480,14 +422,14 @@ class FunctionFieldMorphism_polymod(FunctionFieldMorphism): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^3 + 6*x^3 + x) - sage: f = L.hom(y*2); f + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = L.hom(y*2); f # optional - sage.rings.finite_rings sage.rings.function_field Function Field endomorphism of Function field in y defined by y^3 + 6*x^3 + x Defn: y |--> 2*y - sage: factor(L.polynomial()) + sage: factor(L.polynomial()) # optional - sage.rings.finite_rings sage.rings.function_field y^3 + 6*x^3 + x - sage: f(y).charpoly('y') + sage: f(y).charpoly('y') # optional - sage.rings.finite_rings sage.rings.function_field y^3 + 6*x^3 + x """ def __init__(self, parent, im_gen, base_morphism): @@ -1496,10 +438,10 @@ def __init__(self, parent, im_gen, base_morphism): EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^3 + 6*x^3 + x) - sage: f = L.hom(y*2) - sage: TestSuite(f).run(skip="_test_category") + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 + 6*x^3 + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = L.hom(y*2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(f).run(skip="_test_category") # optional - sage.rings.finite_rings sage.rings.function_field """ FunctionFieldMorphism.__init__(self, parent, im_gen, base_morphism) # Verify that the morphism is valid: @@ -1515,11 +457,11 @@ def _call_(self, x): """ EXAMPLES:: - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^3 + 6*x^3 + x); f = L.hom(y*2) - sage: f(y/x + x^2/(x+1)) # indirect doctest + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 + 6*x^3 + x); f = L.hom(y*2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: f(y/x + x^2/(x+1)) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field 2/x*y + x^2/(x + 1) - sage: f(y) + sage: f(y) # optional - sage.rings.finite_rings sage.rings.function_field 2*y """ v = x.list() @@ -1539,8 +481,8 @@ def __init__(self, parent, im_gen, base_morphism): EXAMPLES:: - sage: K. = FunctionField(GF(7)) - sage: f = K.hom(1/x); f + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: f = K.hom(1/x); f # optional - sage.rings.finite_rings Function Field endomorphism of Rational function field in x over Finite Field of size 7 Defn: x |--> 1/x """ @@ -1550,27 +492,27 @@ def _call_(self, x): """ EXAMPLES:: - sage: K. = FunctionField(GF(7)) - sage: f = K.hom(1/x); f + sage: K. = FunctionField(GF(7)) # optional - sage.rings.finite_rings + sage: f = K.hom(1/x); f # optional - sage.rings.finite_rings Function Field endomorphism of Rational function field in x over Finite Field of size 7 Defn: x |--> 1/x - sage: f(x+1) # indirect doctest + sage: f(x+1) # indirect doctest # optional - sage.rings.finite_rings (x + 1)/x - sage: 1/x + 1 + sage: 1/x + 1 # optional - sage.rings.finite_rings (x + 1)/x You can specify a morphism on the base ring:: - sage: Qi = GaussianIntegers().fraction_field() - sage: i = Qi.gen() - sage: K. = FunctionField(Qi) - sage: phi1 = Qi.hom([CC.gen()]) - sage: phi2 = Qi.hom([-CC.gen()]) - sage: f = K.hom(CC.pi(),phi1) - sage: f(1+i+x) + sage: Qi = GaussianIntegers().fraction_field() # optional - sage.rings.number_field + sage: i = Qi.gen() # optional - sage.rings.number_field + sage: K. = FunctionField(Qi) # optional - sage.rings.number_field + sage: phi1 = Qi.hom([CC.gen()]) # optional - sage.rings.number_field + sage: phi2 = Qi.hom([-CC.gen()]) # optional - sage.rings.number_field + sage: f = K.hom(CC.pi(), phi1) # optional - sage.rings.number_field + sage: f(1 + i + x) # optional - sage.rings.number_field 4.14159265358979 + 1.00000000000000*I - sage: g = K.hom(CC.pi(),phi2) - sage: g(1+i+x) + sage: g = K.hom(CC.pi(), phi2) # optional - sage.rings.number_field + sage: g(1 + i + x) # optional - sage.rings.number_field 4.14159265358979 - 1.00000000000000*I """ a = x.element() @@ -1774,35 +716,35 @@ class FunctionFieldCompletion(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: m = L.completion(p) - sage: m + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m # optional - sage.rings.finite_rings sage.rings.function_field Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 - sage: m(x) + sage: m(x) # optional - sage.rings.finite_rings sage.rings.function_field s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + s^9 + s^10 + s^12 + s^13 + s^15 + s^16 + s^17 + s^19 + O(s^22) - sage: m(y) + sage: m(y) # optional - sage.rings.finite_rings sage.rings.function_field s^-1 + 1 + s^3 + s^5 + s^7 + s^9 + s^13 + s^15 + s^17 + O(s^19) - sage: m(x*y) == m(x) * m(y) + sage: m(x*y) == m(x) * m(y) # optional - sage.rings.finite_rings sage.rings.function_field True - sage: m(x+y) == m(x) + m(y) + sage: m(x+y) == m(x) + m(y) # optional - sage.rings.finite_rings sage.rings.function_field True The variable name of the series can be supplied. If the place is not rational such that the residue field is a proper extension of the constant field, you can also specify the generator name of the extension:: - sage: p2 = L.places_finite(2)[0] - sage: p2 + sage: p2 = L.places_finite(2)[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p2 # optional - sage.rings.finite_rings sage.rings.function_field Place (x^2 + x + 1, x*y + 1) - sage: m2 = L.completion(p2, 't', gen_name='b') - sage: m2(x) + sage: m2 = L.completion(p2, 't', gen_name='b') # optional - sage.rings.finite_rings sage.rings.function_field + sage: m2(x) # optional - sage.rings.finite_rings sage.rings.function_field (b + 1) + t + t^2 + t^4 + t^8 + t^16 + O(t^20) - sage: m2(y) + sage: m2(y) # optional - sage.rings.finite_rings sage.rings.function_field b + b*t + b*t^3 + b*t^4 + (b + 1)*t^5 + (b + 1)*t^7 + b*t^9 + b*t^11 + b*t^12 + b*t^13 + b*t^15 + b*t^16 + (b + 1)*t^17 + (b + 1)*t^19 + O(t^20) """ @@ -1812,11 +754,11 @@ def __init__(self, field, place, name=None, prec=None, gen_name=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p) - sage: m + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m # optional - sage.rings.finite_rings sage.rings.function_field Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 @@ -1850,11 +792,11 @@ def _repr_type(self) -> str: EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p) - sage: m # indirect doctest + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field Completion map: From: Function field in y defined by y^2 + y + (x^2 + 1)/x To: Laurent Series Ring in s over Finite Field of size 2 @@ -1867,11 +809,11 @@ def _call_(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p) - sage: m(y) + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(y) # optional - sage.rings.finite_rings sage.rings.function_field s^-1 + 1 + s^3 + s^5 + s^7 + s^9 + s^13 + s^15 + s^17 + O(s^19) """ if self._precision == infinity: @@ -1885,11 +827,11 @@ def _call_with_args(self, f, args, kwds): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p) - sage: m(x+y, 10) # indirect doctest + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(x+y, 10) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field s^-1 + 1 + s^2 + s^4 + s^8 + O(s^9) """ if self._precision == infinity: @@ -1909,11 +851,11 @@ def _expand(self, f, prec=None): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p) - sage: m(x, prec=20) # indirect doctest + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m(x, prec=20) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + s^9 + s^10 + s^12 + s^13 + s^15 + s^16 + s^17 + s^19 + O(s^22) """ @@ -1943,15 +885,15 @@ def _expand_lazy(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p, prec=infinity) - sage: e = m(x); e + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p, prec=infinity) # optional - sage.rings.finite_rings sage.rings.function_field + sage: e = m(x); e # optional - sage.rings.finite_rings sage.rings.function_field s^2 + s^3 + s^4 + s^5 + s^7 + s^8 + ... - sage: e.coefficient(99) # indirect doctest + sage: e.coefficient(99) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field 0 - sage: e.coefficient(100) + sage: e.coefficient(100) # optional - sage.rings.finite_rings sage.rings.function_field 1 """ place = self._place @@ -1975,11 +917,11 @@ def default_precision(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: p = L.places_finite()[0] - sage: m = L.completion(p) - sage: m.default_precision() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: m = L.completion(p) # optional - sage.rings.finite_rings sage.rings.function_field + sage: m.default_precision() # optional - sage.rings.finite_rings sage.rings.function_field 20 """ return self._precision @@ -1995,14 +937,14 @@ def _repr_(self) -> str: 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 + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: R = p.valuation_ring() # optional - sage.rings.finite_rings sage.rings.function_field + sage: k, fr_k, to_k = R.residue_field() # optional - sage.rings.finite_rings sage.rings.function_field + sage: k # optional - sage.rings.finite_rings sage.rings.function_field Finite Field of size 2 - sage: fr_k + sage: fr_k # optional - sage.rings.finite_rings sage.rings.function_field Ring morphism: From: Finite Field of size 2 To: Valuation ring at Place (x, x*y) @@ -2023,13 +965,13 @@ def _repr_(self) -> str: 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 + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^2-x^3-1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(x - 2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: D = I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: V, from_V, to_V = D.function_space() # optional - sage.rings.finite_rings sage.rings.function_field + sage: from_V # optional - sage.rings.finite_rings sage.rings.function_field 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 @@ -2050,13 +992,13 @@ def _repr_(self) -> str: 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 + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(x - 2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: D = I.divisor() # optional - sage.rings.finite_rings sage.rings.function_field + sage: V, from_V, to_V = D.function_space() # optional - sage.rings.finite_rings sage.rings.function_field + sage: to_V # optional - sage.rings.finite_rings sage.rings.function_field 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 diff --git a/src/sage/rings/function_field/order.py b/src/sage/rings/function_field/order.py index d27d3c3b452..a2e85f21fc8 100644 --- a/src/sage/rings/function_field/order.py +++ b/src/sage/rings/function_field/order.py @@ -30,49 +30,49 @@ `O` and one maximal infinite order `O_\infty`. There are other non-maximal orders such as equation orders:: - sage: K. = FunctionField(GF(3)); R. = K[] - sage: L. = K.extension(y^3-y-x) - sage: O = L.equation_order() - sage: 1/y in O + sage: K. = FunctionField(GF(3)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^3 - y - x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: 1/y in O # optional - sage.rings.finite_rings sage.rings.function_field False - sage: x/y in O + sage: x/y in O # optional - sage.rings.finite_rings sage.rings.function_field True Sage provides an extensive functionality for computations in maximal orders of function fields. For example, you can decompose a prime ideal of a rational function field in an extension:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: o = K.maximal_order() - sage: p = o.ideal(x+1) - sage: p.is_prime() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: o = K.maximal_order() # optional - sage.rings.finite_rings + sage: p = o.ideal(x + 1) # optional - sage.rings.finite_rings + sage: p.is_prime() # optional - sage.rings.finite_rings True - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: O = F.maximal_order() - sage: O.decomposition(p) + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.decomposition(p) # optional - sage.rings.finite_rings sage.rings.function_field [(Ideal (x + 1, y + 1) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), (Ideal (x + 1, (1/(x^3 + x^2 + x))*y^2 + y + 1) of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2, 2, 1)] - sage: p1,relative_degree,ramification_index = O.decomposition(p)[1] - sage: p1.parent() + sage: p1, relative_degree,ramification_index = O.decomposition(p)[1] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1.parent() # optional - sage.rings.finite_rings sage.rings.function_field Monoid of ideals of Maximal order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - sage: relative_degree + sage: relative_degree # optional - sage.rings.finite_rings sage.rings.function_field 2 - sage: ramification_index + sage: ramification_index # optional - sage.rings.finite_rings sage.rings.function_field 1 When the base constant field is the algebraic field `\QQbar`, the only prime ideals of the maximal order of the rational function field are linear polynomials. :: - sage: K. = FunctionField(QQbar) - sage: R. = K[] - sage: L. = K.extension(y^2 - (x^3-x^2)) - sage: p = K.maximal_order().ideal(x) - sage: L.maximal_order().decomposition(p) + sage: K. = FunctionField(QQbar) # optional - sage.rings.number_field + sage: R. = K[] # optional - sage.rings.number_field + sage: L. = K.extension(y^2 - (x^3-x^2)) # optional - sage.rings.function_field sage.rings.number_field + sage: p = K.maximal_order().ideal(x) # optional - sage.rings.function_field sage.rings.number_field + sage: L.maximal_order().decomposition(p) # optional - sage.rings.function_field sage.rings.number_field [(Ideal (1/x*y - I) of Maximal order of Function field in y defined by y^2 - x^3 + x^2, 1, 1), @@ -94,10 +94,13 @@ - Brent Baccala (2019-12-20): support orders in characteristic zero """ + #***************************************************************************** -# Copyright (C) 2010 William Stein -# Copyright (C) 2011 Maarten Derickx -# Copyright (C) 2011 Julian Rueth +# Copyright (C) 2010 William Stein +# 2011 Maarten Derickx +# 2011 Julian Rueth +# 2017-2020 Kwankyu Lee +# 2019 Brent Baccala # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -105,40 +108,11 @@ # https://www.gnu.org/licenses/ #***************************************************************************** -from sage.misc.cachefunc import cached_method - -from sage.modules.free_module_element import vector -from sage.arith.functions import lcm -from sage.arith.misc import GCD as gcd - -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.algebras.all import FiniteDimensionalAlgebra - -from sage.rings.qqbar import QQbar -from sage.rings.number_field.number_field_base import NumberField - +from sage.categories.integral_domains import IntegralDomains from sage.structure.parent import Parent from sage.structure.unique_representation import CachedRepresentation, UniqueRepresentation -from sage.categories.integral_domains import IntegralDomains -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, - FunctionFieldIdeal_module, - FunctionFieldIdeal_rational, - FunctionFieldIdeal_polymod, - FunctionFieldIdeal_global, - FunctionFieldIdealInfinite_module, - FunctionFieldIdealInfinite_rational, - FunctionFieldIdealInfinite_polymod) - -from .hermite_form_polynomial import reversed_hermite_form +from .ideal import IdealMonoid, FunctionFieldIdeal class FunctionFieldOrder_base(CachedRepresentation, Parent): @@ -254,287 +228,6 @@ def _repr_(self): return "Order in {}".format(self._field) -class FunctionFieldOrder_basis(FunctionFieldOrder): - """ - Order given by a basis over the maximal order of the base field. - - INPUT: - - - ``basis`` -- list of elements of the function field - - - ``check`` -- (default: ``True``) if ``True``, check whether the module - that ``basis`` generates forms an order - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order(); O - Order in Function field in y defined by y^4 + x*y + 4*x + 1 - - The basis only defines an order if the module it generates is closed under - multiplication and contains the identity element:: - - sage: K. = FunctionField(QQ) - sage: R. = K[] - sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) - sage: y.is_integral() - False - sage: L.order(y) - Traceback (most recent call last): - ... - ValueError: the module generated by basis (1, y, y^2, y^3, y^4) must be closed under multiplication - - The basis also has to be linearly independent and of the same rank as the - degree of the function field of its elements (only checked when ``check`` - is ``True``):: - - sage: L.order(L(x)) - Traceback (most recent call last): - ... - ValueError: basis (1, x, x^2, x^3, x^4) is not linearly independent - sage: sage.rings.function_field.order.FunctionFieldOrder_basis((y,y,y^3,y^4,y^5)) - Traceback (most recent call last): - ... - ValueError: basis (y, y, y^3, y^4, 2*x*y + (x^4 + 1)/x) is not linearly independent - """ - def __init__(self, basis, check=True): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: TestSuite(O).run() - """ - if len(basis) == 0: - raise ValueError("basis must have positive length") - - field = basis[0].parent() - if len(basis) != field.degree(): - raise ValueError("length of basis must equal degree of field") - - FunctionFieldOrder.__init__(self, field, ideal_class=FunctionFieldIdeal_module) - - V, from_V, to_V = field.vector_space() - - R = V.base_field().maximal_order() - self._module = V.span([to_V(b) for b in basis], base_ring=R) - - self._from_module= from_V - self._to_module = to_V - self._basis = tuple(basis) - self._ring = field.polynomial_ring() - self._populate_coercion_lists_(coerce_list=[self._ring]) - - if check: - if self._module.rank() != field.degree(): - raise ValueError("basis {} is not linearly independent".format(basis)) - if not to_V(field(1)) in self._module: - raise ValueError("the identity element must be in the module spanned by basis {}".format(basis)) - if not all(to_V(a*b) in self._module for a in basis for b in basis): - raise ValueError("the module generated by basis {} must be closed under multiplication".format(basis)) - - def _element_constructor_(self, f): - """ - Construct an element of this order from ``f``. - - INPUT: - - - ``f`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: K.maximal_order()._element_constructor_(x) - x - """ - F = self.function_field() - - try: - f = F(f) - except TypeError: - raise TypeError("unable to convert to an element of {}".format(F)) - - V, fr_V, to_V = F.vector_space() - f_vector = to_V(f) - if f_vector not in self._module: - raise TypeError("{} is not an element of {}".format(f_vector, self)) - - return f - - def ideal_with_gens_over_base(self, gens): - """ - Return the fractional ideal with basis ``gens`` over the - maximal order of the base field. - - It is not checked that the ``gens`` really generates an ideal. - - INPUT: - - - ``gens`` -- list of elements of the function field - - EXAMPLES: - - We construct an ideal in a rational function field:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal([y]); I - Ideal (y) of Maximal order of Rational function field in y over Rational Field - sage: I*I - Ideal (y^2) of Maximal order of Rational function field in y over Rational Field - - We construct some ideals in a nontrivial function field:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order(); O - Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I = O.ideal_with_gens_over_base([1, y]); I - Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I.module() - Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 - Echelon basis matrix: - [1 0] - [0 1] - - There is no check if the resulting object is really an ideal:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal_with_gens_over_base([y]); I - Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: y in I - True - sage: y^2 in I - False - """ - F = self.function_field() - S = F.base_field().maximal_order() - - gens = [F(a) for a in gens] - - V, from_V, to_V = F.vector_space() - M = V.span([to_V(b) for b in gens], base_ring=S) - - return self.ideal_monoid().element_class(self, M) - - def ideal(self, *gens): - """ - Return the fractional ideal generated by the elements in ``gens``. - - INPUT: - - - ``gens`` -- list of generators or an ideal in a ring which - coerces to this order - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: O.ideal(y) - Ideal (y) of Maximal order of Rational function field in y over Rational Field - sage: O.ideal([y,1/y]) == O.ideal(y,1/y) # multiple generators may be given as a list - True - - A fractional ideal of a nontrivial extension:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: O = K.maximal_order() - sage: I = O.ideal(x^2-4) - sage: L. = K.extension(y^2 - x^3 - 1) - sage: S = L.equation_order() - sage: S.ideal(1/y) - Ideal (1, (6/(x^3 + 1))*y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I2 = S.ideal(x^2-4); I2 - Ideal (x^2 + 3) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I2 == S.ideal(I) - True - """ - if len(gens) == 1: - gens = gens[0] - if not isinstance(gens, (list, tuple)): - if isinstance(gens, FunctionFieldIdeal): - gens = gens.gens() - else: - gens = [gens] - K = self.function_field() - - return self.ideal_with_gens_over_base([b*K(g) for b in self.basis() for g in gens]) - - def polynomial(self): - """ - Return the defining polynomial of the function field of which this is an order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.polynomial() - y^4 + x*y + 4*x + 1 - """ - return self._field.polynomial() - - def basis(self): - """ - Return a basis of the order over the maximal order of the base field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.basis() - (1, y, y^2, y^3) - """ - return self._basis - - def free_module(self): - """ - Return the free module formed by the basis over the maximal order - of the base function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.free_module() - Free module of degree 4 and rank 4 over Maximal order of Rational - function field in x over Finite Field of size 7 - Echelon basis matrix: - [1 0 0 0] - [0 1 0 0] - [0 0 1 0] - [0 0 0 1] - """ - return self._module - - def coordinate_vector(self, e): - """ - Return the coordinates of ``e`` with respect to the basis of the order. - - INPUT: - - - ``e`` -- element of the order or the function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: f = (x + y)^3 - sage: O.coordinate_vector(f) - (x^3, 3*x^2, 3*x, 1) - """ - return self._module.coordinate_vector(self._to_module(e), check=False) - - class FunctionFieldOrderInfinite(FunctionFieldOrder_base): """ Base class for infinite orders in function fields. @@ -549,271 +242,6 @@ def _repr_(self): return "Infinite order in {}".format(self.function_field()) -class FunctionFieldOrderInfinite_basis(FunctionFieldOrderInfinite): - """ - Order given by a basis over the infinite maximal order of the base - field. - - INPUT: - - - ``basis`` -- elements of the function field - - - ``check`` -- boolean (default: ``True``); if ``True``, check the basis generates - an order - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order_infinite(); O - Infinite order in Function field in y defined by y^4 + x*y + 4*x + 1 - - The basis only defines an order if the module it generates is closed under - multiplication and contains the identity element (only checked when - ``check`` is ``True``):: - - sage: O = L.order_infinite_with_basis([1, y, 1/x^2*y^2, y^3]); O - Traceback (most recent call last): - ... - ValueError: the module generated by basis (1, y, 1/x^2*y^2, y^3) must be closed under multiplication - - The basis also has to be linearly independent and of the same rank as the - degree of the function field of its elements (only checked when ``check`` - is ``True``):: - - sage: O = L.order_infinite_with_basis([1, y, 1/x^2*y^2, 1 + y]); O - Traceback (most recent call last): - ... - ValueError: The given basis vectors must be linearly independent. - - Note that 1 does not need to be an element of the basis, as long as it is - in the module spanned by it:: - - sage: O = L.order_infinite_with_basis([1 + 1/x*y, 1/x*y, 1/x^2*y^2, 1/x^3*y^3]); O - Infinite order in Function field in y defined by y^4 + x*y + 4*x + 1 - sage: O.basis() - (1/x*y + 1, 1/x*y, 1/x^2*y^2, 1/x^3*y^3) - """ - def __init__(self, basis, check=True): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order_infinite() - sage: TestSuite(O).run() - """ - if len(basis) == 0: - raise ValueError("basis must have positive length") - - field = basis[0].parent() - if len(basis) != field.degree(): - raise ValueError("length of basis must equal degree of field") - - FunctionFieldOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_module) - - # function field element f is in this order if and only if - # W.coordinate_vector(to(f)) in M - V, fr, to = field.vector_space() - R = field.base_field().maximal_order_infinite() - W = V.span_of_basis([to(v) for v in basis]) - from sage.modules.free_module import FreeModule - M = FreeModule(R,W.dimension()) - self._basis = tuple(basis) - self._ambient_space = W - self._module = M - - self._ring = field.polynomial_ring() - self._populate_coercion_lists_(coerce_list=[self._ring]) - - if check: - if self._module.rank() != field.degree(): - raise ValueError("basis {} is not linearly independent".format(basis)) - if not W.coordinate_vector(to(field(1))) in self._module: - raise ValueError("the identity element must be in the module spanned by basis {}".format(basis)) - if not all(W.coordinate_vector(to(a*b)) in self._module for a in basis for b in basis): - raise ValueError("the module generated by basis {} must be closed under multiplication".format(basis)) - - def _element_constructor_(self, f): - """ - Construct an element of this order. - - INPUT: - - - ``f`` -- element - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: O(x) - x - sage: O(1/x) - Traceback (most recent call last): - ... - TypeError: 1/x is not an element of Maximal order of Rational function field in x over Rational Field - """ - F = self.function_field() - try: - f = F(f) - except TypeError: - raise TypeError("unable to convert to an element of {}".format(F)) - - V, fr_V, to_V = F.vector_space() - W = self._ambient_space - if not W.coordinate_vector(to_V(f)) in self._module: - raise TypeError("{} is not an element of {}".format(f, self)) - - return f - - def ideal_with_gens_over_base(self, gens): - """ - Return the fractional ideal with basis ``gens`` over the - maximal order of the base field. - - It is not checked that ``gens`` really generates an ideal. - - INPUT: - - - ``gens`` -- list of elements that are a basis for the ideal over the - maximal order of the base field - - EXAMPLES: - - We construct an ideal in a rational function field:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: I = O.ideal([y]); I - Ideal (y) of Maximal order of Rational function field in y over Rational Field - sage: I*I - Ideal (y^2) of Maximal order of Rational function field in y over Rational Field - - We construct some ideals in a nontrivial function field:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order(); O - Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I = O.ideal_with_gens_over_base([1, y]); I - Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: I.module() - Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 - Echelon basis matrix: - [1 0] - [0 1] - - There is no check if the resulting object is really an ideal:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal_with_gens_over_base([y]); I - Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: y in I - True - sage: y^2 in I - False - """ - F = self.function_field() - S = F.base_field().maximal_order_infinite() - - gens = [F(a) for a in gens] - - V, from_V, to_V = F.vector_space() - M = V.span([to_V(b) for b in gens], base_ring=S) # not work - - return self.ideal_monoid().element_class(self, M) - - def ideal(self, *gens): - """ - Return the fractional ideal generated by the elements in ``gens``. - - INPUT: - - - ``gens`` -- list of generators or an ideal in a ring which coerces - to this order - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: O.ideal(y) - Ideal (y) of Maximal order of Rational function field in y over Rational Field - sage: O.ideal([y,1/y]) == O.ideal(y,1/y) # multiple generators may be given as a list - True - - A fractional ideal of a nontrivial extension:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: O = K.maximal_order_infinite() - sage: I = O.ideal(x^2-4) - sage: L. = K.extension(y^2 - x^3 - 1) - sage: S = L.order_infinite_with_basis([1, 1/x^2*y]) - """ - if len(gens) == 1: - gens = gens[0] - if not isinstance(gens, (list, tuple)): - if isinstance(gens, FunctionFieldIdeal): - gens = gens.gens() - else: - gens = [gens] - K = self.function_field() - - return self.ideal_with_gens_over_base([b*K(g) for b in self.basis() for g in gens]) - - def polynomial(self): - """ - Return the defining polynomial of the function field of which this is an order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.polynomial() - y^4 + x*y + 4*x + 1 - """ - return self._field.polynomial() - - def basis(self): - """ - Return a basis of this order over the maximal order of the base field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.basis() - (1, y, y^2, y^3) - """ - return self._basis - - def free_module(self): - """ - Return the free module formed by the basis over the maximal order of - the base field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.free_module() - Free module of degree 4 and rank 4 over Maximal order of Rational - function field in x over Finite Field of size 7 - Echelon basis matrix: - [1 0 0 0] - [0 1 0 0] - [0 0 1 0] - [0 0 0 1] - """ - return self._module - - class FunctionFieldMaximalOrder(UniqueRepresentation, FunctionFieldOrder): """ Base class of maximal orders of function fields. @@ -830,1944 +258,20 @@ def _repr_(self): return "Maximal order of %s"%(self.function_field(),) -class FunctionFieldMaximalOrder_rational(FunctionFieldMaximalOrder): +class FunctionFieldMaximalOrderInfinite(FunctionFieldMaximalOrder, FunctionFieldOrderInfinite): """ - Maximal orders of rational function fields. - - INPUT: - - - ``field`` -- a function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(19)); K - Rational function field in t over Finite Field of size 19 - sage: R = K.maximal_order(); R - Maximal order of Rational function field in t over Finite Field of size 19 + Base class of maximal infinite orders of function fields. """ - def __init__(self, field): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: TestSuite(O).run(skip='_test_gcd_vs_xgcd') - """ - FunctionFieldMaximalOrder.__init__(self, field, ideal_class=FunctionFieldIdeal_rational, - category=EuclideanDomains()) - - self._populate_coercion_lists_(coerce_list=[field._ring]) - - self._ring = field._ring - self._gen = self(self._ring.gen()) - self._basis = (self.one(),) - - def _element_constructor_(self, f): - """ - Make ``f`` a function field element of this order. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: O._element_constructor_(y) - y - sage: O._element_constructor_(1/y) - Traceback (most recent call last): - ... - TypeError: 1/y is not an element of Maximal order of Rational function field in y over Rational Field - """ - F = self.function_field() - try: - f = F(f) - except TypeError: - raise TypeError("unable to convert to an element of {}".format(F)) - - if not f.denominator() in self.function_field().constant_base_field(): - raise TypeError("%r is not an element of %r"%(f,self)) - - return f - - def ideal_with_gens_over_base(self, gens): - """ - Return the fractional ideal with generators ``gens``. - - INPUT: - - - ``gens`` -- elements of the function field - - EXAMPLES:: - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: O.ideal_with_gens_over_base([x^3+1,-y]) - Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 - """ - return self.ideal(gens) - - def _residue_field(self, ideal, name=None): + def _repr_(self): """ - Return a field isomorphic to the residue field at the prime ideal. - - The residue field is by definition `k[x]/q` where `q` is the irreducible - polynomial generating the prime ideal and `k` is the constant base field. - - INPUT: - - - ``ideal`` -- prime ideal of the order - - - ``name`` -- string; name of the generator of the residue field - - OUTPUT: - - - a field isomorphic to the residue field - - - a morphism from the field to `k[x]` via the residue field - - - a morphism from `k[x]` to the field via the residue field - EXAMPLES:: - sage: F. = FunctionField(GF(2)) - sage: O = F.maximal_order() - sage: I = O.ideal(x^2 + x + 1) - sage: R, fr_R, to_R = O._residue_field(I) - sage: R - Finite Field in z2 of size 2^2 - sage: [to_R(fr_R(e)) == e for e in R] - [True, True, True, True] - sage: [to_R(fr_R(e)).parent() is R for e in R] - [True, True, True, True] - sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) - sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) - True - sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) - True - sage: to_R(e1).parent() is R - True - sage: to_R(e2).parent() is R - True - - sage: F. = FunctionField(GF(2)) - sage: O = F.maximal_order() - sage: I = O.ideal(x + 1) - sage: R, fr_R, to_R = O._residue_field(I) - sage: R - Finite Field of size 2 - sage: [to_R(fr_R(e)) == e for e in R] - [True, True] - sage: [to_R(fr_R(e)).parent() is R for e in R] - [True, True] - sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) - sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) - True - sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) - True - sage: to_R(e1).parent() is R - True - sage: to_R(e2).parent() is R - True - - sage: F. = FunctionField(QQ) - sage: O = F.maximal_order() - sage: I = O.ideal(x^2 + x + 1) - sage: R, fr_R, to_R = O._residue_field(I) - sage: R - Number Field in a with defining polynomial x^2 + x + 1 - sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) - sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) - True - sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) - True - sage: to_R(e1).parent() is R - True - sage: to_R(e2).parent() is R - True - - sage: F. = FunctionField(QQ) - sage: O = F.maximal_order() - sage: I = O.ideal(x + 1) - sage: R, fr_R, to_R = O._residue_field(I) - sage: R - Rational Field - sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) - sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) - True - sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) - True - sage: to_R(e1).parent() is R - True - sage: to_R(e2).parent() is R - True - """ - F = self.function_field() - K = F.constant_base_field() - - q = ideal.gen().element().numerator() - - if F.is_global(): - R, _from_R, _to_R = self._residue_field_global(q, name=name) - elif isinstance(K, NumberField) or K is QQbar: - if name is None: - name = 'a' - if q.degree() == 1: - R = K - _from_R = lambda e: e - _to_R = lambda e: R(e % q) - else: - R = K.extension(q, names=name) - _from_R = lambda e: self._ring(list(R(e))) - _to_R = lambda e: (e % q)(R.gen(0)) - else: - raise NotImplementedError - - def from_R(e): - return F(_from_R(e)) - - def to_R(f): - return _to_R(f.numerator()) - - return R, from_R, to_R + sage: FunctionField(QQ,'y').maximal_order_infinite() + Maximal infinite order of Rational function field in y over Rational Field - def _residue_field_global(self, q, name=None): - """ - Return a finite field isomorphic to the residue field at q. - - This method assumes a global rational function field, that is, - the constant base field is a finite field. - - INPUT: - - - ``q`` -- irreducible polynomial - - - ``name`` -- string; name of the generator of the extension field - - OUTPUT: - - - a finite field - - - a function that outputs a polynomial lifting a finite field element - - - a function that outputs a finite field element for a polynomial - - The residue field is by definition `k[x]/q` where `k` is the base field. - - EXAMPLES:: - - sage: k. = GF(4) - sage: F. = FunctionField(k) - sage: O = F.maximal_order() - sage: O._ring - Univariate Polynomial Ring in x over Finite Field in a of size 2^2 - sage: f = x^3 + x + 1 - sage: _f = f.numerator() - sage: _f.is_irreducible() - True - sage: K, fr_K, to_K = O._residue_field_global(_f) - sage: K - Finite Field in z6 of size 2^6 - sage: all(to_K(fr_K(e)) == e for e in K) - True - - sage: k. = GF(2) - sage: F. = FunctionField(k) - sage: O = F.maximal_order() - sage: O._ring - Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) - sage: f = x^3 + x + 1 - sage: _f = f.numerator() - sage: _f.is_irreducible() - True - sage: K, fr_K, to_K = O._residue_field_global(_f) - sage: all(to_K(fr_K(e)) == e for e in K) - True - - """ - # polynomial ring over the base field - R = self._ring - - # base field of extension degree r over the prime field - k = R.base_ring() - a = k.gen() - r = k.degree() - - # extend the base field to a field of degree r*s over the - # prime field - s = q.degree() - K,sigma = k.extension(s, map=True, name=name) - - # find a root beta in K satisfying the irreducible q - S = K['X'] - beta = S([sigma(c) for c in q.list()]).roots()[0][0] - - # V is a vector space over the prime subfield of k of degree r*s - V = K.vector_space(map=False) - - w = K.one() - beta_pow = [] - for i in range(s): - beta_pow.append(w) - w *= beta - - w = K.one() - sigma_a = sigma(a) - sigma_a_pow = [] - for i in range(r): - sigma_a_pow.append(w) - w *= sigma_a - - basis = [V(sap * bp) for bp in beta_pow for sap in sigma_a_pow] - W = V.span_of_basis(basis) - - def to_K(f): - coeffs = (f % q).list() - return sum((sigma(c) * beta_pow[i] for i, c in enumerate(coeffs)), K.zero()) - - if r == 1: # take care of the prime field case - def fr_K(g): - co = W.coordinates(V(g), check=False) - return R([k(co[j]) for j in range(s)]) - else: - def fr_K(g): - co = W.coordinates(V(g), check=False) - return R([k(co[i:i+r]) for i in range(0, r*s, r)]) - - return K, fr_K, to_K - - def basis(self): - """ - Return the basis (=1) of the order as a module over the polynomial ring. - - EXAMPLES:: - - sage: K. = FunctionField(GF(19)) - sage: O = K.maximal_order() - sage: O.basis() - (1,) - """ - return self._basis - - def gen(self, n=0): - """ - Return the ``n``-th generator of the order. Since there is only one generator ``n`` must be 0. - - EXAMPLES:: - - sage: O = FunctionField(QQ,'y').maximal_order() - sage: O.gen() - y - sage: O.gen(1) - Traceback (most recent call last): - ... - IndexError: there is only one generator - """ - if n != 0: - raise IndexError("there is only one generator") - return self._gen - - def ngens(self): - """ - Return 1 the number of generators of the order. - - EXAMPLES:: - - sage: FunctionField(QQ,'y').maximal_order().ngens() - 1 - """ - return 1 - - def ideal(self, *gens): - """ - Return the fractional ideal generated by ``gens``. - - INPUT: - - - ``gens`` -- elements of the function field - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: O.ideal(x) - Ideal (x) of Maximal order of Rational function field in x over Rational Field - sage: O.ideal([x,1/x]) == O.ideal(x,1/x) # multiple generators may be given as a list - True - sage: O.ideal(x^3+1,x^3+6) - Ideal (1) of Maximal order of Rational function field in x over Rational Field - sage: I = O.ideal((x^2+1)*(x^3+1),(x^3+6)*(x^2+1)); I - Ideal (x^2 + 1) of Maximal order of Rational function field in x over Rational Field - sage: O.ideal(I) - Ideal (x^2 + 1) of Maximal order of Rational function field in x over Rational Field - """ - if len(gens) == 1: - gens = gens[0] - if not isinstance(gens, (list, tuple)): - if isinstance(gens, FunctionFieldIdeal): - gens = gens.gens() - else: - gens = (gens,) - K = self.function_field() - gens = [K(e) for e in gens if e != 0] - if len(gens) == 0: - gen = K(0) - else: - d = lcm([c.denominator() for c in gens]).monic() - g = gcd([(d*c).numerator() for c in gens]).monic() - gen = K(g/d) - - return self.ideal_monoid().element_class(self, gen) - - -class FunctionFieldMaximalOrder_polymod(FunctionFieldMaximalOrder): - """ - Maximal orders of extensions of function fields. - """ - - def __init__(self, field, ideal_class=FunctionFieldIdeal_polymod): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.maximal_order() - sage: TestSuite(O).run() - """ - FunctionFieldMaximalOrder.__init__(self, field, ideal_class) - - from .function_field import FunctionField_integral - if isinstance(field, FunctionField_integral): - basis = field._maximal_order_basis() - else: - model, from_model, to_model = field.monic_integral_model('z') - basis = [from_model(g) for g in model._maximal_order_basis()] - - V, fr, to = field.vector_space() - R = field.base_field().maximal_order() - - # This module is over R, but linear algebra over R (MaximalOrder) - # is not well supported in Sage. So we keep it as a vector space - # over rational function field. - self._module = V.span_of_basis([to(b) for b in basis]) - self._module_base_ring = R - self._basis = tuple(basis) - self._from_module = fr - self._to_module = to - - # multiplication table (lower triangular) - n = len(basis) - self._mtable = [] - for i in range(n): - row = [] - for j in range(n): - row.append(self._coordinate_vector(basis[i] * basis[j])) - self._mtable.append(row) - - zero = vector(R._ring, n * [0]) - - def mul_vecs(f, g): - s = zero - for i in range(n): - if f[i].is_zero(): - continue - for j in range(n): - if g[j].is_zero(): - continue - s += f[i] * g[j] * self._mtable[i][j] - return s - self._mul_vecs = mul_vecs - - # We prepare for using Kummer's theorem to decompose primes. Note - # that Kummer's theorem applies to most places. Here we find - # places for which the theorem does not apply. - - # this element is integral over k[x] and a generator of the field. - for gen in basis: - phi = gen.minimal_polynomial() - if phi.degree() == n: - break - - assert phi.degree() == n - - gen_vec = self._coordinate_vector(gen) - g = gen_vec.parent().gen(0) # x - gen_vec_pow = [g] - for i in range(n): - g = mul_vecs(g, gen_vec) - gen_vec_pow.append(g) - - # find places where {1,gen,...,gen^(n-1)} is not integral basis - W = V.span_of_basis([to(gen ** i) for i in range(phi.degree())]) - - supp = [] - for g in basis: - for c in W.coordinate_vector(to(g), check=False): - if not c.is_zero(): - supp += [f for f,_ in c.denominator().factor()] - supp = set(supp) - - self._kummer_gen = gen - self._kummer_gen_vec_pow = gen_vec_pow - self._kummer_polynomial = phi - self._kummer_places = supp - - def _element_constructor_(self, f): - """ - Construct an element of this order from ``f``. - - INPUT: - - - ``f`` -- element convertible to the function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^2-x*Y+x^2+1) - sage: O = L.maximal_order() - sage: y in O - True - sage: 1/y in O - False - sage: x in O - True - sage: 1/x in O - False - sage: L.=K.extension(Y^2+Y+x+1/x) - sage: O = L.maximal_order() - sage: 1 in O - True - sage: y in O - False - sage: x*y in O - True - sage: x^2*y in O - True - """ - F = self.function_field() - f = F(f) - # check if f is in this order - if not all(e in self._module_base_ring for e in self.coordinate_vector(f)): - raise TypeError( "{} is not an element of {}".format(f, self) ) - - return f - - def ideal_with_gens_over_base(self, gens): - """ - Return the fractional ideal with basis ``gens`` over the - maximal order of the base field. - - INPUT: - - - ``gens`` -- list of elements that generates the ideal over the - maximal order of the base field - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.maximal_order(); O - Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I = O.ideal_with_gens_over_base([1, y]); I - Ideal (1) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I.module() - Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 - Echelon basis matrix: - [1 0] - [0 1] - - There is no check if the resulting object is really an ideal:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.equation_order() - sage: I = O.ideal_with_gens_over_base([y]); I - Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 - sage: y in I - True - sage: y^2 in I - False - """ - return self._ideal_from_vectors([self.coordinate_vector(g) for g in gens]) - - def _ideal_from_vectors(self, vecs): - """ - Return an ideal generated as a module by vectors over rational function - field. - - INPUT: - - - ``vec`` -- list of vectors - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: v1 = O.coordinate_vector(x^3+1) - sage: v2 = O.coordinate_vector(y) - sage: v1 - (x^3 + 1, 0) - sage: v2 - (0, 1) - sage: O._ideal_from_vectors([v1,v2]) - Ideal (y) of Maximal order of Function field in y - defined by y^2 + 6*x^3 + 6 - """ - d = lcm([v.denominator() for v in vecs]) - vecs = [[(d*c).numerator() for c in v] for v in vecs] - return self._ideal_from_vectors_and_denominator(vecs, d, check=False) - - def _ideal_from_vectors_and_denominator(self, vecs, d=1, check=True): - """ - Return an ideal generated as a module by vectors divided by ``d`` over - the polynomial ring underlying the rational function field. - - INPUT: - - - ``vec`` -- list of vectors over the polynomial ring - - - ``d`` -- (default: 1) a nonzero element of the polynomial ring - - - ``check`` -- boolean (default: ``True``); if ``True``, compute the real - denominator of the vectors, possibly different from ``d``. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^2 - x^3 - 1) - sage: O = L.maximal_order() - sage: I = O.ideal(y^2) - sage: m = I.basis_matrix() - sage: v1 = m[0] - sage: v2 = m[1] - sage: v1 - (x^3 + 1, 0) - sage: v2 - (0, x^3 + 1) - sage: O._ideal_from_vectors([v1,v2]) # indirect doctest - Ideal (x^3 + 1) of Maximal order of Function field in y - defined by y^2 + 6*x^3 + 6 - """ - R = self._module_base_ring._ring - - d = R(d) # make it sure that d is in the polynomial ring - - if check and not d.is_one(): # check if d is true denominator - M = [] - g = d - for v in vecs: - for c in v: - g = g.gcd(c) - if g.is_one(): - break - else: - M += list(v) - continue # for v in vecs - mat = matrix(R, vecs) - break - else: - d = d // g - mat = matrix(R, len(vecs), [c // g for c in M]) - else: - mat = matrix(R, vecs) - - # IMPORTANT: make it sure that pivot polynomials monic - # so that we get a unique hnf. Here the hermite form - # algorithm also makes the pivots monic. - - # compute the reversed hermite form with zero rows deleted - reversed_hermite_form(mat) - i = 0 - while i < mat.nrows() and mat.row(i).is_zero(): - i += 1 - hnf = mat[i:] # remove zero rows - - return self.ideal_monoid().element_class(self, hnf, d) - - def ideal(self, *gens, **kwargs): - """ - Return the fractional ideal generated by the elements in ``gens``. - - INPUT: - - - ``gens`` -- list of generators - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: O = K.maximal_order() - sage: I = O.ideal(x^2-4) - sage: L. = K.extension(y^2 - x^3 - 1) - sage: S = L.maximal_order() - sage: S.ideal(1/y) - Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field - in y defined by y^2 + 6*x^3 + 6 - sage: I2 = S.ideal(x^2-4); I2 - Ideal (x^2 + 3) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 - sage: I2 == S.ideal(I) - True - - sage: K. = FunctionField(QQ); R. = K[] - sage: O = K.maximal_order() - sage: I = O.ideal(x^2-4) - sage: L. = K.extension(y^2 - x^3 - 1) - sage: S = L.maximal_order() - sage: S.ideal(1/y) - Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field in y defined by y^2 - x^3 - 1 - sage: I2 = S.ideal(x^2-4); I2 - Ideal (x^2 - 4) of Maximal order of Function field in y defined by y^2 - x^3 - 1 - sage: I2 == S.ideal(I) - True - """ - if len(gens) == 1: - gens = gens[0] - if not isinstance(gens, (list, tuple)): - if isinstance(gens, FunctionFieldIdeal): - gens = gens.gens() - else: - gens = (gens,) - F = self.function_field() - mgens = [b*F(g) for g in gens for b in self.basis()] - return self.ideal_with_gens_over_base(mgens) - - def polynomial(self): - """ - Return the defining polynomial of the function field of which this is an order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.polynomial() - y^4 + x*y + 4*x + 1 - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.polynomial() - y^4 + x*y + 4*x + 1 - """ - return self._field.polynomial() - - def basis(self): - """ - Return a basis of the order over the maximal order of the base function - field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: O.basis() - (1, y, y^2, y^3) - - sage: K. = FunctionField(QQ) - sage: R. = PolynomialRing(K) - sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) - sage: O = F.maximal_order() - sage: O.basis() - (1, 1/x^4*y, 1/x^9*y^2, 1/x^13*y^3) - """ - return self._basis - - def gen(self, n=0): - """ - Return the ``n``-th generator of the order. - - The basis elements of the order are generators. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: O = L.maximal_order() - sage: O.gen() - 1 - sage: O.gen(1) - y - sage: O.gen(2) - (1/(x^3 + x^2 + x))*y^2 - sage: O.gen(3) - Traceback (most recent call last): - ... - IndexError: there are only 3 generators - """ - if not ( n >= 0 and n < self.ngens() ): - raise IndexError("there are only {} generators".format(self.ngens())) - - return self._basis[n] - - def ngens(self): - """ - Return the number of generators of the order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: Oinf = L.maximal_order() - sage: Oinf.ngens() - 3 - """ - return len(self._basis) - - def free_module(self): - """ - Return the free module formed by the basis over the maximal order of the base field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.maximal_order() - sage: O.free_module() - Free module of degree 4 and rank 4 over Maximal order of Rational function field in x over Finite Field of size 7 - User basis matrix: - [1 0 0 0] - [0 1 0 0] - [0 0 1 0] - [0 0 0 1] - """ - return self._module.change_ring(self._module_base_ring) - - def coordinate_vector(self, e): - """ - Return the coordinates of ``e`` with respect to the basis of this order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.maximal_order() - sage: O.coordinate_vector(y) - (0, 1, 0, 0) - sage: O.coordinate_vector(x*y) - (0, x, 0, 0) - - sage: K. = FunctionField(QQ); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.equation_order() - sage: f = (x + y)^3 - sage: O.coordinate_vector(f) - (x^3, 3*x^2, 3*x, 1) - """ - return self._module.coordinate_vector(self._to_module(e)) - - def _coordinate_vector(self, e): - """ - Return the coordinate vector of ``e`` with respect to the basis - of the order. - - Assuming ``e`` is in the maximal order, the coordinates are given - as univariate polynomials in the underlying ring of the maximal - order of the rational function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.maximal_order() - sage: O._coordinate_vector(y) - (0, 1, 0, 0) - sage: O._coordinate_vector(x*y) - (0, x, 0, 0) - """ - v = self._module.coordinate_vector(self._to_module(e), check=False) - return vector([c.numerator() for c in v]) - - @cached_method - def different(self): - """ - Return the different ideal of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.maximal_order() - sage: O.different() - Ideal (y^3 + 2*x) - of Maximal order of Function field in y defined by y^4 + x*y + 4*x + 1 - """ - return ~self.codifferent() - - @cached_method - def codifferent(self): - """ - Return the codifferent ideal of the function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.maximal_order() - sage: O.codifferent() - Ideal (1, (1/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y^3 - + ((5*x^3 + 6*x^2 + x + 6)/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y^2 - + ((x^3 + 2*x^2 + 2*x + 2)/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y - + 6*x/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4)) of Maximal order of Function field - in y defined by y^4 + x*y + 4*x + 1 - """ - T = self._codifferent_matrix() - return self._ideal_from_vectors(T.inverse().columns()) - - @cached_method - def _codifferent_matrix(self): - """ - Return the matrix `T` defined in Proposition 4.8.19 of [Coh1993]_. - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.maximal_order() - sage: O._codifferent_matrix() - [ 4 0 0 4*x] - [ 0 0 4*x 5*x + 3] - [ 0 4*x 5*x + 3 0] - [ 4*x 5*x + 3 0 3*x^2] - """ - rows = [] - for u in self.basis(): - row = [] - for v in self.basis(): - row.append((u*v).trace()) - rows.append(row) - T = matrix(rows) - return T - - @cached_method - def decomposition(self, ideal): - """ - Return the decomposition of the prime ideal. - - INPUT: - - - ``ideal`` -- prime ideal of the base maximal order - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: o = K.maximal_order() - sage: O = F.maximal_order() - sage: p = o.ideal(x+1) - sage: O.decomposition(p) - [(Ideal (x + 1, y + 1) of Maximal order - of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), - (Ideal (x + 1, (1/(x^3 + x^2 + x))*y^2 + y + 1) of Maximal order - of Function field in y defined by y^3 + x^6 + x^4 + x^2, 2, 1)] - - ALGORITHM: - - In principle, we're trying to compute a primary decomposition - of the extension of ``ideal`` in ``self`` (an order, and therefore - a ring). However, while we have primary decomposition methods - for polynomial rings, we lack any such method for an order. - Therefore, we construct ``self`` mod ``ideal`` as a - finite-dimensional algebra, a construct for which we do - support primary decomposition. - - See :trac:`28094` and https://github.com/sagemath/sage/files/10659303/decomposition.pdf.gz - - .. TODO:: - - Use Kummer's theorem to shortcut this code if possible, like as - done in :meth:`FunctionFieldMaximalOrder_global.decomposition()` - - """ - F = self.function_field() - n = F.degree() - - # Base rational function field - K = self.function_field().base_field() - - # Univariate polynomial ring isomorphic to the maximal order of K - o = PolynomialRing(K.constant_field(), K.gen()) - - # Prime ideal in o defined by the generator of ideal in the maximal - # order of K - p = o(ideal.gen().numerator()) - - # Residue field k = o mod p - k = o.quo(p) - - # Given an element of the function field expressed as a K-vector times - # the basis of this order, construct the n n-by-n matrices that show - # how to multiply by each of the basis elements. - matrices = [matrix(o, [self.coordinate_vector(b1*b2) for b1 in self.basis()]) - for b2 in self.basis()] - - # Let O denote the maximal order self. When reduced modulo p, - # matrices_reduced give the multiplication matrices used to form the - # algebra O mod pO. - matrices_reduced = list(map(lambda M: M.mod(p), matrices)) - A = FiniteDimensionalAlgebra(k, matrices_reduced) - - # Each prime ideal of the algebra A corresponds to a prime ideal of O, - # and since the algebra is an Artinian ring, all of its prime ideals - # are maximal [stacks 00JA]. Thus, we find all of our factors by - # iterating over the algebra's maximal ideals. - factors = [] - for q in A.maximal_ideals(): - if q == A.zero_ideal(): - # The zero ideal is the unique maximal ideal, which means that - # A is a field, and the ideal itself is a prime ideal. - P = self.ideal(p) - - P.is_prime.set_cache(True) - P._prime_below = ideal - P._relative_degree = n - P._ramification_index = 1 - P._beta = [1] + [0]*(n-1) - else: - Q = q.basis_matrix().apply_map(lambda e: e.lift()) - P = self.ideal(p, *Q*vector(self.basis())) - - # Now we compute an element beta in O but not in pO such that - # beta*P in pO. - - # Since beta is in k[x]-module O, we keep beta as a vector - # in k[x] with respect to the basis of O. As long as at least - # one element in this vector is not divisible by p, beta will - # not be in pO. To ensure that beta*P is in pO, multiplying - # beta by each of P's generators must produce a vector whose - # elements are multiples of p. We can ensure that all this - # occurs by constructing a matrix in k, and finding a non-zero - # vector in the kernel of the matrix. - - m =[] - for g in q.basis_matrix(): - m.extend(matrix([g * mr for mr in matrices_reduced]).columns()) - beta = [c.lift() for c in matrix(m).right_kernel().basis()[0]] - - r = q - index = 1 - while True: - rq = r*q - if rq == r: - break - r = rq - index = index + 1 - - P.is_prime.set_cache(True) - P._prime_below = ideal - P._relative_degree = n - q.basis_matrix().nrows() - P._ramification_index = index - P._beta = beta - - factors.append((P, P._relative_degree, P._ramification_index)) - - return factors - - -class FunctionFieldMaximalOrder_global(FunctionFieldMaximalOrder_polymod): - """ - Maximal orders of global function fields. - - INPUT: - - - ``field`` -- function field to which this maximal order belongs - - EXAMPLES:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: L.maximal_order() - Maximal order of Function field in y defined by y^4 + x*y + 4*x + 1 - """ - - def __init__(self, field): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(7)); R. = K[] - sage: L. = K.extension(y^4 + x*y + 4*x + 1) - sage: O = L.maximal_order() - sage: TestSuite(O).run() - """ - FunctionFieldMaximalOrder_polymod.__init__(self, field, ideal_class=FunctionFieldIdeal_global) - - @cached_method - def p_radical(self, prime): - """ - Return the ``prime``-radical of the maximal order. - - INPUT: - - - ``prime`` -- prime ideal of the maximal order of the base - rational function field - - The algorithm is outlined in Section 6.1.3 of [Coh1993]_. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(t^3 - x^2 * (x^2 + x + 1)^2) - sage: o = K.maximal_order() - sage: O = F.maximal_order() - sage: p = o.ideal(x+1) - sage: O.p_radical(p) - Ideal (x + 1) of Maximal order of Function field in y - defined by y^3 + x^6 + x^4 + x^2 - """ - g = prime.gens()[0] - - if not (g.denominator() == 1 and g.numerator().is_irreducible()): - raise ValueError('not a prime ideal') - - F = self.function_field() - n = F.degree() - o = prime.ring() - p = g.numerator() - - # Fp is isomorphic to the residue field o/p - Fp, fr_Fp, to_Fp = o._residue_field_global(p) - - # exp = q^j should be at least extension degree where q is - # the order of the residue field o/p - q = F.constant_base_field().order()**p.degree() - exp = q - while exp <= F.degree(): - exp = exp**q - - # radical equals to the kernel of the map x |-> x^exp - mat = [] - for g in self.basis(): - v = [to_Fp(c) for c in self._coordinate_vector(g**exp)] - mat.append(v) - mat = matrix(Fp,mat) - ker = mat.kernel() - - # construct module generators of the p-radical - vecs = [] - for i in range(n): - v = vector([p if j == i else 0 for j in range(n)]) - vecs.append(v) - for b in ker.basis(): - v = vector([fr_Fp(c) for c in b]) - vecs.append(v) - - return self._ideal_from_vectors(vecs) - - @cached_method - def decomposition(self, ideal): - """ - Return the decomposition of the prime ideal. - - INPUT: - - - ``ideal`` -- prime ideal of the base maximal order - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: o = K.maximal_order() - sage: O = F.maximal_order() - sage: p = o.ideal(x+1) - sage: O.decomposition(p) - [(Ideal (x + 1, y + 1) of Maximal order - of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), - (Ideal (x + 1, (1/(x^3 + x^2 + x))*y^2 + y + 1) of Maximal order - of Function field in y defined by y^3 + x^6 + x^4 + x^2, 2, 1)] - """ - F = self.function_field() - n = F.degree() - - p = ideal.gen().numerator() - o = ideal.ring() - - # Fp is isomorphic to the residue field o/p - Fp, fr, to = o._residue_field_global(p) - P,X = Fp['X'].objgen() - - V = Fp**n # Ob = O/pO - - mtable = [] - for i in range(n): - row = [] - for j in range(n): - row.append( V([to(e) for e in self._mtable[i][j]]) ) - mtable.append(row) - - if p not in self._kummer_places: - ##################################### - # Decomposition by Kummer's theorem # - ##################################### - # gen is self._kummer_gen - gen_vec_pow = self._kummer_gen_vec_pow - mul_vecs = self._mul_vecs - - f = self._kummer_polynomial - fp = P([to(c.numerator()) for c in f.list()]) - decomposition = [] - for q, exp in fp.factor(): - # construct O.ideal([p,q(gen)]) - gen_vecs = list(matrix.diagonal(n * [p])) - c = q.list() - - # q(gen) in vector form - qgen = sum(fr(c[i]) * gen_vec_pow[i] for i in range(len(c))) - - I = matrix.identity(o._ring, n) - for i in range(n): - gen_vecs.append(mul_vecs(qgen,I[i])) - prime = self._ideal_from_vectors_and_denominator(gen_vecs) - - # Compute an element beta in O but not in pO. How to find beta - # is explained in Section 4.8.3 of [Coh1993]. We keep beta - # as a vector over k[x] with respect to the basis of O. - - # p and qgen generates the prime; modulo pO, qgenb generates the prime - qgenb = [to(qgen[i]) for i in range(n)] - m =[] - for i in range(n): - m.append(sum(qgenb[j] * mtable[i][j] for j in range(n))) - beta = [fr(coeff) for coeff in matrix(m).left_kernel().basis()[0]] - - prime.is_prime.set_cache(True) - prime._prime_below = ideal - prime._relative_degree = q.degree() - prime._ramification_index = exp - prime._beta = beta - - prime._kummer_form = (p, qgen) - - decomposition.append((prime, q.degree(), exp)) - else: - ############################# - # Buchman-Lenstra algorithm # - ############################# - pO = self.ideal(p) - Ip = self.p_radical(ideal) - Ob = matrix.identity(Fp, n) - - def bar(I): # transfer to O/pO - m = [] - for v in I._hnf: - m.append([to(e) for e in v]) - h = matrix(m).echelon_form() - return cut_last_zero_rows(h) - - def liftb(Ib): - m = [vector([fr(e) for e in v]) for v in Ib] - m += [v for v in pO._hnf] - return self._ideal_from_vectors_and_denominator(m,1) - - def cut_last_zero_rows(h): - i = h.nrows() - while i > 0 and h.row(i-1).is_zero(): - i -= 1 - return h[:i] - - def mul_vec(v1,v2): - s = 0 - for i in range(n): - for j in range(n): - s += v1[i] * v2[j] * mtable[i][j] - return s - - def pow(v, r): # r > 0 - m = v - while r > 1: - m = mul_vec(m,v) - r -= 1 - return m - - # Algorithm 6.2.7 of [Coh1993] - def div(Ib, Jb): - # compute a basis of Jb/Ib - sJb = Jb.row_space() - sIb = Ib.row_space() - sJbsIb,proj_sJbsIb,lift_sJbsIb = sJb.quotient_abstract(sIb) - supplement_basis = [lift_sJbsIb(v) for v in sJbsIb.basis()] - - m = [] - for b in V.gens(): # basis of Ob = O/pO - b_row = [] # row vector representation of the map a -> a*b - for a in supplement_basis: - b_row += lift_sJbsIb(proj_sJbsIb( mul_vec(a,b) )) - m.append(b_row) - return matrix(Fp,n,m).left_kernel().basis_matrix() - - # Algorithm 6.2.5 of [Coh1993] - def mul(Ib, Jb): - m = [] - for v1 in Ib: - for v2 in Jb: - m.append(mul_vec(v1,v2)) - h = matrix(m).echelon_form() - return cut_last_zero_rows(h) - - def add(Ib,Jb): - m = block_matrix([[Ib], [Jb]]) - h = m.echelon_form() - return cut_last_zero_rows(h) - - # K_1, K_2, ... - Lb = IpOb = bar(Ip+pO) - Kb = [Lb] - while not Lb.is_zero(): - Lb = mul(Lb,IpOb) - Kb.append(Lb) - - # J_1, J_2, ... - Jb =[Kb[0]] + [div(Kb[j],Kb[j-1]) for j in range(1,len(Kb))] - - # H_1, H_2, ... - Hb = [div(Jb[j],Jb[j+1]) for j in range(len(Jb)-1)] + [Jb[-1]] - - q = Fp.order() - - def split(h): - # VsW represents O/H as a vector space - W = h.row_space() # H/pO - VsW,to_VsW,lift_to_V = V.quotient_abstract(W) - - # compute the space K of elements in O/H that satisfy a^q-a=0 - l = [lift_to_V(b) for b in VsW.basis()] - - images = [to_VsW(pow(x, q) - x) for x in l] - K = VsW.hom(images, VsW).kernel() - - if K.dimension() == 0: - return [] - if K.dimension() == 1: # h is prime - return [(liftb(h),VsW.dimension())] # relative degree - - # choose a such that a^q - a is 0 but a is not in Fp - for a in K.basis(): - # IMPORTANT: This criterion is based on the assumption - # that O.basis() starts with 1. - if a.support() != [0]: - break - else: - raise AssertionError("no appropriate value found") - - a = lift_to_V(a) - # compute the minimal polynomial of a - m = [to_VsW(Ob[0])] # 1 in VsW - apow = a - while True: - v = to_VsW(apow) - try: - sol = matrix(m).solve_left(v) - except ValueError: - m.append(v) - apow = mul_vec(apow, a) - continue - break - - minpol = X**len(sol) - P(list(sol)) - - # The minimal polynomial of a has only linear factors and at least two - # of them. We set f to the first factor and g to the product of the rest. - fac = minpol.factor() - f = fac[0][0] - g = (fac/f).expand() - d,u,v = f.xgcd(g) - - assert d == 1, "Not relatively prime {} and {}".format(f,g) - - # finally, idempotent! - e = lift_to_V(sum([c1*c2 for c1,c2 in zip(u*f,m)])) - - h1 = add(h, matrix([mul_vec(e,Ob[i]) for i in range(n)])) - h2 = add(h, matrix([mul_vec(Ob[0]-e,Ob[i]) for i in range(n)])) - - return split(h1) + split(h2) - - decomposition = [] - for i in range(len(Hb)): - index = i + 1 # Hb starts with H_1 - for prime, degree in split(Hb[i]): - # Compute an element beta in O but not in pO. How to find beta - # is explained in Section 4.8.3 of [Coh1993]. We keep beta - # as a vector over k[x] with respect to the basis of O. - m =[] - for i in range(n): - r = [] - for g in prime._hnf: - r += sum(to(g[j]) * mtable[i][j] for j in range(n)) - m.append(r) - beta = [fr(e) for e in matrix(m).left_kernel().basis()[0]] - - prime.is_prime.set_cache(True) - prime._prime_below = ideal - prime._relative_degree = degree - prime._ramification_index = index - prime._beta = beta - - decomposition.append((prime, degree, index)) - - return decomposition - - -class FunctionFieldMaximalOrderInfinite(FunctionFieldMaximalOrder, FunctionFieldOrderInfinite): - """ - Base class of maximal infinite orders of function fields. - """ - def _repr_(self): - """ - EXAMPLES:: - - sage: FunctionField(QQ,'y').maximal_order_infinite() - Maximal infinite order of Rational function field in y over Rational Field - - sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) - sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) - sage: F.maximal_order_infinite() - Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 + sage: K. = FunctionField(GF(2)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings sage.rings.function_field + sage: F.maximal_order_infinite() # optional - sage.rings.finite_rings sage.modules sage.rings.function_field + Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 """ return "Maximal infinite order of %s"%(self.function_field(),) - - -class FunctionFieldMaximalOrderInfinite_rational(FunctionFieldMaximalOrderInfinite): - """ - Maximal infinite orders of rational function fields. - - INPUT: - - - ``field`` -- a rational function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(19)); K - Rational function field in t over Finite Field of size 19 - sage: R = K.maximal_order_infinite(); R - Maximal infinite order of Rational function field in t over Finite Field of size 19 - """ - def __init__(self, field, category=None): - """ - Initialize. - - TESTS:: - - sage: K. = FunctionField(GF(19)) - sage: O = K.maximal_order_infinite() - sage: TestSuite(O).run(skip='_test_gcd_vs_xgcd') - """ - FunctionFieldOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_rational, - category=PrincipalIdealDomains().or_subcategory(category)) - self._populate_coercion_lists_(coerce_list=[field.constant_base_field()]) - - def _element_constructor_(self, f): - """ - Make ``f`` an element of this order. - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order() - sage: O._element_constructor_(y) - y - sage: O._element_constructor_(1/y) - Traceback (most recent call last): - ... - TypeError: 1/y is not an element of Maximal order of Rational function field in y over Rational Field - """ - F = self.function_field() - try: - f = F(f) - except TypeError: - raise TypeError("unable to convert to an element of {}".format(F)) - - if f.denominator().degree() < f.numerator().degree(): - raise TypeError("{} is not an element of {}".format(f, self)) - - return f - - def basis(self): - """ - Return the basis (=1) of the order as a module over the polynomial ring. - - EXAMPLES:: - - sage: K. = FunctionField(GF(19)) - sage: O = K.maximal_order() - sage: O.basis() - (1,) - """ - return 1/self.function_field().gen() - - def gen(self, n=0): - """ - Return the ``n``-th generator of self. Since there is only one generator ``n`` must be 0. - - EXAMPLES:: - - sage: O = FunctionField(QQ,'y').maximal_order() - sage: O.gen() - y - sage: O.gen(1) - Traceback (most recent call last): - ... - IndexError: there is only one generator - """ - if n != 0: - raise IndexError("there is only one generator") - return self._gen - - def ngens(self): - """ - Return 1 the number of generators of the order. - - EXAMPLES:: - - sage: FunctionField(QQ,'y').maximal_order().ngens() - 1 - """ - return 1 - - def prime_ideal(self): - """ - Return the unique prime ideal of the order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(19)) - sage: O = K.maximal_order_infinite() - sage: O.prime_ideal() - Ideal (1/t) of Maximal infinite order of Rational function field in t - over Finite Field of size 19 - """ - return self.ideal( 1/self.function_field().gen() ) - - def ideal(self, *gens): - """ - Return the fractional ideal generated by ``gens``. - - INPUT: - - - ``gens`` -- elements of the function field - - EXAMPLES:: - - sage: K. = FunctionField(QQ) - sage: O = K.maximal_order_infinite() - sage: O.ideal(x) - Ideal (x) of Maximal infinite order of Rational function field in x over Rational Field - sage: O.ideal([x,1/x]) == O.ideal(x,1/x) # multiple generators may be given as a list - True - sage: O.ideal(x^3+1,x^3+6) - Ideal (x^3) of Maximal infinite order of Rational function field in x over Rational Field - sage: I = O.ideal((x^2+1)*(x^3+1),(x^3+6)*(x^2+1)); I - Ideal (x^5) of Maximal infinite order of Rational function field in x over Rational Field - sage: O.ideal(I) - Ideal (x^5) of Maximal infinite order of Rational function field in x over Rational Field - """ - if len(gens) == 1: - gens = gens[0] - if not isinstance(gens, (list, tuple)): - if isinstance(gens, FunctionFieldIdeal): - gens = gens.gens() - else: - gens = (gens,) - K = self.function_field() - gens = [K(g) for g in gens] - try: - d = max(g.numerator().degree() - g.denominator().degree() for g in gens if g != 0) - gen = K.gen() ** d - except ValueError: # all gens are zero - gen = K(0) - - return self.ideal_monoid().element_class(self, gen) - - -class FunctionFieldMaximalOrderInfinite_polymod(FunctionFieldMaximalOrderInfinite): - """ - Maximal infinite orders of function fields. - - INPUT: - - - ``field`` -- function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) - sage: F. = K.extension(t^3-x^2*(x^2+x+1)^2) - sage: F.maximal_order_infinite() - Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: L.maximal_order_infinite() - Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x - """ - def __init__(self, field, category=None): - """ - Initialize. - - TESTS:: - - 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_infinite() - sage: TestSuite(O).run() - """ - FunctionFieldOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_polymod) - - M, from_M, to_M = field._inversion_isomorphism() - basis = [from_M(g) for g in M.maximal_order().basis()] - - V, from_V, to_V = field.vector_space() - R = field.base_field().maximal_order_infinite() - - self._basis = tuple(basis) - self._module = V.span_of_basis([to_V(v) for v in basis]) - self._module_base_ring = R - self._to_module = to_V - - def _element_constructor_(self, f): - """ - Make ``f`` an element of this order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf.basis() - (1, 1/x*y) - sage: 1 in Oinf - True - sage: 1/x*y in Oinf - True - sage: x*y in Oinf - False - sage: 1/x in Oinf - True - """ - F = self.function_field() - - try: - f = F(f) - except TypeError: - raise TypeError("unable to convert to an element of {}".format(F)) - - O = F.base_field().maximal_order_infinite() - coordinates = self.coordinate_vector(f) - if not all(c in O for c in coordinates): - raise TypeError("%r is not an element of %r"%(f,self)) - - return f - - def basis(self): - """ - Return a basis of this order as a module over the maximal order - of the base function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf.basis() - (1, 1/x^2*y, (1/(x^4 + x^3 + x^2))*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: Oinf.basis() - (1, 1/x*y) - """ - return self._basis - - def gen(self, n=0): - """ - Return the ``n``-th generator of the order. - - The basis elements of the order are generators. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf.gen() - 1 - sage: Oinf.gen(1) - 1/x^2*y - sage: Oinf.gen(2) - (1/(x^4 + x^3 + x^2))*y^2 - sage: Oinf.gen(3) - Traceback (most recent call last): - ... - IndexError: there are only 3 generators - """ - if not ( n >= 0 and n < self.ngens() ): - raise IndexError("there are only {} generators".format(self.ngens())) - - return self._basis[n] - - def ngens(self): - """ - Return the number of generators of the order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf.ngens() - 3 - """ - return len(self._basis) - - def ideal(self, *gens): - """ - Return the ideal generated by ``gens``. - - INPUT: - - - ``gens`` -- tuple of elements of the function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: Oinf = F.maximal_order_infinite() - sage: I = Oinf.ideal(x,y); I - Ideal (y) of Maximal infinite order of Function field - in y defined by y^3 + x^6 + x^4 + x^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(x,y); I - Ideal (x) of Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x - """ - if len(gens) == 1: - gens = gens[0] - if not type(gens) in (list,tuple): - gens = (gens,) - mgens = [g * b for g in gens for b in self._basis] - return self.ideal_with_gens_over_base(mgens) - - def ideal_with_gens_over_base(self, gens): - """ - Return the ideal generated by ``gens`` as a module. - - INPUT: - - - ``gens`` -- tuple of elements of the function field - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); R. = K[] - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: Oinf = F.maximal_order_infinite() - sage: Oinf.ideal_with_gens_over_base((x^2, y, (1/(x^2 + x + 1))*y^2)) - Ideal (y) of Maximal infinite order of Function field in y - defined by y^3 + x^6 + x^4 + x^2 - """ - F = self.function_field() - iF, from_iF, to_iF = F._inversion_isomorphism() - iO = iF.maximal_order() - - ideal = iO.ideal_with_gens_over_base([to_iF(g) for g in gens]) - - if not ideal.is_zero(): - # Now the ideal does not correspond exactly to the ideal in the - # maximal infinite order through the inversion isomorphism. The - # reason is that the ideal also has factors not lying over x. - # The following procedure removes the spurious factors. The idea - # is that for an integral ideal I, J_n = I + (xO)^n stabilizes - # if n is large enough, and then J_n is the I with the spurious - # factors removed. For a fractional ideal, we also need to find - # the largest factor x^m that divides the denominator. - d = ideal.denominator() - h = ideal.hnf() - x = d.parent().gen() - - # find the largest factor x^m that divides the denominator - i = 0 - while d[i].is_zero(): - i += 1 - d = x ** i - - # find the largest n such that I + (xO)^n stabilizes - h1 = h - MS = h1.matrix_space() - k = MS.identity_matrix() - while True: - k = x * k - - h2 = block_matrix([[h],[k]]) - reversed_hermite_form(h2) - i = 0 - while i < h2.nrows() and h2.row(i).is_zero(): - i += 1 - h2 = h2[i:] # remove zero rows - - if h2 == h1: - break - h1 = h2 - - # reconstruct ideal - ideal = iO._ideal_from_vectors_and_denominator(list(h1), d) - - return self.ideal_monoid().element_class(self, ideal) - - def _to_iF(self, I): - """ - Return the ideal in the inverted function field from ``I``. - - INPUT: - - - ``I`` -- ideal of the function field - - 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: Oinf._to_iF(I) - Ideal (1, 1/x*s) of Maximal order of Function field in s - defined by s^2 + x*s + x^3 + x - """ - F = self.function_field() - iF,from_iF,to_iF = F._inversion_isomorphism() - iO = iF.maximal_order() - iI = iO.ideal_with_gens_over_base([to_iF(b) for b in I.gens_over_base()]) - return iI - - def decomposition(self): - r""" - Return prime ideal decomposition of `pO_\infty` where `p` is the unique - prime ideal of the maximal infinite order of the rational function field. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) - sage: Oinf = F.maximal_order_infinite() - sage: Oinf.decomposition() - [(Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1) of Maximal infinite order - of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), - (Ideal ((1/(x^4 + x^3 + x^2))*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, 2, 1)] - - :: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf.decomposition() - [(Ideal (1/x*y) of Maximal infinite order of Function field in y - defined by y^2 + y + (x^2 + 1)/x, 1, 2)] - - :: - - sage: K. = FunctionField(QQ); _. = K[] - sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) - sage: Oinf = F.maximal_order_infinite() - sage: Oinf.decomposition() - [(Ideal (1/x^2*y - 1) of Maximal infinite order - of Function field in y defined by y^3 - x^6 - 2*x^5 - 3*x^4 - 2*x^3 - x^2, 1, 1), - (Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1/x^2*y + 1) of Maximal infinite order - of Function field in y defined by y^3 - x^6 - 2*x^5 - 3*x^4 - 2*x^3 - x^2, 2, 1)] - - :: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf.decomposition() - [(Ideal (1/x*y) of Maximal infinite order of Function field in y - defined by y^2 + y + (x^2 + 1)/x, 1, 2)] - """ - F = self.function_field() - iF,from_iF,to_iF = F._inversion_isomorphism() - - x = iF.base_field().gen() - iO = iF.maximal_order() - io = iF.base_field().maximal_order() - ip = io.ideal(x) - - dec = [] - for iprime, deg, exp in iO.decomposition(ip): - prime = self.ideal_monoid().element_class(self, iprime) - dec.append((prime, deg, exp)) - return dec - - def different(self): - """ - Return the different ideal of the maximal infinite order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf.different() - Ideal (1/x) of Maximal infinite order of Function field in y - defined by y^2 + y + (x^2 + 1)/x - """ - T = self._codifferent_matrix() - codiff_gens = [] - for c in T.inverse().columns(): - codiff_gens.append(sum([ci*bi for ci,bi in zip(c,self.basis())])) - codiff = self.ideal_with_gens_over_base(codiff_gens) - return ~codiff - - @cached_method - def _codifferent_matrix(self): - """ - Return the codifferent matrix of the maximal infinite order. - - EXAMPLES:: - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^2 + Y + x + 1/x) - sage: Oinf = L.maximal_order_infinite() - sage: Oinf._codifferent_matrix() - [ 0 1/x] - [ 1/x 1/x^2] - """ - rows = [] - for u in self.basis(): - row = [] - for v in self.basis(): - row.append((u*v).trace()) - rows.append(row) - T = matrix(rows) - return T - - def coordinate_vector(self, e): - """ - Return the coordinates of ``e`` with respect to the basis of the order. - - INPUT: - - - ``e`` -- element of the function field - - The returned coordinates are in the base maximal infinite order if and only - if the element is in the order. - - 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/y^2 - sage: f in Oinf - True - sage: Oinf.coordinate_vector(f) - ((x^3 + x^2 + x)/(x^4 + 1), x^3/(x^4 + 1)) - """ - return self._module.coordinate_vector(self._to_module(e)) diff --git a/src/sage/rings/function_field/order_basis.py b/src/sage/rings/function_field/order_basis.py new file mode 100644 index 00000000000..bb9ffd72979 --- /dev/null +++ b/src/sage/rings/function_field/order_basis.py @@ -0,0 +1,563 @@ +# sage.doctest: optional - sage.modules (because __init__ constructs a vector space) +# some tests are marked # optional - sage.rings.finite_rings (because they use finite fields) +r""" +Orders of function fields: basis +""" + +#***************************************************************************** +# Copyright (C) 2023 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 .ideal import FunctionFieldIdeal, FunctionFieldIdeal_module, FunctionFieldIdealInfinite_module +from .order import FunctionFieldOrder, FunctionFieldOrderInfinite + + +class FunctionFieldOrder_basis(FunctionFieldOrder): + """ + Order given by a basis over the maximal order of the base field. + + INPUT: + + - ``basis`` -- list of elements of the function field + + - ``check`` -- (default: ``True``) if ``True``, check whether the module + that ``basis`` generates forms an order + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order(); O # optional - sage.rings.finite_rings sage.rings.function_field + Order in Function field in y defined by y^4 + x*y + 4*x + 1 + + The basis only defines an order if the module it generates is closed under + multiplication and contains the identity element:: + + sage: K. = FunctionField(QQ) + sage: R. = K[] + sage: L. = K.extension(y^5 - (x^3 + 2*x*y + 1/x)) # optional - sage.rings.function_field + sage: y.is_integral() # optional - sage.rings.function_field + False + sage: L.order(y) # optional - sage.rings.function_field + Traceback (most recent call last): + ... + ValueError: the module generated by basis (1, y, y^2, y^3, y^4) must be closed under multiplication + + The basis also has to be linearly independent and of the same rank as the + degree of the function field of its elements (only checked when ``check`` + is ``True``):: + + sage: L.order(L(x)) # optional - sage.rings.function_field + Traceback (most recent call last): + ... + ValueError: basis (1, x, x^2, x^3, x^4) is not linearly independent + sage: sage.rings.function_field.order_basis.FunctionFieldOrder_basis((y,y,y^3,y^4,y^5)) # optional - sage.rings.function_field + Traceback (most recent call last): + ... + ValueError: basis (y, y, y^3, y^4, 2*x*y + (x^4 + 1)/x) is not linearly independent + """ + def __init__(self, basis, check=True): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(O).run() # optional - sage.rings.finite_rings sage.rings.function_field + """ + if len(basis) == 0: + raise ValueError("basis must have positive length") + + field = basis[0].parent() + if len(basis) != field.degree(): + raise ValueError("length of basis must equal degree of field") + + FunctionFieldOrder.__init__(self, field, ideal_class=FunctionFieldIdeal_module) + + V, from_V, to_V = field.vector_space() + + R = V.base_field().maximal_order() + self._module = V.span([to_V(b) for b in basis], base_ring=R) + + self._from_module= from_V + self._to_module = to_V + self._basis = tuple(basis) + self._ring = field.polynomial_ring() + self._populate_coercion_lists_(coerce_list=[self._ring]) + + if check: + if self._module.rank() != field.degree(): + raise ValueError("basis {} is not linearly independent".format(basis)) + if not to_V(field(1)) in self._module: + raise ValueError("the identity element must be in the module spanned by basis {}".format(basis)) + if not all(to_V(a*b) in self._module for a in basis for b in basis): + raise ValueError("the module generated by basis {} must be closed under multiplication".format(basis)) + + def _element_constructor_(self, f): + """ + Construct an element of this order from ``f``. + + INPUT: + + - ``f`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: K.maximal_order()._element_constructor_(x) + x + """ + F = self.function_field() + + try: + f = F(f) + except TypeError: + raise TypeError("unable to convert to an element of {}".format(F)) + + V, fr_V, to_V = F.vector_space() + f_vector = to_V(f) + if f_vector not in self._module: + raise TypeError("{} is not an element of {}".format(f_vector, self)) + + return f + + def ideal_with_gens_over_base(self, gens): + """ + Return the fractional ideal with basis ``gens`` over the + maximal order of the base field. + + It is not checked that the ``gens`` really generates an ideal. + + INPUT: + + - ``gens`` -- list of elements of the function field + + EXAMPLES: + + We construct an ideal in a rational function field:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal([y]); I + Ideal (y) of Maximal order of Rational function field in y over Rational Field + sage: I * I + Ideal (y^2) of Maximal order of Rational function field in y over Rational Field + + We construct some ideals in a nontrivial function field:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order(); O # optional - sage.rings.finite_rings sage.rings.function_field + Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: I = O.ideal_with_gens_over_base([1, y]); I # optional - sage.rings.finite_rings sage.rings.function_field + Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: I.module() # optional - sage.rings.finite_rings sage.rings.function_field + Free module of degree 2 and rank 2 over + Maximal order of Rational function field in x over Finite Field of size 7 + Echelon basis matrix: + [1 0] + [0 1] + + There is no check if the resulting object is really an ideal:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal_with_gens_over_base([y]); I # optional - sage.rings.finite_rings sage.rings.function_field + Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: y in I # optional - sage.rings.finite_rings sage.rings.function_field + True + sage: y^2 in I # optional - sage.rings.finite_rings sage.rings.function_field + False + """ + F = self.function_field() + S = F.base_field().maximal_order() + + gens = [F(a) for a in gens] + + V, from_V, to_V = F.vector_space() + M = V.span([to_V(b) for b in gens], base_ring=S) + + return self.ideal_monoid().element_class(self, M) + + def ideal(self, *gens): + """ + Return the fractional ideal generated by the elements in ``gens``. + + INPUT: + + - ``gens`` -- list of generators or an ideal in a ring which + coerces to this order + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: O.ideal(y) + Ideal (y) of Maximal order of Rational function field in y over Rational Field + sage: O.ideal([y,1/y]) == O.ideal(y,1/y) # multiple generators may be given as a list + True + + A fractional ideal of a nontrivial extension:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^2 - 4) # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: S = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: S.ideal(1/y) # optional - sage.rings.finite_rings sage.rings.function_field + Ideal (1, (6/(x^3 + 1))*y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: I2 = S.ideal(x^2 - 4); I2 # optional - sage.rings.finite_rings sage.rings.function_field + Ideal (x^2 + 3) of Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: I2 == S.ideal(I) # optional - sage.rings.finite_rings sage.rings.function_field + True + """ + if len(gens) == 1: + gens = gens[0] + if not isinstance(gens, (list, tuple)): + if isinstance(gens, FunctionFieldIdeal): + gens = gens.gens() + else: + gens = [gens] + K = self.function_field() + + return self.ideal_with_gens_over_base([b*K(g) for b in self.basis() for g in gens]) + + def polynomial(self): + """ + Return the defining polynomial of the function field of which this is an order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.polynomial() # optional - sage.rings.finite_rings sage.rings.function_field + y^4 + x*y + 4*x + 1 + """ + return self._field.polynomial() + + def basis(self): + """ + Return a basis of the order over the maximal order of the base field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.basis() # optional - sage.rings.finite_rings sage.rings.function_field + (1, y, y^2, y^3) + """ + return self._basis + + def free_module(self): + """ + Return the free module formed by the basis over the maximal order + of the base function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.free_module() # optional - sage.rings.finite_rings sage.rings.function_field + Free module of degree 4 and rank 4 over Maximal order of Rational + function field in x over Finite Field of size 7 + Echelon basis matrix: + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + """ + return self._module + + def coordinate_vector(self, e): + """ + Return the coordinates of ``e`` with respect to the basis of the order. + + INPUT: + + - ``e`` -- element of the order or the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: f = (x + y)^3 # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.coordinate_vector(f) # optional - sage.rings.finite_rings sage.rings.function_field + (x^3, 3*x^2, 3*x, 1) + """ + return self._module.coordinate_vector(self._to_module(e), check=False) + + +class FunctionFieldOrderInfinite_basis(FunctionFieldOrderInfinite): + """ + Order given by a basis over the infinite maximal order of the base field. + + INPUT: + + - ``basis`` -- elements of the function field + + - ``check`` -- boolean (default: ``True``); if ``True``, check the basis generates + an order + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order_infinite(); O # optional - sage.rings.finite_rings sage.rings.function_field + Infinite order in Function field in y defined by y^4 + x*y + 4*x + 1 + + The basis only defines an order if the module it generates is closed under + multiplication and contains the identity element (only checked when + ``check`` is ``True``):: + + sage: O = L.order_infinite_with_basis([1, y, 1/x^2*y^2, y^3]); O # optional - sage.rings.finite_rings sage.rings.function_field + Traceback (most recent call last): + ... + ValueError: the module generated by basis (1, y, 1/x^2*y^2, y^3) must be closed under multiplication + + The basis also has to be linearly independent and of the same rank as the + degree of the function field of its elements (only checked when ``check`` + is ``True``):: + + sage: O = L.order_infinite_with_basis([1, y, 1/x^2*y^2, 1 + y]); O # optional - sage.rings.finite_rings sage.rings.function_field + Traceback (most recent call last): + ... + ValueError: The given basis vectors must be linearly independent. + + Note that 1 does not need to be an element of the basis, as long as it is + in the module spanned by it:: + + sage: O = L.order_infinite_with_basis([1 + 1/x*y, 1/x*y, 1/x^2*y^2, 1/x^3*y^3]); O # optional - sage.rings.finite_rings sage.rings.function_field + Infinite order in Function field in y defined by y^4 + x*y + 4*x + 1 + sage: O.basis() # optional - sage.rings.finite_rings sage.rings.function_field + (1/x*y + 1, 1/x*y, 1/x^2*y^2, 1/x^3*y^3) + """ + def __init__(self, basis, check=True): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order_infinite() # optional - sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(O).run() # optional - sage.rings.finite_rings sage.rings.function_field + """ + if len(basis) == 0: + raise ValueError("basis must have positive length") + + field = basis[0].parent() + if len(basis) != field.degree(): + raise ValueError("length of basis must equal degree of field") + + FunctionFieldOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_module) + + # function field element f is in this order if and only if + # W.coordinate_vector(to(f)) in M + V, fr, to = field.vector_space() + R = field.base_field().maximal_order_infinite() + W = V.span_of_basis([to(v) for v in basis]) + from sage.modules.free_module import FreeModule + M = FreeModule(R, W.dimension()) + self._basis = tuple(basis) + self._ambient_space = W + self._module = M + + self._ring = field.polynomial_ring() + self._populate_coercion_lists_(coerce_list=[self._ring]) + + if check: + if self._module.rank() != field.degree(): + raise ValueError("basis {} is not linearly independent".format(basis)) + if not W.coordinate_vector(to(field(1))) in self._module: + raise ValueError("the identity element must be in the module spanned by basis {}".format(basis)) + if not all(W.coordinate_vector(to(a*b)) in self._module for a in basis for b in basis): + raise ValueError("the module generated by basis {} must be closed under multiplication".format(basis)) + + def _element_constructor_(self, f): + """ + Construct an element of this order. + + INPUT: + + - ``f`` -- element + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: O(x) + x + sage: O(1/x) + Traceback (most recent call last): + ... + TypeError: 1/x is not an element of Maximal order of Rational function field in x over Rational Field + """ + F = self.function_field() + try: + f = F(f) + except TypeError: + raise TypeError("unable to convert to an element of {}".format(F)) + + V, fr_V, to_V = F.vector_space() + W = self._ambient_space + if not W.coordinate_vector(to_V(f)) in self._module: + raise TypeError("{} is not an element of {}".format(f, self)) + + return f + + def ideal_with_gens_over_base(self, gens): + """ + Return the fractional ideal with basis ``gens`` over the + maximal order of the base field. + + It is not checked that ``gens`` really generates an ideal. + + INPUT: + + - ``gens`` -- list of elements that are a basis for the ideal over the + maximal order of the base field + + EXAMPLES: + + We construct an ideal in a rational function field:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: I = O.ideal([y]); I + Ideal (y) of Maximal order of Rational function field in y over Rational Field + sage: I*I + Ideal (y^2) of Maximal order of Rational function field in y over Rational Field + + We construct some ideals in a nontrivial function field:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order(); O # optional - sage.rings.finite_rings sage.rings.function_field + Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: I = O.ideal_with_gens_over_base([1, y]); I # optional - sage.rings.finite_rings sage.rings.function_field + Ideal (1) of Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: I.module() # optional - sage.rings.finite_rings sage.rings.function_field + Free module of degree 2 and rank 2 over Maximal order of Rational function field in x over Finite Field of size 7 + Echelon basis matrix: + [1 0] + [0 1] + + There is no check if the resulting object is really an ideal:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal_with_gens_over_base([y]); I # optional - sage.rings.finite_rings sage.rings.function_field + Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: y in I # optional - sage.rings.finite_rings sage.rings.function_field + True + sage: y^2 in I # optional - sage.rings.finite_rings sage.rings.function_field + False + """ + F = self.function_field() + S = F.base_field().maximal_order_infinite() + + gens = [F(a) for a in gens] + + V, from_V, to_V = F.vector_space() + M = V.span([to_V(b) for b in gens], base_ring=S) # not work + + return self.ideal_monoid().element_class(self, M) + + def ideal(self, *gens): + """ + Return the fractional ideal generated by the elements in ``gens``. + + INPUT: + + - ``gens`` -- list of generators or an ideal in a ring which coerces + to this order + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: O.ideal(y) + Ideal (y) of Maximal order of Rational function field in y over Rational Field + sage: O.ideal([y,1/y]) == O.ideal(y,1/y) # multiple generators may be given as a list + True + + A fractional ideal of a nontrivial extension:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: O = K.maximal_order_infinite() + sage: I = O.ideal(x^2 - 4) + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: S = L.order_infinite_with_basis([1, 1/x^2*y]) # optional - sage.rings.function_field + """ + if len(gens) == 1: + gens = gens[0] + if not isinstance(gens, (list, tuple)): + if isinstance(gens, FunctionFieldIdeal): + gens = gens.gens() + else: + gens = [gens] + K = self.function_field() + + return self.ideal_with_gens_over_base([b*K(g) for b in self.basis() for g in gens]) + + def polynomial(self): + """ + Return the defining polynomial of the function field of which this is an order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.polynomial() # optional - sage.rings.finite_rings sage.rings.function_field + y^4 + x*y + 4*x + 1 + """ + return self._field.polynomial() + + def basis(self): + """ + Return a basis of this order over the maximal order of the base field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.basis() # optional - sage.rings.finite_rings sage.rings.function_field + (1, y, y^2, y^3) + """ + return self._basis + + def free_module(self): + """ + Return the free module formed by the basis over the maximal order of + the base field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O.free_module() # optional - sage.rings.finite_rings sage.rings.function_field + Free module of degree 4 and rank 4 over Maximal order of Rational + function field in x over Finite Field of size 7 + Echelon basis matrix: + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + """ + return self._module diff --git a/src/sage/rings/function_field/order_polymod.py b/src/sage/rings/function_field/order_polymod.py new file mode 100644 index 00000000000..66f6fb4b3db --- /dev/null +++ b/src/sage/rings/function_field/order_polymod.py @@ -0,0 +1,1436 @@ +# sage.doctest: optional - sage.rings.function_field +r""" +Orders of function fields: extension +""" + +#***************************************************************************** +# Copyright (C) 2023 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 sage.arith.functions import lcm +from sage.misc.cachefunc import cached_method +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing + +from .ideal import FunctionFieldIdeal +from .ideal_polymod import ( + FunctionFieldIdeal_polymod, + FunctionFieldIdeal_global, + FunctionFieldIdealInfinite_polymod +) +from .order import FunctionFieldMaximalOrder, FunctionFieldMaximalOrderInfinite + + +class FunctionFieldMaximalOrder_polymod(FunctionFieldMaximalOrder): + """ + Maximal orders of extensions of function fields. + """ + + def __init__(self, field, ideal_class=FunctionFieldIdeal_polymod): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: TestSuite(O).run() # optional - sage.rings.finite_rings + """ + FunctionFieldMaximalOrder.__init__(self, field, ideal_class) + + from sage.modules.free_module_element import vector + from .function_field_polymod import FunctionField_integral + + if isinstance(field, FunctionField_integral): + basis = field._maximal_order_basis() + else: + model, from_model, to_model = field.monic_integral_model('z') + basis = [from_model(g) for g in model._maximal_order_basis()] + + V, fr, to = field.vector_space() + R = field.base_field().maximal_order() + + # This module is over R, but linear algebra over R (MaximalOrder) + # is not well supported in Sage. So we keep it as a vector space + # over rational function field. + self._module = V.span_of_basis([to(b) for b in basis]) + self._module_base_ring = R + self._basis = tuple(basis) + self._from_module = fr + self._to_module = to + + # multiplication table (lower triangular) + n = len(basis) + self._mtable = [] + for i in range(n): + row = [] + for j in range(n): + row.append(self._coordinate_vector(basis[i] * basis[j])) + self._mtable.append(row) + + zero = vector(R._ring, n * [0]) + + def mul_vecs(f, g): + s = zero + for i in range(n): + if f[i].is_zero(): + continue + for j in range(n): + if g[j].is_zero(): + continue + s += f[i] * g[j] * self._mtable[i][j] + return s + self._mul_vecs = mul_vecs + + # We prepare for using Kummer's theorem to decompose primes. Note + # that Kummer's theorem applies to most places. Here we find + # places for which the theorem does not apply. + + # this element is integral over k[x] and a generator of the field. + for gen in basis: + phi = gen.minimal_polynomial() + if phi.degree() == n: + break + + assert phi.degree() == n + + gen_vec = self._coordinate_vector(gen) + g = gen_vec.parent().gen(0) # x + gen_vec_pow = [g] + for i in range(n): + g = mul_vecs(g, gen_vec) + gen_vec_pow.append(g) + + # find places where {1,gen,...,gen^(n-1)} is not integral basis + W = V.span_of_basis([to(gen ** i) for i in range(phi.degree())]) + + supp = [] + for g in basis: + for c in W.coordinate_vector(to(g), check=False): + if not c.is_zero(): + supp += [f for f,_ in c.denominator().factor()] + supp = set(supp) + + self._kummer_gen = gen + self._kummer_gen_vec_pow = gen_vec_pow + self._kummer_polynomial = phi + self._kummer_places = supp + + def _element_constructor_(self, f): + """ + Construct an element of this order from ``f``. + + INPUT: + + - ``f`` -- element convertible to the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 - x*Y + x^2 + 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: y in O # optional - sage.rings.finite_rings + True + sage: 1/y in O # optional - sage.rings.finite_rings + False + sage: x in O # optional - sage.rings.finite_rings + True + sage: 1/x in O # optional - sage.rings.finite_rings + False + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: 1 in O # optional - sage.rings.finite_rings + True + sage: y in O # optional - sage.rings.finite_rings + False + sage: x*y in O # optional - sage.rings.finite_rings + True + sage: x^2*y in O # optional - sage.rings.finite_rings + True + """ + F = self.function_field() + f = F(f) + # check if f is in this order + if not all(e in self._module_base_ring for e in self.coordinate_vector(f)): + raise TypeError( "{} is not an element of {}".format(f, self) ) + + return f + + def ideal_with_gens_over_base(self, gens): + """ + Return the fractional ideal with basis ``gens`` over the + maximal order of the base field. + + INPUT: + + - ``gens`` -- list of elements that generates the ideal over the + maximal order of the base field + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order(); O # optional - sage.rings.finite_rings + Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 + sage: I = O.ideal_with_gens_over_base([1, y]); I # optional - sage.rings.finite_rings + Ideal (1) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 + sage: I.module() # optional - sage.rings.finite_rings + Free module of degree 2 and rank 2 over + Maximal order of Rational function field in x over Finite Field of size 7 + Echelon basis matrix: + [1 0] + [0 1] + + There is no check if the resulting object is really an ideal:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = L.equation_order() # optional - sage.rings.finite_rings + sage: I = O.ideal_with_gens_over_base([y]); I # optional - sage.rings.finite_rings + Ideal (y) of Order in Function field in y defined by y^2 + 6*x^3 + 6 + sage: y in I # optional - sage.rings.finite_rings + True + sage: y^2 in I # optional - sage.rings.finite_rings + False + """ + return self._ideal_from_vectors([self.coordinate_vector(g) for g in gens]) + + def _ideal_from_vectors(self, vecs): + """ + Return an ideal generated as a module by vectors over rational function + field. + + INPUT: + + - ``vec`` -- list of vectors + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: v1 = O.coordinate_vector(x^3 + 1) # optional - sage.rings.finite_rings + sage: v2 = O.coordinate_vector(y) # optional - sage.rings.finite_rings + sage: v1 # optional - sage.rings.finite_rings + (x^3 + 1, 0) + sage: v2 # optional - sage.rings.finite_rings + (0, 1) + sage: O._ideal_from_vectors([v1, v2]) # optional - sage.rings.finite_rings + Ideal (y) of Maximal order of Function field in y + defined by y^2 + 6*x^3 + 6 + """ + d = lcm([v.denominator() for v in vecs]) + vecs = [[(d*c).numerator() for c in v] for v in vecs] + return self._ideal_from_vectors_and_denominator(vecs, d, check=False) + + def _ideal_from_vectors_and_denominator(self, vecs, d=1, check=True): + """ + Return an ideal generated as a module by vectors divided by ``d`` over + the polynomial ring underlying the rational function field. + + INPUT: + + - ``vec`` -- list of vectors over the polynomial ring + + - ``d`` -- (default: 1) a nonzero element of the polynomial ring + + - ``check`` -- boolean (default: ``True``); if ``True``, compute the real + denominator of the vectors, possibly different from ``d``. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(y^2) # optional - sage.rings.finite_rings + sage: m = I.basis_matrix() # optional - sage.rings.finite_rings + sage: v1 = m[0] # optional - sage.rings.finite_rings + sage: v2 = m[1] # optional - sage.rings.finite_rings + sage: v1 # optional - sage.rings.finite_rings + (x^3 + 1, 0) + sage: v2 # optional - sage.rings.finite_rings + (0, x^3 + 1) + sage: O._ideal_from_vectors([v1, v2]) # indirect doctest # optional - sage.rings.finite_rings + Ideal (x^3 + 1) of Maximal order of Function field in y + defined by y^2 + 6*x^3 + 6 + """ + from sage.matrix.constructor import matrix + from .hermite_form_polynomial import reversed_hermite_form + + R = self._module_base_ring._ring + + d = R(d) # make it sure that d is in the polynomial ring + + if check and not d.is_one(): # check if d is true denominator + M = [] + g = d + for v in vecs: + for c in v: + g = g.gcd(c) + if g.is_one(): + break + else: + M += list(v) + continue # for v in vecs + mat = matrix(R, vecs) + break + else: + d = d // g + mat = matrix(R, len(vecs), [c // g for c in M]) + else: + mat = matrix(R, vecs) + + # IMPORTANT: make it sure that pivot polynomials monic + # so that we get a unique hnf. Here the hermite form + # algorithm also makes the pivots monic. + + # compute the reversed hermite form with zero rows deleted + reversed_hermite_form(mat) + i = 0 + while i < mat.nrows() and mat.row(i).is_zero(): + i += 1 + hnf = mat[i:] # remove zero rows + + return self.ideal_monoid().element_class(self, hnf, d) + + def ideal(self, *gens, **kwargs): + """ + Return the fractional ideal generated by the elements in ``gens``. + + INPUT: + + - ``gens`` -- list of generators + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^2 - 4) # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: S = L.maximal_order() # optional - sage.rings.finite_rings + sage: S.ideal(1/y) # optional - sage.rings.finite_rings + Ideal ((1/(x^3 + 1))*y) of Maximal order of Function field + in y defined by y^2 + 6*x^3 + 6 + sage: I2 = S.ideal(x^2 - 4); I2 # optional - sage.rings.finite_rings + Ideal (x^2 + 3) of Maximal order of Function field in y defined by y^2 + 6*x^3 + 6 + sage: I2 == S.ideal(I) # optional - sage.rings.finite_rings + True + + sage: K. = FunctionField(QQ); R. = K[] + sage: O = K.maximal_order() + sage: I = O.ideal(x^2 - 4) + sage: L. = K.extension(y^2 - x^3 - 1) + sage: S = L.maximal_order() + sage: S.ideal(1/y) + Ideal ((1/(x^3 + 1))*y) of + Maximal order of Function field in y defined by y^2 - x^3 - 1 + sage: I2 = S.ideal(x^2-4); I2 + Ideal (x^2 - 4) of Maximal order of Function field in y defined by y^2 - x^3 - 1 + sage: I2 == S.ideal(I) + True + """ + if len(gens) == 1: + gens = gens[0] + if not isinstance(gens, (list, tuple)): + if isinstance(gens, FunctionFieldIdeal): + gens = gens.gens() + else: + gens = (gens,) + F = self.function_field() + mgens = [b*F(g) for g in gens for b in self.basis()] + return self.ideal_with_gens_over_base(mgens) + + def polynomial(self): + """ + Return the defining polynomial of the function field of which this is an order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.equation_order() # optional - sage.rings.finite_rings + sage: O.polynomial() # optional - sage.rings.finite_rings + y^4 + x*y + 4*x + 1 + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: O.polynomial() + y^4 + x*y + 4*x + 1 + """ + return self._field.polynomial() + + def basis(self): + """ + Return a basis of the order over the maximal order of the base function + field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.equation_order() # optional - sage.rings.finite_rings + sage: O.basis() # optional - sage.rings.finite_rings + (1, y, y^2, y^3) + + sage: K. = FunctionField(QQ) + sage: R. = PolynomialRing(K) + sage: F. = K.extension(t^4 + x^12*t^2 + x^18*t + x^21 + x^18) + sage: O = F.maximal_order() + sage: O.basis() + (1, 1/x^4*y, 1/x^9*y^2, 1/x^13*y^3) + """ + return self._basis + + def gen(self, n=0): + """ + Return the ``n``-th generator of the order. + + The basis elements of the order are generators. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: O.gen() # optional - sage.rings.finite_rings + 1 + sage: O.gen(1) # optional - sage.rings.finite_rings + y + sage: O.gen(2) # optional - sage.rings.finite_rings + (1/(x^3 + x^2 + x))*y^2 + sage: O.gen(3) # optional - sage.rings.finite_rings + Traceback (most recent call last): + ... + IndexError: there are only 3 generators + """ + if not ( n >= 0 and n < self.ngens() ): + raise IndexError("there are only {} generators".format(self.ngens())) + + return self._basis[n] + + def ngens(self): + """ + Return the number of generators of the order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order() # optional - sage.rings.finite_rings + sage: Oinf.ngens() # optional - sage.rings.finite_rings + 3 + """ + return len(self._basis) + + def free_module(self): + """ + Return the free module formed by the basis over the maximal order of the base field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: O.free_module() # optional - sage.rings.finite_rings + Free module of degree 4 and rank 4 over + Maximal order of Rational function field in x over Finite Field of size 7 + User basis matrix: + [1 0 0 0] + [0 1 0 0] + [0 0 1 0] + [0 0 0 1] + """ + return self._module.change_ring(self._module_base_ring) + + def coordinate_vector(self, e): + """ + Return the coordinates of ``e`` with respect to the basis of this order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: O.coordinate_vector(y) # optional - sage.rings.finite_rings + (0, 1, 0, 0) + sage: O.coordinate_vector(x*y) # optional - sage.rings.finite_rings + (0, x, 0, 0) + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.equation_order() + sage: f = (x + y)^3 + sage: O.coordinate_vector(f) + (x^3, 3*x^2, 3*x, 1) + """ + return self._module.coordinate_vector(self._to_module(e)) + + def _coordinate_vector(self, e): + """ + Return the coordinate vector of ``e`` with respect to the basis + of the order. + + Assuming ``e`` is in the maximal order, the coordinates are given + as univariate polynomials in the underlying ring of the maximal + order of the rational function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] + sage: L. = K.extension(y^4 + x*y + 4*x + 1) + sage: O = L.maximal_order() + sage: O._coordinate_vector(y) + (0, 1, 0, 0) + sage: O._coordinate_vector(x*y) + (0, x, 0, 0) + """ + from sage.modules.free_module_element import vector + + v = self._module.coordinate_vector(self._to_module(e), check=False) + return vector([c.numerator() for c in v]) + + @cached_method + def different(self): + """ + Return the different ideal of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: O.different() # optional - sage.rings.finite_rings + Ideal (y^3 + 2*x) + of Maximal order of Function field in y defined by y^4 + x*y + 4*x + 1 + """ + return ~self.codifferent() + + @cached_method + def codifferent(self): + """ + Return the codifferent ideal of the function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: O.codifferent() # optional - sage.rings.finite_rings + Ideal (1, (1/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y^3 + + ((5*x^3 + 6*x^2 + x + 6)/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y^2 + + ((x^3 + 2*x^2 + 2*x + 2)/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4))*y + + 6*x/(x^4 + 4*x^3 + 3*x^2 + 6*x + 4)) of Maximal order of Function field + in y defined by y^4 + x*y + 4*x + 1 + """ + T = self._codifferent_matrix() + return self._ideal_from_vectors(T.inverse().columns()) + + @cached_method + def _codifferent_matrix(self): + """ + Return the matrix `T` defined in Proposition 4.8.19 of [Coh1993]_. + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: O._codifferent_matrix() # optional - sage.rings.finite_rings + [ 4 0 0 4*x] + [ 0 0 4*x 5*x + 3] + [ 0 4*x 5*x + 3 0] + [ 4*x 5*x + 3 0 3*x^2] + """ + from sage.matrix.constructor import matrix + + rows = [] + for u in self.basis(): + row = [] + for v in self.basis(): + row.append((u*v).trace()) + rows.append(row) + T = matrix(rows) + return T + + @cached_method + def decomposition(self, ideal): + """ + Return the decomposition of the prime ideal. + + INPUT: + + - ``ideal`` -- prime ideal of the base maximal order + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: o = K.maximal_order() # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: p = o.ideal(x + 1) # optional - sage.rings.finite_rings + sage: O.decomposition(p) # optional - sage.rings.finite_rings + [(Ideal (x + 1, y + 1) of Maximal order + of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), + (Ideal (x + 1, (1/(x^3 + x^2 + x))*y^2 + y + 1) of Maximal order + of Function field in y defined by y^3 + x^6 + x^4 + x^2, 2, 1)] + + ALGORITHM: + + In principle, we're trying to compute a primary decomposition + of the extension of ``ideal`` in ``self`` (an order, and therefore + a ring). However, while we have primary decomposition methods + for polynomial rings, we lack any such method for an order. + Therefore, we construct ``self`` mod ``ideal`` as a + finite-dimensional algebra, a construct for which we do + support primary decomposition. + + See :trac:`28094` and https://github.com/sagemath/sage/files/10659303/decomposition.pdf.gz + + .. TODO:: + + Use Kummer's theorem to shortcut this code if possible, like as + done in :meth:`FunctionFieldMaximalOrder_global.decomposition()` + + """ + from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra + from sage.matrix.constructor import matrix + from sage.modules.free_module_element import vector + + F = self.function_field() + n = F.degree() + + # Base rational function field + K = self.function_field().base_field() + + # Univariate polynomial ring isomorphic to the maximal order of K + o = PolynomialRing(K.constant_field(), K.gen()) + + # Prime ideal in o defined by the generator of ideal in the maximal + # order of K + p = o(ideal.gen().numerator()) + + # Residue field k = o mod p + k = o.quo(p) + + # Given an element of the function field expressed as a K-vector times + # the basis of this order, construct the n n-by-n matrices that show + # how to multiply by each of the basis elements. + matrices = [matrix(o, [self.coordinate_vector(b1*b2) for b1 in self.basis()]) + for b2 in self.basis()] + + # Let O denote the maximal order self. When reduced modulo p, + # matrices_reduced give the multiplication matrices used to form the + # algebra O mod pO. + matrices_reduced = list(map(lambda M: M.mod(p), matrices)) + A = FiniteDimensionalAlgebra(k, matrices_reduced) + + # Each prime ideal of the algebra A corresponds to a prime ideal of O, + # and since the algebra is an Artinian ring, all of its prime ideals + # are maximal [stacks 00JA]. Thus, we find all of our factors by + # iterating over the algebra's maximal ideals. + factors = [] + for q in A.maximal_ideals(): + if q == A.zero_ideal(): + # The zero ideal is the unique maximal ideal, which means that + # A is a field, and the ideal itself is a prime ideal. + P = self.ideal(p) + + P.is_prime.set_cache(True) + P._prime_below = ideal + P._relative_degree = n + P._ramification_index = 1 + P._beta = [1] + [0]*(n-1) + else: + Q = q.basis_matrix().apply_map(lambda e: e.lift()) + P = self.ideal(p, *Q*vector(self.basis())) + + # Now we compute an element beta in O but not in pO such that + # beta*P in pO. + + # Since beta is in k[x]-module O, we keep beta as a vector + # in k[x] with respect to the basis of O. As long as at least + # one element in this vector is not divisible by p, beta will + # not be in pO. To ensure that beta*P is in pO, multiplying + # beta by each of P's generators must produce a vector whose + # elements are multiples of p. We can ensure that all this + # occurs by constructing a matrix in k, and finding a non-zero + # vector in the kernel of the matrix. + + m =[] + for g in q.basis_matrix(): + m.extend(matrix([g * mr for mr in matrices_reduced]).columns()) + beta = [c.lift() for c in matrix(m).right_kernel().basis()[0]] + + r = q + index = 1 + while True: + rq = r*q + if rq == r: + break + r = rq + index = index + 1 + + P.is_prime.set_cache(True) + P._prime_below = ideal + P._relative_degree = n - q.basis_matrix().nrows() + P._ramification_index = index + P._beta = beta + + factors.append((P, P._relative_degree, P._ramification_index)) + + return factors + + +class FunctionFieldMaximalOrderInfinite_polymod(FunctionFieldMaximalOrderInfinite): + """ + Maximal infinite orders of function fields. + + INPUT: + + - ``field`` -- function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: F.maximal_order_infinite() # optional - sage.rings.finite_rings + Maximal infinite order of Function field in y defined by y^3 + x^6 + x^4 + x^2 + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: L.maximal_order_infinite() # optional - sage.rings.finite_rings + Maximal infinite order of Function field in y defined by y^2 + y + (x^2 + 1)/x + """ + def __init__(self, field, category=None): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(2)); _. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2+x+1)^2) # optional - sage.rings.finite_rings + sage: O = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: TestSuite(O).run() # optional - sage.rings.finite_rings + """ + FunctionFieldMaximalOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_polymod) + + M, from_M, to_M = field._inversion_isomorphism() + basis = [from_M(g) for g in M.maximal_order().basis()] + + V, from_V, to_V = field.vector_space() + R = field.base_field().maximal_order_infinite() + + self._basis = tuple(basis) + self._module = V.span_of_basis([to_V(v) for v in basis]) + self._module_base_ring = R + self._to_module = to_V + + def _element_constructor_(self, f): + """ + Make ``f`` an element of this order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.basis() # optional - sage.rings.finite_rings + (1, 1/x*y) + sage: 1 in Oinf # optional - sage.rings.finite_rings + True + sage: 1/x*y in Oinf # optional - sage.rings.finite_rings + True + sage: x*y in Oinf # optional - sage.rings.finite_rings + False + sage: 1/x in Oinf # optional - sage.rings.finite_rings + True + """ + F = self.function_field() + + try: + f = F(f) + except TypeError: + raise TypeError("unable to convert to an element of {}".format(F)) + + O = F.base_field().maximal_order_infinite() + coordinates = self.coordinate_vector(f) + if not all(c in O for c in coordinates): + raise TypeError("%r is not an element of %r"%(f,self)) + + return f + + def basis(self): + """ + Return a basis of this order as a module over the maximal order + of the base function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.basis() # optional - sage.rings.finite_rings + (1, 1/x^2*y, (1/(x^4 + x^3 + x^2))*y^2) + + :: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.basis() # optional - sage.rings.finite_rings + (1, 1/x*y) + """ + return self._basis + + def gen(self, n=0): + """ + Return the ``n``-th generator of the order. + + The basis elements of the order are generators. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.gen() # optional - sage.rings.finite_rings + 1 + sage: Oinf.gen(1) # optional - sage.rings.finite_rings + 1/x^2*y + sage: Oinf.gen(2) # optional - sage.rings.finite_rings + (1/(x^4 + x^3 + x^2))*y^2 + sage: Oinf.gen(3) # optional - sage.rings.finite_rings + Traceback (most recent call last): + ... + IndexError: there are only 3 generators + """ + if not ( n >= 0 and n < self.ngens() ): + raise IndexError("there are only {} generators".format(self.ngens())) + + return self._basis[n] + + def ngens(self): + """ + Return the number of generators of the order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.ngens() # optional - sage.rings.finite_rings + 3 + """ + return len(self._basis) + + def ideal(self, *gens): + """ + Return the ideal generated by ``gens``. + + INPUT: + + - ``gens`` -- tuple of elements of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x, y); I # optional - sage.rings.finite_rings + Ideal (y) of Maximal infinite order of Function field + in y defined by y^3 + x^6 + x^4 + x^2 + + :: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(x, y); I # optional - sage.rings.finite_rings + Ideal (x) of Maximal infinite order of Function field + in y defined by y^2 + y + (x^2 + 1)/x + """ + if len(gens) == 1: + gens = gens[0] + if not type(gens) in (list,tuple): + gens = (gens,) + mgens = [g * b for g in gens for b in self._basis] + return self.ideal_with_gens_over_base(mgens) + + def ideal_with_gens_over_base(self, gens): + """ + Return the ideal generated by ``gens`` as a module. + + INPUT: + + - ``gens`` -- tuple of elements of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) + sage: Oinf = F.maximal_order_infinite() + sage: Oinf.ideal_with_gens_over_base((x^2, y, (1/(x^2 + x + 1))*y^2)) + Ideal (y) of Maximal infinite order of Function field in y + defined by y^3 + x^6 + x^4 + x^2 + """ + F = self.function_field() + iF, from_iF, to_iF = F._inversion_isomorphism() + iO = iF.maximal_order() + + ideal = iO.ideal_with_gens_over_base([to_iF(g) for g in gens]) + + if not ideal.is_zero(): + # Now the ideal does not correspond exactly to the ideal in the + # maximal infinite order through the inversion isomorphism. The + # reason is that the ideal also has factors not lying over x. + # The following procedure removes the spurious factors. The idea + # is that for an integral ideal I, J_n = I + (xO)^n stabilizes + # if n is large enough, and then J_n is the I with the spurious + # factors removed. For a fractional ideal, we also need to find + # the largest factor x^m that divides the denominator. + from sage.matrix.special import block_matrix + from .hermite_form_polynomial import reversed_hermite_form + + d = ideal.denominator() + h = ideal.hnf() + x = d.parent().gen() + + # find the largest factor x^m that divides the denominator + i = 0 + while d[i].is_zero(): + i += 1 + d = x ** i + + # find the largest n such that I + (xO)^n stabilizes + h1 = h + MS = h1.matrix_space() + k = MS.identity_matrix() + while True: + k = x * k + + h2 = block_matrix([[h],[k]]) + reversed_hermite_form(h2) + i = 0 + while i < h2.nrows() and h2.row(i).is_zero(): + i += 1 + h2 = h2[i:] # remove zero rows + + if h2 == h1: + break + h1 = h2 + + # reconstruct ideal + ideal = iO._ideal_from_vectors_and_denominator(list(h1), d) + + return self.ideal_monoid().element_class(self, ideal) + + def _to_iF(self, I): + """ + Return the ideal in the inverted function field from ``I``. + + INPUT: + + - ``I`` -- ideal of the function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: I = Oinf.ideal(y) # optional - sage.rings.finite_rings + sage: Oinf._to_iF(I) # optional - sage.rings.finite_rings + Ideal (1, 1/x*s) of Maximal order of Function field in s + defined by s^2 + x*s + x^3 + x + """ + F = self.function_field() + iF,from_iF,to_iF = F._inversion_isomorphism() + iO = iF.maximal_order() + iI = iO.ideal_with_gens_over_base([to_iF(b) for b in I.gens_over_base()]) + return iI + + def decomposition(self): + r""" + Return prime ideal decomposition of `pO_\infty` where `p` is the unique + prime ideal of the maximal infinite order of the rational function field. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: Oinf = F.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.decomposition() # optional - sage.rings.finite_rings + [(Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1) of Maximal infinite order + of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), + (Ideal ((1/(x^4 + x^3 + x^2))*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, 2, 1)] + + :: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.decomposition() # optional - sage.rings.finite_rings + [(Ideal (1/x*y) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x, 1, 2)] + + :: + + sage: K. = FunctionField(QQ); _. = K[] + sage: F. = K.extension(Y^3 - x^2*(x^2 + x + 1)^2) + sage: Oinf = F.maximal_order_infinite() + sage: Oinf.decomposition() + [(Ideal (1/x^2*y - 1) of Maximal infinite order + of Function field in y defined by y^3 - x^6 - 2*x^5 - 3*x^4 - 2*x^3 - x^2, 1, 1), + (Ideal ((1/(x^4 + x^3 + x^2))*y^2 + 1/x^2*y + 1) of Maximal infinite order + of Function field in y defined by y^3 - x^6 - 2*x^5 - 3*x^4 - 2*x^3 - x^2, 2, 1)] + + :: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^2 + Y + x + 1/x) + sage: Oinf = L.maximal_order_infinite() + sage: Oinf.decomposition() + [(Ideal (1/x*y) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x, 1, 2)] + """ + F = self.function_field() + iF,from_iF,to_iF = F._inversion_isomorphism() + + x = iF.base_field().gen() + iO = iF.maximal_order() + io = iF.base_field().maximal_order() + ip = io.ideal(x) + + dec = [] + for iprime, deg, exp in iO.decomposition(ip): + prime = self.ideal_monoid().element_class(self, iprime) + dec.append((prime, deg, exp)) + return dec + + def different(self): + """ + Return the different ideal of the maximal infinite order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf.different() # optional - sage.rings.finite_rings + Ideal (1/x) of Maximal infinite order of Function field in y + defined by y^2 + y + (x^2 + 1)/x + """ + T = self._codifferent_matrix() + codiff_gens = [] + for c in T.inverse().columns(): + codiff_gens.append(sum([ci*bi for ci,bi in zip(c,self.basis())])) + codiff = self.ideal_with_gens_over_base(codiff_gens) + return ~codiff + + @cached_method + def _codifferent_matrix(self): + """ + Return the codifferent matrix of the maximal infinite order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: Oinf._codifferent_matrix() # optional - sage.rings.finite_rings + [ 0 1/x] + [ 1/x 1/x^2] + """ + from sage.matrix.constructor import matrix + + rows = [] + for u in self.basis(): + row = [] + for v in self.basis(): + row.append((u*v).trace()) + rows.append(row) + T = matrix(rows) + return T + + def coordinate_vector(self, e): + """ + Return the coordinates of ``e`` with respect to the basis of the order. + + INPUT: + + - ``e`` -- element of the function field + + The returned coordinates are in the base maximal infinite order if and only + if the element is in the order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: Oinf = L.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: f = 1/y^2 # optional - sage.rings.finite_rings + sage: f in Oinf # optional - sage.rings.finite_rings + True + sage: Oinf.coordinate_vector(f) # optional - sage.rings.finite_rings + ((x^3 + x^2 + x)/(x^4 + 1), x^3/(x^4 + 1)) + """ + return self._module.coordinate_vector(self._to_module(e)) + + +class FunctionFieldMaximalOrder_global(FunctionFieldMaximalOrder_polymod): + """ + Maximal orders of global function fields. + + INPUT: + + - ``field`` -- function field to which this maximal order belongs + + EXAMPLES:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: L.maximal_order() # optional - sage.rings.finite_rings + Maximal order of Function field in y defined by y^4 + x*y + 4*x + 1 + """ + + def __init__(self, field): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(7)); R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^4 + x*y + 4*x + 1) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: TestSuite(O).run() # optional - sage.rings.finite_rings + """ + FunctionFieldMaximalOrder_polymod.__init__(self, field, ideal_class=FunctionFieldIdeal_global) + + @cached_method + def p_radical(self, prime): + """ + Return the ``prime``-radical of the maximal order. + + INPUT: + + - ``prime`` -- prime ideal of the maximal order of the base + rational function field + + The algorithm is outlined in Section 6.1.3 of [Coh1993]_. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2 * (x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: o = K.maximal_order() # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: p = o.ideal(x + 1) # optional - sage.rings.finite_rings + sage: O.p_radical(p) # optional - sage.rings.finite_rings + Ideal (x + 1) of Maximal order of Function field in y + defined by y^3 + x^6 + x^4 + x^2 + """ + from sage.matrix.constructor import matrix + from sage.modules.free_module_element import vector + + g = prime.gens()[0] + + if not (g.denominator() == 1 and g.numerator().is_irreducible()): + raise ValueError('not a prime ideal') + + F = self.function_field() + n = F.degree() + o = prime.ring() + p = g.numerator() + + # Fp is isomorphic to the residue field o/p + Fp, fr_Fp, to_Fp = o._residue_field_global(p) + + # exp = q^j should be at least extension degree where q is + # the order of the residue field o/p + q = F.constant_base_field().order()**p.degree() + exp = q + while exp <= F.degree(): + exp = exp**q + + # radical equals to the kernel of the map x |-> x^exp + mat = [] + for g in self.basis(): + v = [to_Fp(c) for c in self._coordinate_vector(g**exp)] + mat.append(v) + mat = matrix(Fp, mat) + ker = mat.kernel() + + # construct module generators of the p-radical + vecs = [] + for i in range(n): + v = vector([p if j == i else 0 for j in range(n)]) + vecs.append(v) + for b in ker.basis(): + v = vector([fr_Fp(c) for c in b]) + vecs.append(v) + + return self._ideal_from_vectors(vecs) + + @cached_method + def decomposition(self, ideal): + """ + Return the decomposition of the prime ideal. + + INPUT: + + - ``ideal`` -- prime ideal of the base maximal order + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); R. = K[] # optional - sage.rings.finite_rings + sage: F. = K.extension(t^3 - x^2*(x^2 + x + 1)^2) # optional - sage.rings.finite_rings + sage: o = K.maximal_order() # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: p = o.ideal(x + 1) # optional - sage.rings.finite_rings + sage: O.decomposition(p) # optional - sage.rings.finite_rings + [(Ideal (x + 1, y + 1) of Maximal order + of Function field in y defined by y^3 + x^6 + x^4 + x^2, 1, 1), + (Ideal (x + 1, (1/(x^3 + x^2 + x))*y^2 + y + 1) of Maximal order + of Function field in y defined by y^3 + x^6 + x^4 + x^2, 2, 1)] + """ + from sage.matrix.constructor import matrix + + F = self.function_field() + n = F.degree() + + p = ideal.gen().numerator() + o = ideal.ring() + + # Fp is isomorphic to the residue field o/p + Fp, fr, to = o._residue_field_global(p) + P,X = Fp['X'].objgen() + + V = Fp**n # Ob = O/pO + + mtable = [] + for i in range(n): + row = [] + for j in range(n): + row.append( V([to(e) for e in self._mtable[i][j]]) ) + mtable.append(row) + + if p not in self._kummer_places: + ##################################### + # Decomposition by Kummer's theorem # + ##################################### + # gen is self._kummer_gen + gen_vec_pow = self._kummer_gen_vec_pow + mul_vecs = self._mul_vecs + + f = self._kummer_polynomial + fp = P([to(c.numerator()) for c in f.list()]) + decomposition = [] + for q, exp in fp.factor(): + # construct O.ideal([p,q(gen)]) + gen_vecs = list(matrix.diagonal(n * [p])) + c = q.list() + + # q(gen) in vector form + qgen = sum(fr(c[i]) * gen_vec_pow[i] for i in range(len(c))) + + I = matrix.identity(o._ring, n) + for i in range(n): + gen_vecs.append(mul_vecs(qgen,I[i])) + prime = self._ideal_from_vectors_and_denominator(gen_vecs) + + # Compute an element beta in O but not in pO. How to find beta + # is explained in Section 4.8.3 of [Coh1993]. We keep beta + # as a vector over k[x] with respect to the basis of O. + + # p and qgen generates the prime; modulo pO, qgenb generates the prime + qgenb = [to(qgen[i]) for i in range(n)] + m =[] + for i in range(n): + m.append(sum(qgenb[j] * mtable[i][j] for j in range(n))) + beta = [fr(coeff) for coeff in matrix(m).left_kernel().basis()[0]] + + prime.is_prime.set_cache(True) + prime._prime_below = ideal + prime._relative_degree = q.degree() + prime._ramification_index = exp + prime._beta = beta + + prime._kummer_form = (p, qgen) + + decomposition.append((prime, q.degree(), exp)) + else: + ############################# + # Buchman-Lenstra algorithm # + ############################# + from sage.matrix.special import block_matrix + from sage.modules.free_module_element import vector + + pO = self.ideal(p) + Ip = self.p_radical(ideal) + Ob = matrix.identity(Fp, n) + + def bar(I): # transfer to O/pO + m = [] + for v in I._hnf: + m.append([to(e) for e in v]) + h = matrix(m).echelon_form() + return cut_last_zero_rows(h) + + def liftb(Ib): + m = [vector([fr(e) for e in v]) for v in Ib] + m += [v for v in pO._hnf] + return self._ideal_from_vectors_and_denominator(m,1) + + def cut_last_zero_rows(h): + i = h.nrows() + while i > 0 and h.row(i-1).is_zero(): + i -= 1 + return h[:i] + + def mul_vec(v1,v2): + s = 0 + for i in range(n): + for j in range(n): + s += v1[i] * v2[j] * mtable[i][j] + return s + + def pow(v, r): # r > 0 + m = v + while r > 1: + m = mul_vec(m,v) + r -= 1 + return m + + # Algorithm 6.2.7 of [Coh1993] + def div(Ib, Jb): + # compute a basis of Jb/Ib + sJb = Jb.row_space() + sIb = Ib.row_space() + sJbsIb,proj_sJbsIb,lift_sJbsIb = sJb.quotient_abstract(sIb) + supplement_basis = [lift_sJbsIb(v) for v in sJbsIb.basis()] + + m = [] + for b in V.gens(): # basis of Ob = O/pO + b_row = [] # row vector representation of the map a -> a*b + for a in supplement_basis: + b_row += lift_sJbsIb(proj_sJbsIb( mul_vec(a,b) )) + m.append(b_row) + return matrix(Fp,n,m).left_kernel().basis_matrix() + + # Algorithm 6.2.5 of [Coh1993] + def mul(Ib, Jb): + m = [] + for v1 in Ib: + for v2 in Jb: + m.append(mul_vec(v1,v2)) + h = matrix(m).echelon_form() + return cut_last_zero_rows(h) + + def add(Ib,Jb): + m = block_matrix([[Ib], [Jb]]) + h = m.echelon_form() + return cut_last_zero_rows(h) + + # K_1, K_2, ... + Lb = IpOb = bar(Ip+pO) + Kb = [Lb] + while not Lb.is_zero(): + Lb = mul(Lb,IpOb) + Kb.append(Lb) + + # J_1, J_2, ... + Jb =[Kb[0]] + [div(Kb[j],Kb[j-1]) for j in range(1,len(Kb))] + + # H_1, H_2, ... + Hb = [div(Jb[j],Jb[j+1]) for j in range(len(Jb)-1)] + [Jb[-1]] + + q = Fp.order() + + def split(h): + # VsW represents O/H as a vector space + W = h.row_space() # H/pO + VsW,to_VsW,lift_to_V = V.quotient_abstract(W) + + # compute the space K of elements in O/H that satisfy a^q-a=0 + l = [lift_to_V(b) for b in VsW.basis()] + + images = [to_VsW(pow(x, q) - x) for x in l] + K = VsW.hom(images, VsW).kernel() + + if K.dimension() == 0: + return [] + if K.dimension() == 1: # h is prime + return [(liftb(h),VsW.dimension())] # relative degree + + # choose a such that a^q - a is 0 but a is not in Fp + for a in K.basis(): + # IMPORTANT: This criterion is based on the assumption + # that O.basis() starts with 1. + if a.support() != [0]: + break + else: + raise AssertionError("no appropriate value found") + + a = lift_to_V(a) + # compute the minimal polynomial of a + m = [to_VsW(Ob[0])] # 1 in VsW + apow = a + while True: + v = to_VsW(apow) + try: + sol = matrix(m).solve_left(v) + except ValueError: + m.append(v) + apow = mul_vec(apow, a) + continue + break + + minpol = X**len(sol) - P(list(sol)) + + # The minimal polynomial of a has only linear factors and at least two + # of them. We set f to the first factor and g to the product of the rest. + fac = minpol.factor() + f = fac[0][0] + g = (fac/f).expand() + d,u,v = f.xgcd(g) + + assert d == 1, "Not relatively prime {} and {}".format(f,g) + + # finally, idempotent! + e = lift_to_V(sum([c1*c2 for c1,c2 in zip(u*f,m)])) + + h1 = add(h, matrix([mul_vec(e,Ob[i]) for i in range(n)])) + h2 = add(h, matrix([mul_vec(Ob[0]-e,Ob[i]) for i in range(n)])) + + return split(h1) + split(h2) + + decomposition = [] + for i in range(len(Hb)): + index = i + 1 # Hb starts with H_1 + for prime, degree in split(Hb[i]): + # Compute an element beta in O but not in pO. How to find beta + # is explained in Section 4.8.3 of [Coh1993]. We keep beta + # as a vector over k[x] with respect to the basis of O. + m =[] + for i in range(n): + r = [] + for g in prime._hnf: + r += sum(to(g[j]) * mtable[i][j] for j in range(n)) + m.append(r) + beta = [fr(e) for e in matrix(m).left_kernel().basis()[0]] + + prime.is_prime.set_cache(True) + prime._prime_below = ideal + prime._relative_degree = degree + prime._ramification_index = index + prime._beta = beta + + decomposition.append((prime, degree, index)) + + return decomposition diff --git a/src/sage/rings/function_field/order_rational.py b/src/sage/rings/function_field/order_rational.py new file mode 100644 index 00000000000..d421b64e67e --- /dev/null +++ b/src/sage/rings/function_field/order_rational.py @@ -0,0 +1,567 @@ +r""" +Orders of function fields: rational +""" + +#***************************************************************************** +# Copyright (C) 2023 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/ +#***************************************************************************** + +import sage.rings.abc + +from sage.arith.functions import lcm +from sage.arith.misc import GCD as gcd +from sage.categories.euclidean_domains import EuclideanDomains +from sage.categories.principal_ideal_domains import PrincipalIdealDomains +from sage.rings.number_field.number_field_base import NumberField + +from .ideal import FunctionFieldIdeal +from .ideal_rational import FunctionFieldIdeal_rational, FunctionFieldIdealInfinite_rational +from .order import FunctionFieldMaximalOrder, FunctionFieldMaximalOrderInfinite + + +class FunctionFieldMaximalOrder_rational(FunctionFieldMaximalOrder): + """ + Maximal orders of rational function fields. + + INPUT: + + - ``field`` -- a function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(19)); K # optional - sage.rings.finite_rings + Rational function field in t over Finite Field of size 19 + sage: R = K.maximal_order(); R # optional - sage.rings.finite_rings + Maximal order of Rational function field in t over Finite Field of size 19 + """ + def __init__(self, field): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: TestSuite(O).run(skip='_test_gcd_vs_xgcd') + """ + FunctionFieldMaximalOrder.__init__(self, field, ideal_class=FunctionFieldIdeal_rational, + category=EuclideanDomains()) + + self._populate_coercion_lists_(coerce_list=[field._ring]) + + self._ring = field._ring + self._gen = self(self._ring.gen()) + self._basis = (self.one(),) + + def _element_constructor_(self, f): + """ + Make ``f`` a function field element of this order. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: O._element_constructor_(y) + y + sage: O._element_constructor_(1/y) + Traceback (most recent call last): + ... + TypeError: 1/y is not an element of Maximal order of Rational function field in y over Rational Field + """ + F = self.function_field() + try: + f = F(f) + except TypeError: + raise TypeError("unable to convert to an element of {}".format(F)) + + if not f.denominator() in self.function_field().constant_base_field(): + raise TypeError("%r is not an element of %r"%(f,self)) + + return f + + def ideal_with_gens_over_base(self, gens): + """ + Return the fractional ideal with generators ``gens``. + + INPUT: + + - ``gens`` -- elements of the function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ); R. = K[] + sage: L. = K.extension(y^2 - x^3 - 1) # optional - sage.rings.function_field + sage: O = L.equation_order() # optional - sage.rings.function_field + sage: O.ideal_with_gens_over_base([x^3 + 1, -y]) # optional - sage.rings.function_field + Ideal (x^3 + 1, -y) of Order in Function field in y defined by y^2 - x^3 - 1 + """ + return self.ideal(gens) + + def _residue_field(self, ideal, name=None): + """ + Return a field isomorphic to the residue field at the prime ideal. + + The residue field is by definition `k[x]/q` where `q` is the irreducible + polynomial generating the prime ideal and `k` is the constant base field. + + INPUT: + + - ``ideal`` -- prime ideal of the order + + - ``name`` -- string; name of the generator of the residue field + + OUTPUT: + + - a field isomorphic to the residue field + + - a morphism from the field to `k[x]` via the residue field + + - a morphism from `k[x]` to the field via the residue field + + EXAMPLES:: + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: R, fr_R, to_R = O._residue_field(I) # optional - sage.rings.finite_rings + sage: R # optional - sage.rings.finite_rings + Finite Field in z2 of size 2^2 + sage: [to_R(fr_R(e)) == e for e in R] # optional - sage.rings.finite_rings + [True, True, True, True] + sage: [to_R(fr_R(e)).parent() is R for e in R] # optional - sage.rings.finite_rings + [True, True, True, True] + sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) # optional - sage.rings.finite_rings + sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) # optional - sage.rings.finite_rings + True + sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) # optional - sage.rings.finite_rings + True + sage: to_R(e1).parent() is R # optional - sage.rings.finite_rings + True + sage: to_R(e2).parent() is R # optional - sage.rings.finite_rings + True + + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: I = O.ideal(x + 1) # optional - sage.rings.finite_rings + sage: R, fr_R, to_R = O._residue_field(I) # optional - sage.rings.finite_rings + sage: R # optional - sage.rings.finite_rings + Finite Field of size 2 + sage: [to_R(fr_R(e)) == e for e in R] # optional - sage.rings.finite_rings + [True, True] + sage: [to_R(fr_R(e)).parent() is R for e in R] # optional - sage.rings.finite_rings + [True, True] + sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) # optional - sage.rings.finite_rings + sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) # optional - sage.rings.finite_rings + True + sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) # optional - sage.rings.finite_rings + True + sage: to_R(e1).parent() is R # optional - sage.rings.finite_rings + True + sage: to_R(e2).parent() is R # optional - sage.rings.finite_rings + True + + sage: F. = FunctionField(QQ) + sage: O = F.maximal_order() + sage: I = O.ideal(x^2 + x + 1) + sage: R, fr_R, to_R = O._residue_field(I) # optional - sage.rings.number_field + sage: R # optional - sage.rings.number_field + Number Field in a with defining polynomial x^2 + x + 1 + sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) # optional - sage.rings.number_field + sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) # optional - sage.rings.number_field + True + sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) # optional - sage.rings.number_field + True + sage: to_R(e1).parent() is R # optional - sage.rings.number_field + True + sage: to_R(e2).parent() is R # optional - sage.rings.number_field + True + + sage: F. = FunctionField(QQ) + sage: O = F.maximal_order() + sage: I = O.ideal(x + 1) + sage: R, fr_R, to_R = O._residue_field(I) + sage: R + Rational Field + sage: e1, e2 = fr_R(R.random_element()), fr_R(R.random_element()) + sage: to_R(e1 * e2) == to_R(e1) * to_R(e2) + True + sage: to_R(e1 + e2) == to_R(e1) + to_R(e2) + True + sage: to_R(e1).parent() is R + True + sage: to_R(e2).parent() is R + True + """ + F = self.function_field() + K = F.constant_base_field() + + q = ideal.gen().element().numerator() + + if F.is_global(): + R, _from_R, _to_R = self._residue_field_global(q, name=name) + elif isinstance(K, (NumberField, sage.rings.abc.AlgebraicField)): + if name is None: + name = 'a' + if q.degree() == 1: + R = K + _from_R = lambda e: e + _to_R = lambda e: R(e % q) + else: + R = K.extension(q, names=name) + _from_R = lambda e: self._ring(list(R(e))) + _to_R = lambda e: (e % q)(R.gen(0)) + else: + raise NotImplementedError + + def from_R(e): + return F(_from_R(e)) + + def to_R(f): + return _to_R(f.numerator()) + + return R, from_R, to_R + + def _residue_field_global(self, q, name=None): + """ + Return a finite field isomorphic to the residue field at q. + + This method assumes a global rational function field, that is, + the constant base field is a finite field. + + INPUT: + + - ``q`` -- irreducible polynomial + + - ``name`` -- string; name of the generator of the extension field + + OUTPUT: + + - a finite field + + - a function that outputs a polynomial lifting a finite field element + + - a function that outputs a finite field element for a polynomial + + The residue field is by definition `k[x]/q` where `k` is the base field. + + EXAMPLES:: + + sage: k. = GF(4) # optional - sage.rings.finite_rings + sage: F. = FunctionField(k) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: O._ring # optional - sage.rings.finite_rings + Univariate Polynomial Ring in x over Finite Field in a of size 2^2 + sage: f = x^3 + x + 1 # optional - sage.rings.finite_rings + sage: _f = f.numerator() # optional - sage.rings.finite_rings + sage: _f.is_irreducible() # optional - sage.rings.finite_rings + True + sage: K, fr_K, to_K = O._residue_field_global(_f) # optional - sage.rings.finite_rings sage.modules + sage: K # optional - sage.rings.finite_rings sage.modules + Finite Field in z6 of size 2^6 + sage: all(to_K(fr_K(e)) == e for e in K) # optional - sage.rings.finite_rings sage.modules + True + + sage: k. = GF(2) # optional - sage.rings.finite_rings + sage: F. = FunctionField(k) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: O._ring # optional - sage.rings.finite_rings + Univariate Polynomial Ring in x over Finite Field of size 2 (using GF2X) + sage: f = x^3 + x + 1 # optional - sage.rings.finite_rings + sage: _f = f.numerator() # optional - sage.rings.finite_rings + sage: _f.is_irreducible() # optional - sage.rings.finite_rings + True + sage: K, fr_K, to_K = O._residue_field_global(_f) # optional - sage.rings.finite_rings sage.modules + sage: all(to_K(fr_K(e)) == e for e in K) # optional - sage.rings.finite_rings sage.modules + True + + """ + # polynomial ring over the base field + R = self._ring + + # base field of extension degree r over the prime field + k = R.base_ring() + a = k.gen() + r = k.degree() + + # extend the base field to a field of degree r*s over the + # prime field + s = q.degree() + K,sigma = k.extension(s, map=True, name=name) + + # find a root beta in K satisfying the irreducible q + S = K['X'] + beta = S([sigma(c) for c in q.list()]).roots()[0][0] + + # V is a vector space over the prime subfield of k of degree r*s + V = K.vector_space(map=False) + + w = K.one() + beta_pow = [] + for i in range(s): + beta_pow.append(w) + w *= beta + + w = K.one() + sigma_a = sigma(a) + sigma_a_pow = [] + for i in range(r): + sigma_a_pow.append(w) + w *= sigma_a + + basis = [V(sap * bp) for bp in beta_pow for sap in sigma_a_pow] + W = V.span_of_basis(basis) + + def to_K(f): + coeffs = (f % q).list() + return sum((sigma(c) * beta_pow[i] for i, c in enumerate(coeffs)), K.zero()) + + if r == 1: # take care of the prime field case + def fr_K(g): + co = W.coordinates(V(g), check=False) + return R([k(co[j]) for j in range(s)]) + else: + def fr_K(g): + co = W.coordinates(V(g), check=False) + return R([k(co[i:i+r]) for i in range(0, r*s, r)]) + + return K, fr_K, to_K + + def basis(self): + """ + Return the basis (=1) of the order as a module over the polynomial ring. + + EXAMPLES:: + + sage: K. = FunctionField(GF(19)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: O.basis() # optional - sage.rings.finite_rings + (1,) + """ + return self._basis + + def gen(self, n=0): + """ + Return the ``n``-th generator of the order. Since there is only one generator ``n`` must be 0. + + EXAMPLES:: + + sage: O = FunctionField(QQ,'y').maximal_order() + sage: O.gen() + y + sage: O.gen(1) + Traceback (most recent call last): + ... + IndexError: there is only one generator + """ + if n != 0: + raise IndexError("there is only one generator") + return self._gen + + def ngens(self): + """ + Return 1 the number of generators of the order. + + EXAMPLES:: + + sage: FunctionField(QQ,'y').maximal_order().ngens() + 1 + """ + return 1 + + def ideal(self, *gens): + """ + Return the fractional ideal generated by ``gens``. + + INPUT: + + - ``gens`` -- elements of the function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: O.ideal(x) + Ideal (x) of Maximal order of Rational function field in x over Rational Field + sage: O.ideal([x, 1/x]) == O.ideal(x, 1/x) # multiple generators may be given as a list + True + sage: O.ideal(x^3 + 1, x^3 + 6) + Ideal (1) of Maximal order of Rational function field in x over Rational Field + sage: I = O.ideal((x^2+1)*(x^3+1), (x^3+6)*(x^2+1)); I + Ideal (x^2 + 1) of Maximal order of Rational function field in x over Rational Field + sage: O.ideal(I) + Ideal (x^2 + 1) of Maximal order of Rational function field in x over Rational Field + """ + if len(gens) == 1: + gens = gens[0] + if not isinstance(gens, (list, tuple)): + if isinstance(gens, FunctionFieldIdeal): + gens = gens.gens() + else: + gens = (gens,) + K = self.function_field() + gens = [K(e) for e in gens if e != 0] + if len(gens) == 0: + gen = K(0) + else: + d = lcm([c.denominator() for c in gens]).monic() + g = gcd([(d*c).numerator() for c in gens]).monic() + gen = K(g/d) + + return self.ideal_monoid().element_class(self, gen) + + +class FunctionFieldMaximalOrderInfinite_rational(FunctionFieldMaximalOrderInfinite): + """ + Maximal infinite orders of rational function fields. + + INPUT: + + - ``field`` -- a rational function field + + EXAMPLES:: + + sage: K. = FunctionField(GF(19)); K # optional - sage.rings.finite_rings + Rational function field in t over Finite Field of size 19 + sage: R = K.maximal_order_infinite(); R # optional - sage.rings.finite_rings + Maximal infinite order of Rational function field in t over Finite Field of size 19 + """ + def __init__(self, field, category=None): + """ + Initialize. + + TESTS:: + + sage: K. = FunctionField(GF(19)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: TestSuite(O).run(skip='_test_gcd_vs_xgcd') # optional - sage.rings.finite_rings + """ + FunctionFieldMaximalOrderInfinite.__init__(self, field, ideal_class=FunctionFieldIdealInfinite_rational, + category=PrincipalIdealDomains().or_subcategory(category)) + self._populate_coercion_lists_(coerce_list=[field.constant_base_field()]) + + def _element_constructor_(self, f): + """ + Make ``f`` an element of this order. + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order() + sage: O._element_constructor_(y) + y + sage: O._element_constructor_(1/y) + Traceback (most recent call last): + ... + TypeError: 1/y is not an element of Maximal order of Rational function field in y over Rational Field + """ + F = self.function_field() + try: + f = F(f) + except TypeError: + raise TypeError("unable to convert to an element of {}".format(F)) + + if f.denominator().degree() < f.numerator().degree(): + raise TypeError("{} is not an element of {}".format(f, self)) + + return f + + def basis(self): + """ + Return the basis (=1) of the order as a module over the polynomial ring. + + EXAMPLES:: + + sage: K. = FunctionField(GF(19)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order() # optional - sage.rings.finite_rings + sage: O.basis() # optional - sage.rings.finite_rings + (1,) + """ + return 1/self.function_field().gen() + + def gen(self, n=0): + """ + Return the ``n``-th generator of self. Since there is only one generator ``n`` must be 0. + + EXAMPLES:: + + sage: O = FunctionField(QQ,'y').maximal_order() + sage: O.gen() + y + sage: O.gen(1) + Traceback (most recent call last): + ... + IndexError: there is only one generator + """ + if n != 0: + raise IndexError("there is only one generator") + return self._gen + + def ngens(self): + """ + Return 1 the number of generators of the order. + + EXAMPLES:: + + sage: FunctionField(QQ,'y').maximal_order().ngens() + 1 + """ + return 1 + + def prime_ideal(self): + """ + Return the unique prime ideal of the order. + + EXAMPLES:: + + sage: K. = FunctionField(GF(19)) # optional - sage.rings.finite_rings + sage: O = K.maximal_order_infinite() # optional - sage.rings.finite_rings + sage: O.prime_ideal() # optional - sage.rings.finite_rings + Ideal (1/t) of Maximal infinite order of Rational function field in t + over Finite Field of size 19 + """ + return self.ideal( 1/self.function_field().gen() ) + + def ideal(self, *gens): + """ + Return the fractional ideal generated by ``gens``. + + INPUT: + + - ``gens`` -- elements of the function field + + EXAMPLES:: + + sage: K. = FunctionField(QQ) + sage: O = K.maximal_order_infinite() + sage: O.ideal(x) + Ideal (x) of Maximal infinite order of Rational function field in x over Rational Field + sage: O.ideal([x, 1/x]) == O.ideal(x ,1/x) # multiple generators may be given as a list + True + sage: O.ideal(x^3 + 1, x^3 + 6) + Ideal (x^3) of Maximal infinite order of Rational function field in x over Rational Field + sage: I = O.ideal((x^2+1)*(x^3+1), (x^3+6)*(x^2+1)); I + Ideal (x^5) of Maximal infinite order of Rational function field in x over Rational Field + sage: O.ideal(I) + Ideal (x^5) of Maximal infinite order of Rational function field in x over Rational Field + """ + if len(gens) == 1: + gens = gens[0] + if not isinstance(gens, (list, tuple)): + if isinstance(gens, FunctionFieldIdeal): + gens = gens.gens() + else: + gens = (gens,) + K = self.function_field() + gens = [K(g) for g in gens] + try: + d = max(g.numerator().degree() - g.denominator().degree() for g in gens if g != 0) + gen = K.gen() ** d + except ValueError: # all gens are zero + gen = K(0) + + return self.ideal_monoid().element_class(self, gen) diff --git a/src/sage/rings/function_field/place.py b/src/sage/rings/function_field/place.py index 69993a4b744..685af63a571 100644 --- a/src/sage/rings/function_field/place.py +++ b/src/sage/rings/function_field/place.py @@ -11,9 +11,9 @@ All rational places of a function field can be computed:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x + x^3*Y) - sage: L.places() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.places() # optional - sage.rings.finite_rings sage.rings.function_field [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)] @@ -21,20 +21,20 @@ 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 + sage: F. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings + sage: p = O.ideal(x^2 + x + 1).place() # optional - sage.rings.finite_rings + sage: k, fr_k, to_k = p.residue_field() # optional - sage.rings.finite_rings + sage: k # optional - sage.rings.finite_rings Finite Field in z2 of size 2^2 The homomorphisms are between the valuation ring and the residue field:: - sage: fr_k + sage: fr_k # optional - sage.rings.finite_rings Ring morphism: From: Finite Field in z2 of size 2^2 To: Valuation ring at Place (x^2 + x + 1) - sage: to_k + sage: to_k # optional - sage.rings.finite_rings Ring morphism: From: Valuation ring at Place (x^2 + x + 1) To: Finite Field in z2 of size 2^2 @@ -46,8 +46,11 @@ - Brent Baccala (2019-12-20): function fields of characteristic zero """ + #***************************************************************************** -# Copyright (C) 2016 Kwankyu Lee +# Copyright (C) 2016-2022 Kwankyu Lee +# 2019 Brent Baccala +# 2021 Jonathan Kliem # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of @@ -55,25 +58,12 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.misc.cachefunc import cached_method - -from sage.arith.functions import lcm - -from sage.rings.integer_ring import ZZ -from sage.rings.qqbar import QQbar -from sage.rings.number_field.number_field_base import NumberField - 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 - -from sage.matrix.constructor import matrix - class FunctionFieldPlace(Element): """ @@ -87,9 +77,9 @@ class FunctionFieldPlace(Element): EXAMPLES:: - sage: K.=FunctionField(GF(2)); _. = K[] - sage: L.=K.extension(Y^3 + x + x^3*Y) - sage: L.places_finite()[0] + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field Place (x, y) """ def __init__(self, parent, prime): @@ -98,10 +88,10 @@ def __init__(self, parent, prime): 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() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(p).run() # optional - sage.rings.finite_rings sage.rings.function_field """ Element.__init__(self, parent) @@ -113,10 +103,10 @@ def __hash__(self): 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} + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: {p: 1} # optional - sage.rings.finite_rings sage.rings.function_field {Place (x, y): 1} """ return hash((self.function_field(), self._prime)) @@ -127,10 +117,10 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: p = L.places_finite()[0] - sage: p + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p # optional - sage.rings.finite_rings sage.rings.function_field Place (x, y) """ try: @@ -146,10 +136,10 @@ def _latex_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3+x+x^3*Y) - sage: p = L.places_finite()[0] - sage: latex(p) + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: latex(p) # optional - sage.rings.finite_rings sage.rings.function_field \left(y\right) """ return self._prime._latex_() @@ -160,14 +150,14 @@ def _richcmp_(self, other, op): 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 + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x + x^3*Y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1, p2, p3 = L.places()[:3] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1 < p2 # optional - sage.rings.finite_rings sage.rings.function_field True - sage: p2 < p1 + sage: p2 < p1 # optional - sage.rings.finite_rings sage.rings.function_field False - sage: p1 == p3 + sage: p1 == p3 # optional - sage.rings.finite_rings sage.rings.function_field False """ from sage.rings.function_field.order import FunctionFieldOrderInfinite @@ -186,12 +176,12 @@ def _acted_upon_(self, other, self_on_left): EXAMPLES:: - sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) - sage: F. = K.extension(Y^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 + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings + sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(x + 1, y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: P = I.place() # optional - sage.rings.finite_rings sage.rings.function_field + sage: -3*P + 5*P # optional - sage.rings.finite_rings sage.rings.function_field 2*Place (x + 1, y) """ if self_on_left: @@ -204,10 +194,10 @@ def _neg_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: p1, p2, p3 = L.places()[:3] - sage: -p1 + p2 + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1, p2, p3 = L.places()[:3] # optional - sage.rings.finite_rings sage.rings.function_field + sage: -p1 + p2 # optional - sage.rings.finite_rings sage.rings.function_field - Place (1/x, 1/x^3*y^2 + 1/x) + Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) """ @@ -220,10 +210,10 @@ def _add_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: p1, p2, p3 = L.places()[:3] - sage: p1 + p2 + p3 + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1, p2, p3 = L.places()[:3] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1 + p2 + p3 # optional - sage.rings.finite_rings sage.rings.function_field 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) @@ -237,10 +227,10 @@ def _sub_(self, other): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: p1, p2 = L.places()[:2] - sage: p1 - p2 + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1, p2 = L.places()[:2] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p1 - p2 # optional - sage.rings.finite_rings sage.rings.function_field Place (1/x, 1/x^3*y^2 + 1/x) - Place (1/x, 1/x^3*y^2 + 1/x^2*y + 1) """ @@ -256,14 +246,14 @@ def __radd__(self, other): EXAMPLES:: - sage: k. = GF(2) - sage: K. = FunctionField(k) - sage: sum(K.places_finite()) + sage: k. = GF(2) # optional - sage.rings.finite_rings + sage: K. = FunctionField(k) # optional - sage.rings.finite_rings + sage: sum(K.places_finite()) # optional - sage.rings.finite_rings Place (x) + Place (x + 1) Note that this does not work, as wanted:: - sage: 0 + K.place_infinite() + sage: 0 + K.place_infinite() # optional - sage.rings.finite_rings Traceback (most recent call last): ... TypeError: unsupported operand parent(s) for +: ... @@ -282,10 +272,10 @@ def function_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: p = L.places()[0] - sage: p.function_field() == L + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p.function_field() == L # optional - sage.rings.finite_rings sage.rings.function_field True """ return self.parent()._field @@ -296,10 +286,10 @@ def prime_ideal(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: p = L.places()[0] - sage: p.prime_ideal() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: p = L.places()[0] # optional - sage.rings.finite_rings sage.rings.function_field + sage: p.prime_ideal() # optional - sage.rings.finite_rings sage.rings.function_field Ideal (1/x^3*y^2 + 1/x) of Maximal infinite order of Function field in y defined by y^3 + x^3*y + x """ @@ -311,816 +301,18 @@ def divisor(self, multiplicity=1): EXAMPLES:: - sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) - sage: F. = K.extension(Y^2 - x^3 - 1) - sage: O = F.maximal_order() - sage: I = O.ideal(x + 1,y) - sage: P = I.place() - sage: P.divisor() + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) # optional - sage.rings.finite_rings + sage: F. = K.extension(Y^2 - x^3 - 1) # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = F.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: I = O.ideal(x + 1, y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: P = I.place() # optional - sage.rings.finite_rings sage.rings.function_field + sage: P.divisor() # optional - sage.rings.finite_rings sage.rings.function_field 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 fields. - """ - 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 - - return FunctionFieldValuationRing(self.function_field(), self) - - -class FunctionFieldPlace_polymod(FunctionFieldPlace): - """ - Places of extensions 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^3*Y + x) - 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^3*Y + x) - 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^3*Y + x) - 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^3*Y + x) - 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 gaps(self): - """ - Return the gap sequence for the place. - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: O = L.maximal_order() - sage: p = O.ideal(x,y).place() - sage: p.gaps() # a Weierstrass place - [1, 2, 4] - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3 * Y + x) - sage: [p.gaps() for p in L.places()] - [[1, 2, 4], [1, 2, 4], [1, 2, 4]] - """ - if self.degree() == 1: - return self._gaps_rational() # faster for rational places - else: - return self._gaps_wronskian() - - def _gaps_rational(self): - """ - Return the gap sequence for the rational place. - - This method computes the gap numbers using the definition of gap - numbers. The dimension of the multiple of the prime divisor - supported at the place is computed by Hess' algorithm. - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: O = L.maximal_order() - sage: p = O.ideal(x,y).place() - sage: p.gaps() # indirect doctest - [1, 2, 4] - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: [p.gaps() for p in L.places()] # indirect doctest - [[1, 2, 4], [1, 2, 4], [1, 2, 4]] - """ - F = self.function_field() - n = F.degree() - O = F.maximal_order() - Oinf = F.maximal_order_infinite() - - R = O._module_base_ring._ring - one = R.one() - - # Hess' Riemann-Roch basis algorithm stripped down for gaps computation - def dim_RR(M): - den = lcm([e.denominator() for e in M.list()]) - mat = matrix(R, M.nrows(), [(den*e).numerator() for e in M.list()]) - - # 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) - - 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) - - dim = 0 - for j in range(n): - i,ideg = pivot_row[j][0] - k = den.degree() - ideg + 1 - if k > 0: - dim += k - return dim - - V,fr,to = F.vector_space() - - prime_inv = ~ self.prime_ideal() - I = O.ideal(1) - J = Oinf.ideal(1) - - B = matrix([to(b) for b in J.gens_over_base()]) - C = matrix([to(v) for v in I.gens_over_base()]) - - prev = dim_RR(C * B.inverse()) - gaps = [] - g = F.genus() - i = 1 - if self.is_infinite_place(): - while g: - J = J * prime_inv - B = matrix([to(b) for b in J.gens_over_base()]) - dim = dim_RR(C * B.inverse()) - if dim == prev: - gaps.append(i) - g -= 1 - else: - prev = dim - i += 1 - else: # self is a finite place - Binv = B.inverse() - while g: - I = I * prime_inv - C = matrix([to(v) for v in I.gens_over_base()]) - dim = dim_RR(C * Binv) - if dim == prev: - gaps.append(i) - g -= 1 - else: - prev = dim - i += 1 - - return gaps - - def _gaps_wronskian(self): - """ - Return the gap sequence for the place. - - This method implements the local version of Hess' Algorithm 30 of [Hes2002b]_ - based on the Wronskian determinant. - - EXAMPLES:: - - sage: K. = FunctionField(GF(4)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: O = L.maximal_order() - sage: p = O.ideal(x,y).place() - sage: p._gaps_wronskian() # a Weierstrass place - [1, 2, 4] - - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3 * Y + x) - sage: [p._gaps_wronskian() for p in L.places()] - [[1, 2, 4], [1, 2, 4], [1, 2, 4]] - """ - F = self.function_field() - R,fr_R,to_R = self._residue_field() - der = F.higher_derivation() - - sep = self.local_uniformizer() - - # a differential divisor satisfying - # v_p(W) = 0 for the place p - W = sep.differential().divisor() - - # Step 3: - basis = W._basis() - d = len(basis) - M = matrix([to_R(b) for b in basis]) - if M.rank() == 0: - return [] - - # Steps 4, 5, 6, 7: - e = 1 - gaps = [1] - while M.nrows() < d: - row = vector([to_R(der._derive(basis[i], e, sep)) for i in range(d)]) - if row not in M.row_space(): - M = matrix(M.rows() + [row]) - M.echelonize() - gaps.append(e + 1) - e += 1 - - return gaps - - def residue_field(self, name=None): - """ - Return the residue field of the place. - - INPUT: - - - ``name`` -- string; name of the generator of the residue field - - OUTPUT: - - - a field isomorphic to the residue field - - - a ring homomorphism from the valuation ring to the field - - - a ring homomorphism from the 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: 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) - - :: - - sage: K. = FunctionField(QQ); _. = K[] - sage: L. = K.extension(Y^3 + Y - x^4) - sage: O = K.maximal_order() - sage: I = O.ideal(x) - sage: [p.residue_field() for p in L.places_above(I.place())] - [(Rational Field, Ring morphism: - From: Rational Field - To: Valuation ring at Place (x, y, y^2), Ring morphism: - From: Valuation ring at Place (x, y, y^2) - To: Rational Field), - (Number Field in s with defining polynomial x^2 - 2*x + 2, Ring morphism: - From: Number Field in s with defining polynomial x^2 - 2*x + 2 - To: Valuation ring at Place (x, x*y, y^2 + 1), Ring morphism: - From: Valuation ring at Place (x, x*y, y^2 + 1) - To: Number Field in s with defining polynomial x^2 - 2*x + 2)] - sage: for p in L.places_above(I.place()): - ....: k, fr_k, to_k = p.residue_field() - ....: assert all(fr_k(k(e)) == e for e in range(10)) - ....: assert all(to_k(fr_k(e)) == e for e in [k.random_element() for i in [1..10]]) - - :: - - sage: K. = FunctionField(QQbar); _. = K[] - sage: L. = K.extension(Y^3 + Y - x^4) - sage: O = K.maximal_order() - sage: I = O.ideal(x) - sage: [p.residue_field() for p in L.places_above(I.place())] - [(Algebraic Field, Ring morphism: - From: Algebraic Field - To: Valuation ring at Place (x, y - I, y^2 + 1), Ring morphism: - From: Valuation ring at Place (x, y - I, y^2 + 1) - To: Algebraic Field), (Algebraic Field, Ring morphism: - From: Algebraic Field - To: Valuation ring at Place (x, y, y^2), Ring morphism: - From: Valuation ring at Place (x, y, y^2) - To: Algebraic Field), (Algebraic Field, Ring morphism: - From: Algebraic Field - To: Valuation ring at Place (x, y + I, y^2 + 1), Ring morphism: - From: Valuation ring at Place (x, y + I, y^2 + 1) - To: Algebraic Field)] - """ - F = self.function_field() - prime = self.prime_ideal() # Let P be this 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 - - # Step 1: construct a vector space representing the residue field - # - # Given an (reversed) HNF basis M for a prime ideal P of O, every - # element of O mod P can be represented by a vector of polynomials of - # degrees less than those of the (anti)diagonal elements of M. In turn, - # the vector of polynomials can be represented by the vector of the - # coefficients of the polynomials. V is the space of these vectors. - - k = F.constant_base_field() - degs = [M[i,i].degree() for i in range(n)] - deg = sum(degs) # degree of the place - - # Let 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 primitive element of the residue field - - def candidates(): - # Trial 1: this suffices for places obtained from Kummers' theorem - # and for places of function fields over number fields or QQbar - - # 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()) - if isinstance(k, NumberField) or k is QQbar: - kk = ZZ - else: - kk = k - for c in kk: - if c != 0: - 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 - - # Search for a primitive element. It is such an element g of O - # whose powers span the vector space V. - 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]) - - # Step 4: construct the residue field K as an extension of the base - # constant field using the minimal polynomial and compute vector space - # representation W of K along with maps between them - if deg > 1: - if isinstance(k, NumberField): - if name is None: - name='s' - K = k.extension(min_poly, names=name) - - def from_W(e): - return K(list(e)) - - def to_W(e): - return vector(K(e)) - else: - K = k.extension(deg, name=name) - - # primitive element in K corresponding to g in O mod P - prim = min_poly.roots(K)[0][0] - - W, from_W, to_W = K.vector_space(k, basis=[prim**i for i in range(deg)], map=True) - else: # deg == 1 - K = k - - def from_W(e): - return K(e[0]) - - def to_W(e): - return vector([e]) - - # Step 5: compute the matrix of change of basis, from V to W via K - C = mat.inverse() - - # Step 6: construct the maps between the residue field of the valuation - # ring at P and K, via O and V and W - - def from_K(e): - return fr_V(to_W(e) * mat) - - # As explained in Section 4.8.3 of [Coh1993]_, alpha has a simple pole - # at this place and no other poles at finite places. - p = prime.prime_below().gen().numerator() - beta = prime._beta - alpha = ~p * sum(c1*c2 for c1,c2 in zip(beta, Obasis)) - alpha_powered_by_ramification_index = alpha ** prime._ramification_index - - def to_K(f): - if f not 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) - - return from_W(to_V(f) * C) - - 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 - - return FunctionFieldValuationRing(self.function_field(), self) - - class PlaceSet(UniqueRepresentation, Parent): """ Sets of Places of function fields. @@ -1131,9 +323,9 @@ class PlaceSet(UniqueRepresentation, Parent): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: L.place_set() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field Set of places of Function field in y defined by y^3 + x^3*y + x """ Element = FunctionFieldPlace @@ -1144,10 +336,10 @@ def __init__(self, field): TESTS:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: places = L.place_set() - sage: TestSuite(places).run() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: places = L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field + sage: TestSuite(places).run() # optional - sage.rings.finite_rings sage.rings.function_field """ self.Element = field._place_class Parent.__init__(self, category = Sets().Infinite()) @@ -1160,9 +352,9 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: L.place_set() + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field Set of places of Function field in y defined by y^3 + x^3*y + x """ return "Set of places of {}".format(self._field) @@ -1173,11 +365,11 @@ def _element_constructor_(self, x): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: places = L.place_set() - sage: O = L.maximal_order() - sage: places(O.ideal(x,y)) + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: places = L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field + sage: O = L.maximal_order() # optional - sage.rings.finite_rings sage.rings.function_field + sage: places(O.ideal(x, y)) # optional - sage.rings.finite_rings sage.rings.function_field Place (x, y) """ from .ideal import FunctionFieldIdeal @@ -1193,10 +385,10 @@ def _an_element_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: places = L.place_set() - sage: places.an_element() # random + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: places = L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field + sage: places.an_element() # random # optional - sage.rings.finite_rings sage.rings.function_field Ideal (x) of Maximal order of Rational function field in x over Finite Field of size 2 """ @@ -1216,10 +408,10 @@ def function_field(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)); _. = K[] - sage: L. = K.extension(Y^3 + x^3*Y + x) - sage: PS = L.place_set() - sage: PS.function_field() == L + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: PS = L.place_set() # optional - sage.rings.finite_rings sage.rings.function_field + sage: PS.function_field() == L # optional - sage.rings.finite_rings sage.rings.function_field True """ return self._field diff --git a/src/sage/rings/function_field/place_polymod.py b/src/sage/rings/function_field/place_polymod.py new file mode 100644 index 00000000000..dcba75d7fd3 --- /dev/null +++ b/src/sage/rings/function_field/place_polymod.py @@ -0,0 +1,665 @@ +# sage.doctest: optional - sage.rings.function_field +""" +Places of function fields: extension +""" + +#***************************************************************************** +# Copyright (C) 2023 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/ +#***************************************************************************** + +import sage +from sage.arith.functions import lcm +from sage.rings.integer_ring import ZZ +from sage.misc.cachefunc import cached_method +from sage.rings.number_field.number_field_base import NumberField + +from .place import FunctionFieldPlace + + +class FunctionFieldPlace_polymod(FunctionFieldPlace): + """ + Places of extensions of function fields. + """ + def place_below(self): + """ + Return the place lying below the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: OK = K.maximal_order() # optional - sage.rings.finite_rings + sage: OL = L.maximal_order() # optional - sage.rings.finite_rings + sage: p = OK.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: dec = OL.decomposition(p) # optional - sage.rings.finite_rings + sage: q = dec[0][0].place() # optional - sage.rings.finite_rings + sage: q.place_below() # optional - sage.rings.finite_rings + 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[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: OK = K.maximal_order() # optional - sage.rings.finite_rings + sage: OL = L.maximal_order() # optional - sage.rings.finite_rings + sage: p = OK.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: dec = OL.decomposition(p) # optional - sage.rings.finite_rings + sage: q = dec[0][0].place() # optional - sage.rings.finite_rings + sage: q.relative_degree() # optional - sage.rings.finite_rings + 1 + """ + return self._prime._relative_degree + + def degree(self): + """ + Return the degree of the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: OK = K.maximal_order() # optional - sage.rings.finite_rings + sage: OL = L.maximal_order() # optional - sage.rings.finite_rings + sage: p = OK.ideal(x^2 + x + 1) # optional - sage.rings.finite_rings + sage: dec = OL.decomposition(p) # optional - sage.rings.finite_rings + sage: q = dec[0][0].place() # optional - sage.rings.finite_rings + sage: q.degree() # optional - sage.rings.finite_rings + 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[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: pls = L.places() # optional - sage.rings.finite_rings + sage: [p.is_infinite_place() for p in pls] # optional - sage.rings.finite_rings + [True, True, False] + sage: [p.place_below() for p in pls] # optional - sage.rings.finite_rings + [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[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: pls = L.places() # optional - sage.rings.finite_rings + sage: [p.local_uniformizer().valuation(p) for p in pls] # optional - sage.rings.finite_rings + [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 gaps(self): + """ + Return the gap sequence for the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: p = O.ideal(x,y).place() # optional - sage.rings.finite_rings + sage: p.gaps() # a Weierstrass place # optional - sage.rings.finite_rings + [1, 2, 4] + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings + sage: [p.gaps() for p in L.places()] # optional - sage.rings.finite_rings + [[1, 2, 4], [1, 2, 4], [1, 2, 4]] + """ + if self.degree() == 1: + return self._gaps_rational() # faster for rational places + else: + return self._gaps_wronskian() + + def _gaps_rational(self): + """ + Return the gap sequence for the rational place. + + This method computes the gap numbers using the definition of gap + numbers. The dimension of the multiple of the prime divisor + supported at the place is computed by Hess' algorithm. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: p = O.ideal(x, y).place() # optional - sage.rings.finite_rings + sage: p.gaps() # indirect doctest # optional - sage.rings.finite_rings + [1, 2, 4] + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: [p.gaps() for p in L.places()] # indirect doctest # optional - sage.rings.finite_rings + [[1, 2, 4], [1, 2, 4], [1, 2, 4]] + """ + from sage.matrix.constructor import matrix + + F = self.function_field() + n = F.degree() + O = F.maximal_order() + Oinf = F.maximal_order_infinite() + + R = O._module_base_ring._ring + one = R.one() + + # Hess' Riemann-Roch basis algorithm stripped down for gaps computation + def dim_RR(M): + den = lcm([e.denominator() for e in M.list()]) + mat = matrix(R, M.nrows(), [(den*e).numerator() for e in M.list()]) + + # 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) + + 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) + + dim = 0 + for j in range(n): + i,ideg = pivot_row[j][0] + k = den.degree() - ideg + 1 + if k > 0: + dim += k + return dim + + V,fr,to = F.vector_space() + + prime_inv = ~ self.prime_ideal() + I = O.ideal(1) + J = Oinf.ideal(1) + + B = matrix([to(b) for b in J.gens_over_base()]) + C = matrix([to(v) for v in I.gens_over_base()]) + + prev = dim_RR(C * B.inverse()) + gaps = [] + g = F.genus() + i = 1 + if self.is_infinite_place(): + while g: + J = J * prime_inv + B = matrix([to(b) for b in J.gens_over_base()]) + dim = dim_RR(C * B.inverse()) + if dim == prev: + gaps.append(i) + g -= 1 + else: + prev = dim + i += 1 + else: # self is a finite place + Binv = B.inverse() + while g: + I = I * prime_inv + C = matrix([to(v) for v in I.gens_over_base()]) + dim = dim_RR(C * Binv) + if dim == prev: + gaps.append(i) + g -= 1 + else: + prev = dim + i += 1 + + return gaps + + def _gaps_wronskian(self): + """ + Return the gap sequence for the place. + + This method implements the local version of Hess' Algorithm 30 of [Hes2002b]_ + based on the Wronskian determinant. + + EXAMPLES:: + + sage: K. = FunctionField(GF(4)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3*Y + x) # optional - sage.rings.finite_rings + sage: O = L.maximal_order() # optional - sage.rings.finite_rings + sage: p = O.ideal(x, y).place() # optional - sage.rings.finite_rings + sage: p._gaps_wronskian() # a Weierstrass place # optional - sage.rings.finite_rings + [1, 2, 4] + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + x^3 * Y + x) # optional - sage.rings.finite_rings + sage: [p._gaps_wronskian() for p in L.places()] # optional - sage.rings.finite_rings + [[1, 2, 4], [1, 2, 4], [1, 2, 4]] + """ + from sage.matrix.constructor import matrix + from sage.modules.free_module_element import vector + + F = self.function_field() + R,fr_R,to_R = self._residue_field() + der = F.higher_derivation() + + sep = self.local_uniformizer() + + # a differential divisor satisfying + # v_p(W) = 0 for the place p + W = sep.differential().divisor() + + # Step 3: + basis = W._basis() + d = len(basis) + M = matrix([to_R(b) for b in basis]) + if M.rank() == 0: + return [] + + # Steps 4, 5, 6, 7: + e = 1 + gaps = [1] + while M.nrows() < d: + row = vector([to_R(der._derive(basis[i], e, sep)) for i in range(d)]) + if row not in M.row_space(): + M = matrix(M.rows() + [row]) + M.echelonize() + gaps.append(e + 1) + e += 1 + + return gaps + + def residue_field(self, name=None): + """ + Return the residue field of the place. + + INPUT: + + - ``name`` -- string; name of the generator of the residue field + + OUTPUT: + + - a field isomorphic to the residue field + + - a ring homomorphism from the valuation ring to the field + + - a ring homomorphism from the field to the valuation ring + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings + sage: k, fr_k, to_k = p.residue_field() # optional - sage.rings.finite_rings + sage: k # optional - sage.rings.finite_rings + Finite Field of size 2 + sage: fr_k # optional - sage.rings.finite_rings + Ring morphism: + From: Finite Field of size 2 + To: Valuation ring at Place (x, x*y) + sage: to_k # optional - sage.rings.finite_rings + Ring morphism: + From: Valuation ring at Place (x, x*y) + To: Finite Field of size 2 + sage: to_k(y) # optional - sage.rings.finite_rings + 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) # optional - sage.rings.finite_rings + 0 + sage: to_k(y/(1+y)) # optional - sage.rings.finite_rings + 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[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings + sage: k,fr_k,to_k = p._residue_field() # optional - sage.rings.finite_rings + sage: k # optional - sage.rings.finite_rings + Finite Field of size 2 + sage: [fr_k(e) for e in k] # optional - sage.rings.finite_rings + [0, 1] + + :: + + sage: K. = FunctionField(GF(9)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^3 + Y - x^4) # optional - sage.rings.finite_rings + sage: p = L.places()[-1] # optional - sage.rings.finite_rings + sage: p.residue_field() # optional - sage.rings.finite_rings + (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) + + :: + + sage: K. = FunctionField(QQ); _. = K[] + sage: L. = K.extension(Y^3 + Y - x^4) + sage: O = K.maximal_order() + sage: I = O.ideal(x) + sage: [p.residue_field() for p in L.places_above(I.place())] + [(Rational Field, Ring morphism: + From: Rational Field + To: Valuation ring at Place (x, y, y^2), Ring morphism: + From: Valuation ring at Place (x, y, y^2) + To: Rational Field), + (Number Field in s with defining polynomial x^2 - 2*x + 2, Ring morphism: + From: Number Field in s with defining polynomial x^2 - 2*x + 2 + To: Valuation ring at Place (x, x*y, y^2 + 1), Ring morphism: + From: Valuation ring at Place (x, x*y, y^2 + 1) + To: Number Field in s with defining polynomial x^2 - 2*x + 2)] + sage: for p in L.places_above(I.place()): + ....: k, fr_k, to_k = p.residue_field() + ....: assert all(fr_k(k(e)) == e for e in range(10)) + ....: assert all(to_k(fr_k(e)) == e for e in [k.random_element() for i in [1..10]]) + + :: + + sage: K. = FunctionField(QQbar); _. = K[] # optional - sage.rings.number_field + sage: L. = K.extension(Y^3 + Y - x^4) # optional - sage.rings.number_field + sage: O = K.maximal_order() # optional - sage.rings.number_field + sage: I = O.ideal(x) # optional - sage.rings.number_field + sage: [p.residue_field() for p in L.places_above(I.place())] # optional - sage.rings.number_field + [(Algebraic Field, Ring morphism: + From: Algebraic Field + To: Valuation ring at Place (x, y - I, y^2 + 1), Ring morphism: + From: Valuation ring at Place (x, y - I, y^2 + 1) + To: Algebraic Field), (Algebraic Field, Ring morphism: + From: Algebraic Field + To: Valuation ring at Place (x, y, y^2), Ring morphism: + From: Valuation ring at Place (x, y, y^2) + To: Algebraic Field), (Algebraic Field, Ring morphism: + From: Algebraic Field + To: Valuation ring at Place (x, y + I, y^2 + 1), Ring morphism: + From: Valuation ring at Place (x, y + I, y^2 + 1) + To: Algebraic Field)] + """ + F = self.function_field() + prime = self.prime_ideal() # Let P be this 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 + + from sage.matrix.constructor import matrix + from sage.modules.free_module_element import vector + + 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 + + # Step 1: construct a vector space representing the residue field + # + # Given an (reversed) HNF basis M for a prime ideal P of O, every + # element of O mod P can be represented by a vector of polynomials of + # degrees less than those of the (anti)diagonal elements of M. In turn, + # the vector of polynomials can be represented by the vector of the + # coefficients of the polynomials. V is the space of these vectors. + + k = F.constant_base_field() + degs = [M[i,i].degree() for i in range(n)] + deg = sum(degs) # degree of the place + + # Let 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 primitive element of the residue field + + def candidates(): + # Trial 1: this suffices for places obtained from Kummers' theorem + # and for places of function fields over number fields or QQbar + + # 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()) + if isinstance(k, (NumberField, sage.rings.abc.AlgebraicField)): + kk = ZZ + else: + kk = k + for c in kk: + if c != 0: + 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 + + # Search for a primitive element. It is such an element g of O + # whose powers span the vector space V. + 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]) + + # Step 4: construct the residue field K as an extension of the base + # constant field using the minimal polynomial and compute vector space + # representation W of K along with maps between them + if deg > 1: + if isinstance(k, NumberField): + if name is None: + name='s' + K = k.extension(min_poly, names=name) + + def from_W(e): + return K(list(e)) + + def to_W(e): + return vector(K(e)) + else: + K = k.extension(deg, name=name) + + # primitive element in K corresponding to g in O mod P + prim = min_poly.roots(K)[0][0] + + W, from_W, to_W = K.vector_space(k, basis=[prim**i for i in range(deg)], map=True) + else: # deg == 1 + K = k + + def from_W(e): + return K(e[0]) + + def to_W(e): + return vector([e]) + + # Step 5: compute the matrix of change of basis, from V to W via K + C = mat.inverse() + + # Step 6: construct the maps between the residue field of the valuation + # ring at P and K, via O and V and W + + def from_K(e): + return fr_V(to_W(e) * mat) + + # As explained in Section 4.8.3 of [Coh1993]_, alpha has a simple pole + # at this place and no other poles at finite places. + p = prime.prime_below().gen().numerator() + beta = prime._beta + alpha = ~p * sum(c1*c2 for c1,c2 in zip(beta, Obasis)) + alpha_powered_by_ramification_index = alpha ** prime._ramification_index + + def to_K(f): + if f not 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) + + return from_W(to_V(f) * C) + + return K, from_K, to_K + + def valuation_ring(self): + """ + Return the valuation ring at the place. + + EXAMPLES:: + + sage: K. = FunctionField(GF(2)); _. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(Y^2 + Y + x + 1/x) # optional - sage.rings.finite_rings + sage: p = L.places_finite()[0] # optional - sage.rings.finite_rings + sage: p.valuation_ring() # optional - sage.rings.finite_rings + Valuation ring at Place (x, x*y) + """ + from .valuation_ring import FunctionFieldValuationRing + + return FunctionFieldValuationRing(self.function_field(), self) diff --git a/src/sage/rings/function_field/place_rational.py b/src/sage/rings/function_field/place_rational.py new file mode 100644 index 00000000000..7a8acd440b6 --- /dev/null +++ b/src/sage/rings/function_field/place_rational.py @@ -0,0 +1,177 @@ +# sage.doctest: optional - sage.rings.finite_rings (because all doctests use finite fields) +""" +Places of function fields: rational +""" + +#***************************************************************************** +# Copyright (C) 2023 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 .place import FunctionFieldPlace + + +class FunctionFieldPlace_rational(FunctionFieldPlace): + """ + Places of rational function fields. + """ + 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 + + return FunctionFieldValuationRing(self.function_field(), self) diff --git a/src/sage/rings/function_field/function_field_valuation.py b/src/sage/rings/function_field/valuation.py similarity index 68% rename from src/sage/rings/function_field/function_field_valuation.py rename to src/sage/rings/function_field/valuation.py index 2573df22267..022103e096d 100644 --- a/src/sage/rings/function_field/function_field_valuation.py +++ b/src/sage/rings/function_field/valuation.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- r""" Discrete valuations on function fields @@ -33,8 +32,8 @@ sage: v = K.valuation(x - 1); v (x - 1)-adic valuation sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: w = v.extensions(L); w + sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: w = v.extensions(L); w # optional - sage.rings.function_field [[ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation, [ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation] @@ -57,9 +56,9 @@ sage: K. = FunctionField(QQ) sage: v = K.valuation(x - 1) sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: ws = v.extensions(L) - sage: for w in ws: TestSuite(w).run(max_runs=100) # long time + sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: ws = v.extensions(L) # optional - sage.rings.function_field + sage: for w in ws: TestSuite(w).run(max_runs=100) # long time # optional - sage.rings.function_field Run test suite for valuations that do not correspond to a classical place:: @@ -78,19 +77,19 @@ Run test suite for some other classical places over large ground fields:: - sage: K. = FunctionField(GF(3)) - sage: M. = FunctionField(K) - sage: v = M.valuation(x^3 - t) - sage: TestSuite(v).run(max_runs=10) # long time + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: M. = FunctionField(K) # optional - sage.rings.finite_rings + sage: v = M.valuation(x^3 - t) # optional - sage.rings.finite_rings + sage: TestSuite(v).run(max_runs=10) # long time # optional - sage.rings.finite_rings Run test suite for extensions over the infinite place:: sage: K. = FunctionField(QQ) sage: v = K.valuation(1/x) sage: R. = K[] - sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: w = v.extensions(L) - sage: TestSuite(w).run() # long time + sage: L. = K.extension(y^2 - 1/(x^2 + 1)) # optional - sage.rings.function_field + sage: w = v.extensions(L) # optional - sage.rings.function_field + sage: TestSuite(w).run() # long time # optional - sage.rings.function_field Run test suite for a valuation with `v(1/x) > 0` which does not come from a classical valuation of the infinite place:: @@ -107,14 +106,14 @@ sage: K. = FunctionField(QQ) sage: v = K.valuation(x^2 + 1) sage: L. = FunctionField(GaussianIntegers().fraction_field()) - sage: ws = v.extensions(L) + sage: ws = v.extensions(L) # optional - sage.rings.function_field sage: for w in ws: TestSuite(w).run(max_runs=100) # long time Run test suite for a finite place with residual degree and ramification:: - sage: K. = FunctionField(GF(3)) - sage: L. = FunctionField(K) - sage: v = L.valuation(x^6 - t) + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: L. = FunctionField(K) # optional - sage.rings.finite_rings + sage: v = L.valuation(x^6 - t) # optional - sage.rings.finite_rings sage: TestSuite(v).run(max_runs=10) # long time Run test suite for a valuation which is backed by limit valuation:: @@ -123,8 +122,8 @@ sage: R. = K[] sage: L. = K.extension(y^2 - (x^2 + x + 1)) sage: v = K.valuation(x - 1) - sage: w = v.extension(L) - sage: TestSuite(w).run() # long time + sage: w = v.extension(L) # optional - sage.rings.function_field + sage: TestSuite(w).run() # long time # optional - sage.rings.function_field Run test suite for a valuation which sends an element to `-\infty`:: @@ -172,7 +171,7 @@ class FunctionFieldValuationFactory(UniqueFactory): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(1); v # indirect doctest + sage: v = K.valuation(1); v # indirect doctest (x - 1)-adic valuation sage: v(x) 0 @@ -193,7 +192,7 @@ def create_key_and_extra_args(self, domain, prime): get the same object:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(x - 1) # indirect doctest + sage: v = K.valuation(x - 1) # indirect doctest sage: R. = QQ[] sage: w = GaussValuation(R, valuations.TrivialValuation(QQ)).augmentation(x - 1, 1) @@ -256,7 +255,7 @@ def create_key_and_extra_args_from_place(self, domain, generator): TESTS: sage: K. = FunctionField(QQ) - sage: v = K.valuation(1/x) # indirect doctest + sage: v = K.valuation(1/x) # indirect doctest """ if generator not in domain.base_field(): @@ -306,9 +305,9 @@ def create_key_and_extra_args_from_valuation(self, domain, valuation): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^3 + 1/x^3*y + 2/x^4) - sage: v = K.valuation(x) - sage: v.extensions(L) + sage: L. = K.extension(y^3 + 1/x^3*y + 2/x^4) # optional - sage.rings.function_field + sage: v = K.valuation(x) # optional - sage.rings.function_field + sage: v.extensions(L) # optional - sage.rings.function_field [[ (x)-adic valuation, v(y) = 1 ]-adic valuation (in Function field in y defined by y^3 + x*y + 2*x^2 after y |--> 1/x^2*y), [ (x)-adic valuation, v(y) = 1/2 ]-adic valuation (in Function field in y defined by y^3 + x*y + 2*x^2 after y |--> 1/x^2*y)] @@ -351,11 +350,11 @@ def create_key_and_extra_args_from_valuation_on_isomorphic_field(self, domain, v TESTS:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: w = v.extension(L) # indirect doctest + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = v.extension(L) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field """ from sage.categories.function_fields import FunctionFields @@ -400,7 +399,7 @@ def create_object(self, version, key, **extra_args): sage: K. = FunctionField(QQ) sage: R. = QQ[] sage: w = valuations.GaussValuation(R, QQ.valuation(2)) - sage: v = K.valuation(w); v # indirect doctest + sage: v = K.valuation(w); v # indirect doctest 2-adic valuation """ @@ -451,7 +450,7 @@ def create_object(self, version, key, **extra_args): raise NotImplementedError("valuation on %r from %r on %r" % (domain, valuation, valuation.domain())) -FunctionFieldValuation = FunctionFieldValuationFactory("sage.rings.function_field.function_field_valuation.FunctionFieldValuation") +FunctionFieldValuation = FunctionFieldValuationFactory("sage.rings.function_field.valuation.FunctionFieldValuation") class FunctionFieldValuation_base(DiscretePseudoValuation): @@ -462,8 +461,8 @@ class FunctionFieldValuation_base(DiscretePseudoValuation): TESTS:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(x) # indirect doctest - sage: from sage.rings.function_field.function_field_valuation import FunctionFieldValuation_base + sage: v = K.valuation(x) # indirect doctest + sage: from sage.rings.function_field.valuation import FunctionFieldValuation_base sage: isinstance(v, FunctionFieldValuation_base) True @@ -477,8 +476,8 @@ class DiscreteFunctionFieldValuation_base(DiscreteValuation): TESTS:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(x) # indirect doctest - sage: from sage.rings.function_field.function_field_valuation import DiscreteFunctionFieldValuation_base + sage: v = K.valuation(x) # indirect doctest + sage: from sage.rings.function_field.valuation import DiscreteFunctionFieldValuation_base sage: isinstance(v, DiscreteFunctionFieldValuation_base) True @@ -492,8 +491,8 @@ def extensions(self, L): sage: K. = FunctionField(QQ) sage: v = K.valuation(x) sage: R. = K[] - sage: L. = K.extension(y^2 - x) - sage: v.extensions(L) + sage: L. = K.extension(y^2 - x) # optional - sage.rings.function_field + sage: v.extensions(L) # optional - sage.rings.function_field [(x)-adic valuation] TESTS: @@ -502,21 +501,21 @@ def extensions(self, L): sage: v = K.valuation(1/x) sage: R. = K[] - sage: L. = K.extension(y^2 - 1/(x^2 + 1)) - sage: sorted(v.extensions(L), key=str) + sage: L. = K.extension(y^2 - 1/(x^2 + 1)) # optional - sage.rings.function_field + sage: sorted(v.extensions(L), key=str) # optional - sage.rings.function_field [[ Valuation at the infinite place, v(y + 1/x) = 3 ]-adic valuation, [ Valuation at the infinite place, v(y - 1/x) = 3 ]-adic valuation] Iterated extensions over the infinite place:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: w = v.extension(L) - sage: R. = L[] - sage: M. = L.extension(z^2 - y) - sage: w.extension(M) # squarefreeness is not implemented here + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field + sage: R. = L[] # optional - sage.rings.finite_rings sage.rings.function_field + sage: M. = L.extension(z^2 - y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w.extension(M) # squarefreeness is not implemented here # optional - sage.rings.finite_rings sage.rings.function_field Traceback (most recent call last): ... NotImplementedError @@ -530,19 +529,19 @@ def extensions(self, L): sage: v = K.valuation(v) sage: R. = K[] - sage: L. = K.extension(y^3 - x^4 - 1) - sage: v.extensions(L) + sage: L. = K.extension(y^3 - x^4 - 1) # optional - sage.rings.function_field + sage: v.extensions(L) # optional - sage.rings.function_field [2-adic valuation] Test that this works in towers:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y - x) - sage: R. = L[] - sage: L. = L.extension(z - y) - sage: v = K.valuation(x) - sage: v.extensions(L) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y - x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: R. = L[] # optional - sage.rings.finite_rings sage.rings.function_field + sage: L. = L.extension(z - y) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v.extensions(L) # optional - sage.rings.finite_rings sage.rings.function_field [(x)-adic valuation] """ K = self.domain() @@ -587,10 +586,10 @@ class RationalFunctionFieldValuation_base(FunctionFieldValuation_base): TESTS:: - sage: K. = FunctionField(GF(2)) - sage: v = K.valuation(x) # indirect doctest - sage: from sage.rings.function_field.function_field_valuation import RationalFunctionFieldValuation_base - sage: isinstance(v, RationalFunctionFieldValuation_base) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: v = K.valuation(x) # indirect doctest # optional - sage.rings.finite_rings + sage: from sage.rings.function_field.valuation import RationalFunctionFieldValuation_base + sage: isinstance(v, RationalFunctionFieldValuation_base) # optional - sage.rings.finite_rings True """ @@ -601,15 +600,15 @@ def element_with_valuation(self, s): EXAMPLES:: - sage: K. = NumberField(x^3+6) - sage: v = K.valuation(2) - sage: R. = K[] - sage: w = GaussValuation(R, v).augmentation(x, 1/123) - sage: K. = FunctionField(K) - sage: w = w.extension(K) - sage: w.element_with_valuation(122/123) + sage: K. = NumberField(x^3 + 6) # optional - sage.rings.number_field + sage: v = K.valuation(2) # optional - sage.rings.number_field + sage: R. = K[] # optional - sage.rings.number_field + sage: w = GaussValuation(R, v).augmentation(x, 1/123) # optional - sage.rings.number_field + sage: K. = FunctionField(K) # optional - sage.rings.number_field + sage: w = w.extension(K) # optional - sage.rings.number_field + sage: w.element_with_valuation(122/123) # optional - sage.rings.number_field 2/x - sage: w.element_with_valuation(1) + sage: w.element_with_valuation(1) # optional - sage.rings.number_field 2 """ @@ -630,10 +629,10 @@ class ClassicalFunctionFieldValuation_base(DiscreteFunctionFieldValuation_base): TESTS:: - sage: K. = FunctionField(GF(5)) - sage: v = K.valuation(x) # indirect doctest - sage: from sage.rings.function_field.function_field_valuation import ClassicalFunctionFieldValuation_base - sage: isinstance(v, ClassicalFunctionFieldValuation_base) + sage: K. = FunctionField(GF(5)) # optional - sage.rings.finite_rings + sage: v = K.valuation(x) # indirect doctest # optional - sage.rings.finite_rings + sage: from sage.rings.function_field.valuation import ClassicalFunctionFieldValuation_base + sage: isinstance(v, ClassicalFunctionFieldValuation_base) # optional - sage.rings.finite_rings True """ @@ -692,7 +691,7 @@ def __init__(self, parent, base_valuation): sage: K. = FunctionField(QQ) sage: v = K.valuation(x) # indirect doctest - sage: from sage.rings.function_field.function_field_valuation import InducedRationalFunctionFieldValuation_base + sage: from sage.rings.function_field.valuation import InducedRationalFunctionFieldValuation_base sage: isinstance(v, InducedRationalFunctionFieldValuation_base) True @@ -822,7 +821,7 @@ def extensions(self, L): sage: K. = FunctionField(QQ) sage: v = K.valuation(x^2 + 1) sage: L. = FunctionField(GaussianIntegers().fraction_field()) - sage: v.extensions(L) # indirect doctest + sage: v.extensions(L) # indirect doctest [(x - I)-adic valuation, (x + I)-adic valuation] """ @@ -854,7 +853,7 @@ def _call_(self, f): EXAMPLES:: sage: K. = FunctionField(QQ) - sage: v = K.valuation(x) # indirect doctest + sage: v = K.valuation(x) # indirect doctest sage: v((x+1)/x^2) -2 @@ -998,14 +997,14 @@ class FiniteRationalFunctionFieldValuation(InducedRationalFunctionFieldValuation A finite place with ramification:: - sage: K. = FunctionField(GF(3)) - sage: L. = FunctionField(K) - sage: u = L.valuation(x^3 - t); u + sage: K. = FunctionField(GF(3)) # optional - sage.rings.finite_rings + sage: L. = FunctionField(K) # optional - sage.rings.finite_rings + sage: u = L.valuation(x^3 - t); u # optional - sage.rings.finite_rings (x^3 + 2*t)-adic valuation A finite place with residual degree and ramification:: - sage: q = L.valuation(x^6 - t); q + sage: q = L.valuation(x^6 - t); q # optional - sage.rings.finite_rings (x^6 + 2*t)-adic valuation """ @@ -1015,7 +1014,7 @@ def __init__(self, parent, base_valuation): sage: K. = FunctionField(QQ) sage: v = K.valuation(x + 1) - sage: from sage.rings.function_field.function_field_valuation import FiniteRationalFunctionFieldValuation + sage: from sage.rings.function_field.valuation import FiniteRationalFunctionFieldValuation sage: isinstance(v, FiniteRationalFunctionFieldValuation) True @@ -1050,7 +1049,7 @@ def __init__(self, parent, base_valuation): sage: v = GaussValuation(QQ['x'], QQ.valuation(2)).augmentation(x, infinity) sage: K. = FunctionField(QQ) sage: w = K.valuation(v) - sage: from sage.rings.function_field.function_field_valuation import NonClassicalRationalFunctionFieldValuation + sage: from sage.rings.function_field.valuation import NonClassicalRationalFunctionFieldValuation sage: isinstance(w, NonClassicalRationalFunctionFieldValuation) True @@ -1077,8 +1076,8 @@ def residue_ring(self): Rational function field in x over Finite Field of size 2 sage: R. = K[] - sage: L. = K.extension(y^2 + 2*x) - sage: w.extension(L).residue_ring() + sage: L. = K.extension(y^2 + 2*x) # optional - sage.rings.function_field + sage: w.extension(L).residue_ring() # optional - sage.rings.function_field Function field in u2 defined by u2^2 + x TESTS: @@ -1109,9 +1108,9 @@ class FunctionFieldFromLimitValuation(FiniteExtensionFromLimitValuation, Discret sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = K.valuation(x - 1) # indirect doctest - sage: w = v.extension(L); w + sage: L. = K.extension(y^2 - (x^2 + x + 1)) # optional - sage.rings.function_field + sage: v = K.valuation(x - 1) # indirect doctest # optional - sage.rings.function_field + sage: w = v.extension(L); w # optional - sage.rings.function_field (x - 1)-adic valuation """ @@ -1121,11 +1120,11 @@ def __init__(self, parent, approximant, G, approximants): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = K.valuation(x - 1) # indirect doctest - sage: w = v.extension(L) - sage: from sage.rings.function_field.function_field_valuation import FunctionFieldFromLimitValuation - sage: isinstance(w, FunctionFieldFromLimitValuation) + sage: L. = K.extension(y^2 - (x^2 + x + 1)) # optional - sage.rings.function_field + sage: v = K.valuation(x - 1) # indirect doctest # optional - sage.rings.function_field + sage: w = v.extension(L) # optional - sage.rings.function_field + sage: from sage.rings.function_field.valuation import FunctionFieldFromLimitValuation + sage: isinstance(w, FunctionFieldFromLimitValuation) # optional - sage.rings.function_field True """ @@ -1140,10 +1139,10 @@ def _to_base_domain(self, f): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = K.valuation(x - 1) # indirect doctest - sage: w = v.extension(L) - sage: w._to_base_domain(y).parent() + sage: L. = K.extension(y^2 - (x^2 + x + 1)) # optional - sage.rings.function_field + sage: v = K.valuation(x - 1) # indirect doctest # optional - sage.rings.function_field + sage: w = v.extension(L) # optional - sage.rings.function_field + sage: w._to_base_domain(y).parent() # optional - sage.rings.function_field Univariate Polynomial Ring in y over Rational function field in x over Rational Field """ @@ -1157,10 +1156,10 @@ def scale(self, scalar): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - (x^2 + x + 1)) - sage: v = K.valuation(x - 1) # indirect doctest - sage: w = v.extension(L) - sage: 3*w + sage: L. = K.extension(y^2 - (x^2 + x + 1)) # optional - sage.rings.function_field + sage: v = K.valuation(x - 1) # indirect doctest # optional - sage.rings.function_field + sage: w = v.extension(L) # optional - sage.rings.function_field + sage: 3*w # optional - sage.rings.function_field 3 * (x - 1)-adic valuation """ @@ -1176,8 +1175,8 @@ class FunctionFieldMappedValuation_base(FunctionFieldValuation_base, MappedValua EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: v = K.valuation(1/x); v + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: v = K.valuation(1/x); v # optional - sage.rings.finite_rings Valuation at the infinite place """ @@ -1185,10 +1184,10 @@ def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_v 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) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: from sage.rings.function_field.valuation import FunctionFieldMappedValuation_base + sage: isinstance(v, FunctionFieldMappedValuation_base) # optional - sage.rings.finite_rings True """ @@ -1204,12 +1203,12 @@ def _to_base_domain(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: w = v.extension(L) - sage: w._to_base_domain(y) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w._to_base_domain(y) # optional - sage.rings.finite_rings sage.rings.function_field x^2*y """ @@ -1221,12 +1220,12 @@ def _from_base_domain(self, f): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: w = v.extension(L) - sage: w._from_base_domain(w._to_base_domain(y)) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w._from_base_domain(w._to_base_domain(y)) # optional - sage.rings.finite_rings sage.rings.function_field y r""" @@ -1238,12 +1237,12 @@ def scale(self, scalar): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: w = v.extension(L) - sage: 3*w + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field + sage: 3*w # optional - sage.rings.finite_rings sage.rings.function_field 3 * (x)-adic valuation (in Rational function field in x over Finite Field of size 2 after x |--> 1/x) """ @@ -1258,11 +1257,11 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: v.extension(L) # indirect doctest + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: v.extension(L) # indirect doctest # optional - sage.rings.finite_rings sage.rings.function_field Valuation at the infinite place """ @@ -1279,10 +1278,10 @@ def is_discrete_valuation(self): sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - x^4 - 1) - sage: v = K.valuation(1/x) - sage: w0,w1 = v.extensions(L) - sage: w0.is_discrete_valuation() + sage: L. = K.extension(y^2 - x^4 - 1) # optional - sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.function_field + sage: w0,w1 = v.extensions(L) # optional - sage.rings.function_field + sage: w0.is_discrete_valuation() # optional - sage.rings.function_field True """ @@ -1297,8 +1296,8 @@ class FunctionFieldMappedValuationRelative_base(FunctionFieldMappedValuation_bas EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: v = K.valuation(1/x); v + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: v = K.valuation(1/x); v # optional - sage.rings.finite_rings Valuation at the infinite place """ @@ -1306,10 +1305,10 @@ def __init__(self, parent, base_valuation, to_base_valuation_domain, from_base_v 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) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: from sage.rings.function_field.valuation import FunctionFieldMappedValuationRelative_base + sage: isinstance(v, FunctionFieldMappedValuationRelative_base) # optional - sage.rings.finite_rings True """ @@ -1323,8 +1322,8 @@ def restriction(self, ring): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: K.valuation(1/x).restriction(GF(2)) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: K.valuation(1/x).restriction(GF(2)) # optional - sage.rings.finite_rings Trivial valuation on Finite Field of size 2 """ @@ -1345,7 +1344,9 @@ class RationalFunctionFieldMappedValuation(FunctionFieldMappedValuationRelative_ sage: w = GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) sage: w = K.valuation(w) sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))); v - Valuation on rational function field induced by [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] (in Rational function field in x over Rational Field after x |--> 1/x) + Valuation on rational function field induced by + [ Gauss valuation induced by 2-adic valuation, v(x) = 1 ] + (in Rational function field in x over Rational Field after x |--> 1/x) """ def __init__(self, parent, base_valuation, to_base_valuation_doain, from_base_valuation_domain): @@ -1357,7 +1358,7 @@ def __init__(self, parent, base_valuation, to_base_valuation_doain, from_base_va sage: w = GaussValuation(R, QQ.valuation(2)).augmentation(x, 1) sage: w = K.valuation(w) sage: v = K.valuation((w, K.hom([~K.gen()]), K.hom([~K.gen()]))) - sage: from sage.rings.function_field.function_field_valuation import RationalFunctionFieldMappedValuation + sage: from sage.rings.function_field.valuation import RationalFunctionFieldMappedValuation sage: isinstance(v, RationalFunctionFieldMappedValuation) True @@ -1382,7 +1383,7 @@ def __init__(self, parent): sage: K. = FunctionField(QQ) sage: v = K.valuation(1/x) # indirect doctest - sage: from sage.rings.function_field.function_field_valuation import InfiniteRationalFunctionFieldValuation + sage: from sage.rings.function_field.valuation import InfiniteRationalFunctionFieldValuation sage: isinstance(v, InfiniteRationalFunctionFieldValuation) True @@ -1416,23 +1417,23 @@ class FunctionFieldExtensionMappedValuation(FunctionFieldMappedValuationRelative EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: w = v.extension(L) + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field - sage: w(x) + sage: w(x) # optional - sage.rings.finite_rings sage.rings.function_field -1 - sage: w(y) + sage: w(y) # optional - sage.rings.finite_rings sage.rings.function_field -3/2 - sage: w.uniformizer() + sage: w.uniformizer() # optional - sage.rings.finite_rings sage.rings.function_field 1/x^2*y TESTS:: - sage: from sage.rings.function_field.function_field_valuation import FunctionFieldExtensionMappedValuation - sage: isinstance(w, FunctionFieldExtensionMappedValuation) + sage: from sage.rings.function_field.valuation import FunctionFieldExtensionMappedValuation + sage: isinstance(w, FunctionFieldExtensionMappedValuation) # optional - sage.rings.finite_rings sage.rings.function_field True """ @@ -1442,18 +1443,18 @@ def _repr_(self): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: w = v.extension(L); w + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w = v.extension(L); w # optional - sage.rings.finite_rings sage.rings.function_field Valuation at the infinite place sage: K. = FunctionField(QQ) sage: R. = K[] - sage: L. = K.extension(y^2 - 1/x^2 - 1) - sage: v = K.valuation(1/x) - sage: w = v.extensions(L); w + sage: L. = K.extension(y^2 - 1/x^2 - 1) # optional - sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.function_field + sage: w = v.extensions(L); w # optional - sage.rings.function_field [[ Valuation at the infinite place, v(y + 1) = 2 ]-adic valuation, [ Valuation at the infinite place, v(y - 1) = 2 ]-adic valuation] @@ -1469,12 +1470,12 @@ def restriction(self, ring): EXAMPLES:: - sage: K. = FunctionField(GF(2)) - sage: R. = K[] - sage: L. = K.extension(y^2 + y + x^3) - sage: v = K.valuation(1/x) - sage: w = v.extension(L) - sage: w.restriction(K) is v + sage: K. = FunctionField(GF(2)) # optional - sage.rings.finite_rings + sage: R. = K[] # optional - sage.rings.finite_rings + sage: L. = K.extension(y^2 + y + x^3) # optional - sage.rings.finite_rings sage.rings.function_field + sage: v = K.valuation(1/x) # optional - sage.rings.finite_rings + sage: w = v.extension(L) # optional - sage.rings.finite_rings sage.rings.function_field + sage: w.restriction(K) is v # optional - sage.rings.finite_rings sage.rings.function_field True """ if ring.is_subring(self.domain().base()): diff --git a/src/sage/rings/function_field/valuation_ring.py b/src/sage/rings/function_field/valuation_ring.py index bb3a39c687a..37d111df2c8 100644 --- a/src/sage/rings/function_field/valuation_ring.py +++ b/src/sage/rings/function_field/valuation_ring.py @@ -1,3 +1,5 @@ +# sage.doctest: optional - sage.rings.finite_rings +# sage.doctest: optional - sage.rings.function_field r""" Valuation rings of function fields @@ -54,7 +56,7 @@ """ # **************************************************************************** -# Copyright (C) 2016 Kwankyu Lee +# Copyright (C) 2016-2019 Kwankyu Lee # # Distributed under the terms of the GNU General Public License (GPL) # as published by the Free Software Foundation; either version 2 of diff --git a/src/sage/rings/polynomial/polynomial_singular_interface.py b/src/sage/rings/polynomial/polynomial_singular_interface.py index f19415b34d3..d8baaa37c7f 100644 --- a/src/sage/rings/polynomial/polynomial_singular_interface.py +++ b/src/sage/rings/polynomial/polynomial_singular_interface.py @@ -43,7 +43,7 @@ from sage.interfaces.singular import singular from sage.rings.rational_field import is_RationalField -from sage.rings.function_field.function_field import RationalFunctionField +from sage.rings.function_field.function_field_rational import RationalFunctionField from sage.rings.finite_rings.finite_field_base import FiniteField from sage.rings.integer_ring import ZZ @@ -162,7 +162,7 @@ def _do_singular_init_(singular, base_ring, char, _vars, order): return singular(f"std(ideal({base_ring.__minpoly}))", type='qring'), None - elif isinstance(base_ring, sage.rings.function_field.function_field.RationalFunctionField) \ + elif isinstance(base_ring, sage.rings.function_field.function_field_rational.RationalFunctionField) \ and base_ring.constant_field().is_prime_field(): gen = str(base_ring.gen()) return make_ring(f"({base_ring.characteristic()},{gen})"), None diff --git a/src/sage/rings/valuation/augmented_valuation.py b/src/sage/rings/valuation/augmented_valuation.py index e4834e38f0b..37fda19d7dc 100644 --- a/src/sage/rings/valuation/augmented_valuation.py +++ b/src/sage/rings/valuation/augmented_valuation.py @@ -1109,7 +1109,7 @@ def lift(self, F): # We only have to do that if psi is non-trivial if self.psi().degree() > 1: from sage.rings.polynomial.polynomial_quotient_ring_element import PolynomialQuotientRingElement - from sage.rings.function_field.element import FunctionFieldElement_polymod + from sage.rings.function_field.element_polymod import FunctionFieldElement_polymod from sage.rings.number_field.number_field_element import NumberFieldElement_relative from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing if isinstance(F, PolynomialQuotientRingElement): diff --git a/src/sage/rings/valuation/valuations_catalog.py b/src/sage/rings/valuation/valuations_catalog.py index 0d0f3362768..69e064a10c1 100644 --- a/src/sage/rings/valuation/valuations_catalog.py +++ b/src/sage/rings/valuation/valuations_catalog.py @@ -1,5 +1,5 @@ from sage.rings.padics.padic_valuation import pAdicValuation -from sage.rings.function_field.function_field_valuation import FunctionFieldValuation +from sage.rings.function_field.valuation import FunctionFieldValuation from .gauss_valuation import GaussValuation from .trivial_valuation import TrivialDiscretePseudoValuation, TrivialPseudoValuation, TrivialValuation from .limit_valuation import LimitValuation