From df3598cd28addbeea131fdaacb00c2acd9fae95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 26 Apr 2022 18:46:55 +0200 Subject: [PATCH 001/197] Create a draft of FiniteDrinfeldModule --- src/sage/modules/all.py | 1 + src/sage/modules/finite_drinfeld_module.py | 175 +++++++++++++++++++++ 2 files changed, 176 insertions(+) create mode 100644 src/sage/modules/finite_drinfeld_module.py diff --git a/src/sage/modules/all.py b/src/sage/modules/all.py index a90258d7ec8..a6ca79ddf69 100644 --- a/src/sage/modules/all.py +++ b/src/sage/modules/all.py @@ -29,3 +29,4 @@ lazy_import('sage.modules.multi_filtered_vector_space', 'MultiFilteredVectorSpace') lazy_import('sage.modules.free_quadratic_module_integer_symmetric', 'IntegralLattice') lazy_import('sage.modules.torsion_quadratic_module', 'TorsionQuadraticForm') +lazy_import('sage.modules.finite_drinfeld_module', 'FiniteDrinfeldModule') diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/modules/finite_drinfeld_module.py new file mode 100644 index 00000000000..e6eb1f7aba1 --- /dev/null +++ b/src/sage/modules/finite_drinfeld_module.py @@ -0,0 +1,175 @@ +r""" + + + + +EXAMPLES:: + + + +AUTHORS: + +- Antoine Leudière (2022-04-26): initial version + +""" + +#***************************************************************************** +# Copyright (C) 2022 Antoine Leudière +# +# 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. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +from sage.categories.action import Action +from sage.categories.homset import Hom +from sage.rings.morphism import RingHomomorphism_im_gens +from sage.rings.polynomial.ore_polynomial_element import OrePolynomial +from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing +from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field +from sage.misc.latex import latex + + +class FiniteDrinfeldModule(RingHomomorphism_im_gens): + + def __init__(self, polring, gen): + # VERIFICATIONS + # Check `polring` is an Fq[X]: + # See docstrings of `PolynomialRing_dense_finite_field` and + # `is_PolynomialRing`. + isinstance(polring, PolynomialRing_dense_finite_field) + # Check `gen` is an Ore polynomial: + if not isinstance(gen, OrePolynomial): + raise TypeError('The generator must be an Ore polynomial') + # Now we can define those for convenience: + FqX = polring + Ltau = gen.parent() + Fq = FqX.base_ring() + L = Ltau.base_ring() + # Check the Ore polynomial ring is an L{tau} with L a finite + # field extension of Fq: + _check_base_fields(Fq, L) + if not Ltau.twisting_derivation() is None: + raise ValueError('The Ore polynomial ring should have no ' \ + 'derivation') + # Check the frobenius is x -> x^q: + if Ltau.twisting_morphism().power() != Fq.degree(): + raise ValueError('The twisting morphism of the Ore polynomial ' \ + 'ring must be the Frobenius endomorphism of the base ' \ + 'field of the polynomial ring') + # The generator is not constant: + if gen.is_constant(): + raise ValueError('The generator must not be constant') + # ACTUAL WORK + super().__init__(Hom(FqX, Ltau), gen) + + ########### + # Methods # + ########### + + def change_ring(self, R): + # VERIFICATIONS + if not R.is_field() and R.is_finite(): + raise TypeError('Argument must be a finite field') + if not self.ore_polring().base_ring().is_subring(R): + raise ValueError('The new field must be a finite field ' \ + 'extension of the base field of the Ore polynomial ring.') + _check_base_fields(self.polring().base_ring(), R) + # ACTUAL WORK + new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) + new_ore_polring = OrePolynomialRing(R, new_frobenius, + names=self.ore_polring().variable_names()) + return FiniteDrinfeldModule(self.polring(), new_ore_polring(self.gen())) + + def rank(self): + return self.gen().degree() + + ########################## + # Special Sage functions # + ########################## + + def _get_action_(self, extension): + return FiniteDrinfeldModuleAction(self, extension) + + def _latex_(self): + return f'\\text{{Finite{{ }}{latex(self.polring())}-Drinfeld{{ }}' \ + f'module{{ }}defined{{ }}over{{ }}}}' \ + f'{latex(self.ore_polring().base_ring())}\\text{{{{ }}' \ + f'by{{ }}}}\n' \ + f'\\begin{{align}}\n' \ + f' {latex(self.polring())}\n' \ + f' &\\to {latex(self.ore_polring())} \\\\\n' \ + f' {latex(self.polring().gen())}\n' \ + f' &\\mapsto {latex(self.gen())}\n' \ + f'\\end{{align}}' + + def _repr_(self): + return f'Finite Drinfeld module from {self.polring()} over ' \ + f'{self.ore_polring().base_ring()} defined by {self.gen()}.' + + ########### + # Getters # + ########### + + def frobenius(self): + return self.ore_polring().twisting_morphism() + + def gen(self): + [gen] = self.im_gens() + return gen + + def ore_polring(self): + return self.codomain() + + def polring(self): + return self.domain() + +class FiniteDrinfeldModuleAction(Action): + + def __init__(self, finite_drinfeld_module, extension): + # VERIFICATIONS + if not isinstance(finite_drinfeld_module, FiniteDrinfeldModule): + raise TypeError('First argument must be a FiniteDrinfeldModule') + if not (extension.is_field() and extension.is_finite() and + finite_drinfeld_module.polring().base_ring().is_subring(extension)): + raise ValueError('The extension must be a finite field ' \ + 'extension of the base field of the Ore polynomial ring') + # WORK + self.__finite_drinfeld_module = finite_drinfeld_module + super().__init__(finite_drinfeld_module.polring(), extension) + + ########### + # Methods # + ########### + + def extension(self): + return self.codomain() + + def finite_drinfeld_module(self): + return self.__finite_drinfeld_module + + ########################## + # Special Sage functions # + ########################## + + def _latex_(self): + phi = self.finite_drinfeld_module() + return f'\\text{{Drinfeld{{ }}module{{ }}action{{ }}' \ + f'on{{ }}}}{latex(self.extension())}\\text{{{{ }}' \ + f'induced{{ }}by{{ }}}}{latex(phi)}' + + def _repr_(self): + return f'Action on {self.domain()} induced by the ' \ + f'{self.finite_drinfeld_module()}' + + def _act_(self, g, x): + return self.finite_drinfeld_module().change_ring(self.extension())(g)(x) + + +def _check_base_fields(Fq, L): + if not (L.is_field() and L.is_finite() and Fq.is_subring(L)): + raise ValueError(f'The base field of the Ore polynomial ring must ' \ + 'be a finite field extension of the base field of the ' \ + 'polynomial ring') From 84c7b5b652ab828a68e492af31831fc25ed4b52a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 26 Apr 2022 20:58:53 +0200 Subject: [PATCH 002/197] Create draft for finite Drinfeld module doctest --- src/sage/modules/finite_drinfeld_module.py | 120 +++++++++++++++++++-- 1 file changed, 111 insertions(+), 9 deletions(-) diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/modules/finite_drinfeld_module.py index e6eb1f7aba1..8170da59e7b 100644 --- a/src/sage/modules/finite_drinfeld_module.py +++ b/src/sage/modules/finite_drinfeld_module.py @@ -1,16 +1,26 @@ r""" - +This module provides classes for finite Drinfeld modules +(`FiniteDrinfeldModule`) and their module action on the algebraic +closure of `\Fq` (`FiniteDrinfeldModuleAction`). - - -EXAMPLES:: +AUTHORS: - +- Antoine Leudière (2022-04): initial version -AUTHORS: +Let `\tau` be the `\Fq`-linear Frobenius endomorphism of `\Fqbar` +defined by `x \mapsto x^q`. Let `L\{\tau\}` be the ring of Ore +polynomials in `\tau` with coefficients in L. Fix an element `\omega` in +`L` (global parameter). A finite Drinfeld module is an `\Fq`-algebra +morphism `\phi: \Fq[X] \to L\{\tau\]` such that: + - the constant coefficient of `\phi(X)` is `\omega`, + - there exists at least one `a \in \Fq[X]` such that `\phi(a)` has a + non zero `\tau`-degree. -- Antoine Leudière (2022-04-26): initial version +As an `\Fq[X]`-algebra morphism, a finite Drinfeld module is only +determined by the image of `X`. +Crucially, the Drinfeld module `\phi` gives rise to the `\Fq[X]`-module +law on `\Fqbar` defined by `(a, x) = \phi(a)(x)`. """ #***************************************************************************** @@ -25,14 +35,106 @@ from sage.categories.action import Action from sage.categories.homset import Hom +from sage.misc.latex import latex from sage.rings.morphism import RingHomomorphism_im_gens from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field -from sage.misc.latex import latex class FiniteDrinfeldModule(RingHomomorphism_im_gens): + r""" + Class for finite Drinfeld modules. + + INPUT: + + - ``polring`` -- the base polynomial ring + - ``gen`` -- the image of `X` + + EXAMPLES: + + .. RUBRIC:: Basics + + First, create base objects:: + + sage: Fq = GF(7^2) + sage: FqX. = Fq[] + sage: L = Fq.extension(3) + sage: frobenius = L.frobenius_endomorphism(2) + sage: Ltau. = OrePolynomialRing(L, frobenius) + + Then we instanciate the Drinfeld module:: + + sage: phi_X = 1 + t^2 + sage: phi = FiniteDrinfeldModule(FqX, phi_X) + sage: phi + Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z6 of size 7^6 generated by t^2 + 1. + + There are getters for the base objects:: + + sage: phi.polring() + Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 + sage: phi.ore_polring() + Ore Polynomial Ring in t over Finite Field in z6 of size 7^6 twisted by z6 |--> z6^(7^2) + sage: phi.gen() + t^2 + 1 + + And the class inherits `RingHomomorphism_im_gens`, so that one can + use:: + + sage: phi.domain() + Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 + sage: phi.codomain() + Ore Polynomial Ring in t over Finite Field in z6 of size 7^6 twisted by z6 |--> z6^(7^2) + sage: phi.im_gens() + [t^2 + 1] + + The rank of the Drinfeld module is retrieved with:: + + sage: phi.rank() + 2 + + .. RUBRIC:: The module law induced by a Drinfeld module + + The most important feature of Drinfeld modules is that they induce + an `\Fq[X]`-module law on the algebraic closure `\Fqbar` of `\Fq`. + We implement this action for any finite field extension `M` of `L`. + The `_get_action_` method returns an `Action` object:: + + sage: M = L.extension(2) + sage: action = phi._get_action_(M) + sage: action + Action on Finite Field in z12 of size 7^12 induced by the Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z6 of size 7^6 + generated by t^2 + 1. + sage: x = M.gen() + sage: g = X^3 + X + 5 + sage: action(g, x) + ... + 6*z12^11 + 5*z12^9 + 5*z12^8 + 2*z12^7 + 6*z12^6 + z12^5 + 6*z12^4 + 2*z12^3 + 3*z12^2 + 5*z12 + 4 + + Furthermore, it can be useful to embed a Drinfeld module into a + larger Ore polynomial ring:: + + sage: M = L.extension(2) + sage: psi = phi.change_ring(M); psi + Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z12 of size 7^12 generated by t^2 + 1. + + .. NOTE:: + + The general definition of a Drinfeld module is out of the scope + of this implementation. + + :: + + You can see all available methods of `RingHomomorphism_im_gens` + with `dir(sage.rings.morphism.RingHomomorphism_im_gens)`. Same + for `Action`. + + .. SEEALSO:: + :mod:`sage.categories.action.Action` + :mod:`sage.rings.polynomial.ore_polynomial_element` + :mod:`sage.rings.polynomial.ore_polynomial_ring` + """ def __init__(self, polring, gen): # VERIFICATIONS @@ -107,7 +209,7 @@ def _latex_(self): def _repr_(self): return f'Finite Drinfeld module from {self.polring()} over ' \ - f'{self.ore_polring().base_ring()} defined by {self.gen()}.' + f'{self.ore_polring().base_ring()} generated by {self.gen()}.' ########### # Getters # From 2723f99dbcb6005d7446e47ca3ea8d20f1f09b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 27 Apr 2022 14:46:56 +0200 Subject: [PATCH 003/197] Fix FiniteDrinfeldModule action in all.py --- src/sage/modules/all.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/modules/all.py b/src/sage/modules/all.py index a6ca79ddf69..ab41fbcf564 100644 --- a/src/sage/modules/all.py +++ b/src/sage/modules/all.py @@ -30,3 +30,4 @@ lazy_import('sage.modules.free_quadratic_module_integer_symmetric', 'IntegralLattice') lazy_import('sage.modules.torsion_quadratic_module', 'TorsionQuadraticForm') lazy_import('sage.modules.finite_drinfeld_module', 'FiniteDrinfeldModule') +lazy_import('sage.modules.finite_drinfeld_module', 'FiniteDrinfeldModuleAction') From 8bef6ba375e8b60448764395d46c72144c9a66a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 5 May 2022 12:58:48 +0200 Subject: [PATCH 004/197] Refactor _latex_ method in FiniteDrinfeldModule --- src/sage/modules/finite_drinfeld_module.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/modules/finite_drinfeld_module.py index 8170da59e7b..5212bb808ff 100644 --- a/src/sage/modules/finite_drinfeld_module.py +++ b/src/sage/modules/finite_drinfeld_module.py @@ -197,15 +197,15 @@ def _get_action_(self, extension): def _latex_(self): return f'\\text{{Finite{{ }}{latex(self.polring())}-Drinfeld{{ }}' \ - f'module{{ }}defined{{ }}over{{ }}}}' \ - f'{latex(self.ore_polring().base_ring())}\\text{{{{ }}' \ - f'by{{ }}}}\n' \ + f'module{{ }}defined{{ }}by{{ }}}}\n' \ f'\\begin{{align}}\n' \ f' {latex(self.polring())}\n' \ f' &\\to {latex(self.ore_polring())} \\\\\n' \ f' {latex(self.polring().gen())}\n' \ f' &\\mapsto {latex(self.gen())}\n' \ - f'\\end{{align}}' + f'\\end{{align}}\n' \ + f'\\text{{with{{ }}characteristic{{ }}}} ' \ + f'{latex(self.characteristic())}' def _repr_(self): return f'Finite Drinfeld module from {self.polring()} over ' \ From f0a9f630a6f7be1215f883cbb9d07f893ca27cb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 6 May 2022 17:48:53 +0200 Subject: [PATCH 005/197] Refactor FiniteDrinfeldModuleAction --- src/sage/modules/finite_drinfeld_module.py | 31 +++++++++------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/modules/finite_drinfeld_module.py index 5212bb808ff..68ee9630579 100644 --- a/src/sage/modules/finite_drinfeld_module.py +++ b/src/sage/modules/finite_drinfeld_module.py @@ -192,8 +192,8 @@ def rank(self): # Special Sage functions # ########################## - def _get_action_(self, extension): - return FiniteDrinfeldModuleAction(self, extension) + def _get_action_(self): + return FiniteDrinfeldModuleAction(self) def _latex_(self): return f'\\text{{Finite{{ }}{latex(self.polring())}-Drinfeld{{ }}' \ @@ -230,25 +230,19 @@ def polring(self): class FiniteDrinfeldModuleAction(Action): - def __init__(self, finite_drinfeld_module, extension): - # VERIFICATIONS + def __init__(self, finite_drinfeld_module): + # Verifications if not isinstance(finite_drinfeld_module, FiniteDrinfeldModule): raise TypeError('First argument must be a FiniteDrinfeldModule') - if not (extension.is_field() and extension.is_finite() and - finite_drinfeld_module.polring().base_ring().is_subring(extension)): - raise ValueError('The extension must be a finite field ' \ - 'extension of the base field of the Ore polynomial ring') - # WORK + # Work self.__finite_drinfeld_module = finite_drinfeld_module - super().__init__(finite_drinfeld_module.polring(), extension) + super().__init__(finite_drinfeld_module.polring(), + finite_drinfeld_module.ore_polring().base_ring()) ########### # Methods # ########### - def extension(self): - return self.codomain() - def finite_drinfeld_module(self): return self.__finite_drinfeld_module @@ -257,17 +251,16 @@ def finite_drinfeld_module(self): ########################## def _latex_(self): - phi = self.finite_drinfeld_module() - return f'\\text{{Drinfeld{{ }}module{{ }}action{{ }}' \ - f'on{{ }}}}{latex(self.extension())}\\text{{{{ }}' \ - f'induced{{ }}by{{ }}}}{latex(phi)}' + return f'\\text{{Action{{ }}on{{ }}}}' \ + f'{latex(self.extension())}\\text{{{{ }}' \ + f'induced{{ }}by{{ }}}}{self.finite_drinfeld_module()}' def _repr_(self): - return f'Action on {self.domain()} induced by the ' \ + return f'Action on {self.domain()} induced by ' \ f'{self.finite_drinfeld_module()}' def _act_(self, g, x): - return self.finite_drinfeld_module().change_ring(self.extension())(g)(x) + return self.finite_drinfeld_module()(g)(x) def _check_base_fields(Fq, L): From 86348fff6bdd0aec5a414ed07925291cb4e13041 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 5 May 2022 12:52:52 +0200 Subject: [PATCH 006/197] Add various getters for FiniteDrinfeldModule This commit is a bit dirty, apologies. There are new getters (see below), and the commit includes some refactoring of the doctests. The added getters are: - characteristic, - constant_term, - delta (rank two specific), - g (rank two specific), - height, - j (rank two specific). --- src/sage/modules/finite_drinfeld_module.py | 135 ++++++++++++++------- 1 file changed, 88 insertions(+), 47 deletions(-) diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/modules/finite_drinfeld_module.py index 68ee9630579..3f8f9b20a8c 100644 --- a/src/sage/modules/finite_drinfeld_module.py +++ b/src/sage/modules/finite_drinfeld_module.py @@ -36,6 +36,7 @@ from sage.categories.action import Action from sage.categories.homset import Hom from sage.misc.latex import latex +from sage.rings.integer import Integer from sage.rings.morphism import RingHomomorphism_im_gens from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing @@ -57,68 +58,75 @@ class FiniteDrinfeldModule(RingHomomorphism_im_gens): First, create base objects:: - sage: Fq = GF(7^2) + sage: Fq. = GF(3^2) sage: FqX. = Fq[] - sage: L = Fq.extension(3) - sage: frobenius = L.frobenius_endomorphism(2) - sage: Ltau. = OrePolynomialRing(L, frobenius) + sage: p = X^3 + (z2 + 2)*X^2 + (6*z2 + 1)*X + 3*z2 + 5 + sage: L = Fq.extension(6) + sage: Ltau. = OrePolynomialRing(L, L.frobenius_endomorphism(2)) + sage: omega = p.roots(L, multiplicities=False)[0] + sage: phi_X = omega + t + t^2 Then we instanciate the Drinfeld module:: - sage: phi_X = 1 + t^2 - sage: phi = FiniteDrinfeldModule(FqX, phi_X) + sage: phi = FiniteDrinfeldModule(FqX, phi_X, p) sage: phi - Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z6 of size 7^6 generated by t^2 + 1. + Finite Drinfeld module: + Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) + Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 + Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 There are getters for the base objects:: sage: phi.polring() - Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 + Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 sage: phi.ore_polring() - Ore Polynomial Ring in t over Finite Field in z6 of size 7^6 twisted by z6 |--> z6^(7^2) + Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) sage: phi.gen() - t^2 + 1 + t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 And the class inherits `RingHomomorphism_im_gens`, so that one can use:: sage: phi.domain() - Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 + Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 sage: phi.codomain() - Ore Polynomial Ring in t over Finite Field in z6 of size 7^6 twisted by z6 |--> z6^(7^2) + Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) sage: phi.im_gens() - [t^2 + 1] + [t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12] - The rank of the Drinfeld module is retrieved with:: + We can retrieve basic quantities: sage: phi.rank() 2 + sage: phi.j() + 1 .. RUBRIC:: The module law induced by a Drinfeld module The most important feature of Drinfeld modules is that they induce - an `\Fq[X]`-module law on the algebraic closure `\Fqbar` of `\Fq`. - We implement this action for any finite field extension `M` of `L`. - The `_get_action_` method returns an `Action` object:: + an `\Fq[X]`-module law on any subextension of the algebraic closure + `\Fqbar` of `\Fq`. For this, we created the class + `FiniteDrinfeldModuleAction`, which inherits `Action`. For the sake + of simplicity, `phi` will only act on the base field (`L`) of its + Ore polynomial ring. If you want to act on a bigger field, you can + use the method `change_ring`. sage: M = L.extension(2) - sage: action = phi._get_action_(M) - sage: action + sage: phi_M = phi.change_ring(M) # todo: not tested + sage: action = phi._get_action_() # todo: not tested + sage: action # todo: not tested Action on Finite Field in z12 of size 7^12 induced by the Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z6 of size 7^6 generated by t^2 + 1. - sage: x = M.gen() - sage: g = X^3 + X + 5 - sage: action(g, x) + + The calculation of the action is simple: + + sage: x = M.gen() # todo: not tested + sage: g = X^3 + X + 5 # todo: not tested + sage: action(g, x) # todo: not tested ... 6*z12^11 + 5*z12^9 + 5*z12^8 + 2*z12^7 + 6*z12^6 + z12^5 + 6*z12^4 + 2*z12^3 + 3*z12^2 + 5*z12 + 4 - Furthermore, it can be useful to embed a Drinfeld module into a - larger Ore polynomial ring:: - - sage: M = L.extension(2) - sage: psi = phi.change_ring(M); psi - Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z12 of size 7^12 generated by t^2 + 1. - .. NOTE:: The general definition of a Drinfeld module is out of the scope @@ -136,35 +144,37 @@ class FiniteDrinfeldModule(RingHomomorphism_im_gens): :mod:`sage.rings.polynomial.ore_polynomial_ring` """ - def __init__(self, polring, gen): - # VERIFICATIONS - # Check `polring` is an Fq[X]: - # See docstrings of `PolynomialRing_dense_finite_field` and - # `is_PolynomialRing`. - isinstance(polring, PolynomialRing_dense_finite_field) - # Check `gen` is an Ore polynomial: + def __init__(self, polring, gen, characteristic): + # Verifications + if not isinstance(polring, PolynomialRing_dense_finite_field): + raise TypeError('First argument must be a polynomial ring') + if not characteristic in polring: + raise TypeError('The characteristic must be in the polynomial ' \ + 'ring') + if not characteristic.is_prime(): + raise ValueError('The characteristic must be irreducible') if not isinstance(gen, OrePolynomial): raise TypeError('The generator must be an Ore polynomial') - # Now we can define those for convenience: + # We define those for convenience before continuing FqX = polring Ltau = gen.parent() Fq = FqX.base_ring() L = Ltau.base_ring() - # Check the Ore polynomial ring is an L{tau} with L a finite - # field extension of Fq: _check_base_fields(Fq, L) if not Ltau.twisting_derivation() is None: raise ValueError('The Ore polynomial ring should have no ' \ 'derivation') - # Check the frobenius is x -> x^q: if Ltau.twisting_morphism().power() != Fq.degree(): raise ValueError('The twisting morphism of the Ore polynomial ' \ 'ring must be the Frobenius endomorphism of the base ' \ 'field of the polynomial ring') - # The generator is not constant: + if characteristic(gen[0]) != 0: + raise ValueError('The constant coefficient of the generator ' \ + 'must be a root of the characteristic') if gen.is_constant(): raise ValueError('The generator must not be constant') - # ACTUAL WORK + # Finally + self.__characteristic = characteristic super().__init__(Hom(FqX, Ltau), gen) ########### @@ -183,7 +193,30 @@ def change_ring(self, R): new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, names=self.ore_polring().variable_names()) - return FiniteDrinfeldModule(self.polring(), new_ore_polring(self.gen())) + return FiniteDrinfeldModule(self.polring(), + new_ore_polring(self.gen()), self.characteristic()) + + def delta(self): + if self.rank() != 2: + raise ValueError('The rank must equal 2') + return self.gen()[2] + + def g(self): + if self.rank() != 2: + raise ValueError('The rank must equal 2') + return self.gen()[1] + + def height(self): + return Integer(1) + + def j(self): + if self.rank() != 2: + raise ValueError('The j-invariant is only defined for rank 2 ' \ + 'Drinfeld modules') + g = self.gen()[1] + delta = self.gen()[2] + q = self.polring().base_ring().order() + return (g**(q+1)) / delta def rank(self): return self.gen().degree() @@ -196,8 +229,7 @@ def _get_action_(self): return FiniteDrinfeldModuleAction(self) def _latex_(self): - return f'\\text{{Finite{{ }}{latex(self.polring())}-Drinfeld{{ }}' \ - f'module{{ }}defined{{ }}by{{ }}}}\n' \ + return f'\\text{{Finite{{ }}Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}}\n' \ f'\\begin{{align}}\n' \ f' {latex(self.polring())}\n' \ f' &\\to {latex(self.ore_polring())} \\\\\n' \ @@ -208,13 +240,22 @@ def _latex_(self): f'{latex(self.characteristic())}' def _repr_(self): - return f'Finite Drinfeld module from {self.polring()} over ' \ - f'{self.ore_polring().base_ring()} generated by {self.gen()}.' + return f'Finite Drinfeld module:\n' \ + f' Polring: {self.polring()}\n' \ + f' Ore polring: {self.ore_polring()}\n' \ + f' Generator: {self.gen()}\n' \ + f' Characteristic: {self.characteristic()}' ########### # Getters # ########### + def characteristic(self): + return self.__characteristic + + def constant_term(self): + return self.gen()[0] + def frobenius(self): return self.ore_polring().twisting_morphism() From 2c24562b74fdcb23c8844d727a15d8c217ff0ec5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 6 May 2022 17:43:31 +0200 Subject: [PATCH 007/197] Add methods to deal with complex multiplication in FiniteDrinfeldModule The added methods are: - frobenius_trace, - frobenius_norm, - invert (required by frobenius_norm), - is_ordinary, - is_supersingular, - characteristic_polynomial. --- src/sage/modules/finite_drinfeld_module.py | 69 +++++++++++++++++++++- 1 file changed, 68 insertions(+), 1 deletion(-) diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/modules/finite_drinfeld_module.py index 3f8f9b20a8c..5bc6280d694 100644 --- a/src/sage/modules/finite_drinfeld_module.py +++ b/src/sage/modules/finite_drinfeld_module.py @@ -35,12 +35,15 @@ from sage.categories.action import Action from sage.categories.homset import Hom +from sage.matrix.constructor import Matrix from sage.misc.latex import latex +from sage.modules.free_module_element import vector from sage.rings.integer import Integer from sage.rings.morphism import RingHomomorphism_im_gens from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing class FiniteDrinfeldModule(RingHomomorphism_im_gens): @@ -51,6 +54,10 @@ class FiniteDrinfeldModule(RingHomomorphism_im_gens): - ``polring`` -- the base polynomial ring - ``gen`` -- the image of `X` + - ``characteristic`` -- the Fq[X]-characteristic of the Drinfeld + module, i.e. an irreducible polynomial in `polring` that defines a + subextension of L (the base field of the Ore polynomial ring) and + for which the constant coefficient of `gen` is a root. EXAMPLES: @@ -173,10 +180,20 @@ def __init__(self, polring, gen, characteristic): 'must be a root of the characteristic') if gen.is_constant(): raise ValueError('The generator must not be constant') - # Finally + # Work self.__characteristic = characteristic super().__init__(Hom(FqX, Ltau), gen) + ################# + # Private utils # + ################# + + def _Fq(self): + return self.polring().base_ring() + + def _L(self): + return self.ore_polring().base_ring() + ########### # Methods # ########### @@ -196,11 +213,30 @@ def change_ring(self, R): return FiniteDrinfeldModule(self.polring(), new_ore_polring(self.gen()), self.characteristic()) + def characteristic_polynomial(self): + FqXT = PolynomialRing(self.polring(), 'T') + return FqXT([self.frobenius_norm(), self.frobenius_trace(), 1]) + def delta(self): if self.rank() != 2: raise ValueError('The rank must equal 2') return self.gen()[2] + def frobenius_norm(self): + # Notations from Schost-Musleh: + n = self._L().over(self._Fq()).degree_over(self._Fq()) + d = self.characteristic().degree() + m = n // d + norm = self._L().over(self._Fq())(self.delta()).norm() + return ((-1)**n) * (self.characteristic()**m) / norm + + def frobenius_trace(self): + # Notations from Schost-Musleh: + n = self._L().over(self._Fq()).degree_over(self._Fq()) + B = self.frobenius_norm() + t = self.ore_polring().gen() + return self.invert(t**n + (self(B) // t**n)) + def g(self): if self.rank() != 2: raise ValueError('The rank must equal 2') @@ -209,6 +245,37 @@ def g(self): def height(self): return Integer(1) + def invert(self, image): + """ + Given an Ore polynomial `image` of the form `phi(c)`, find c. + """ + if not image in self.ore_polring(): + raise TypeError('The tested image should be in the Ore ' \ + 'polynomial ring') + if image in self._L(): # Only works if `image` is in the image of self + return self._Fq()(image) + r = self.rank() + X = self.polring().gen() + k = image.degree() // r + m_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] + for i in range(k+1): + phi_X_i = self(X**i) + for j in range(i+1): + m_lines[j][i] = phi_X_i[r*j] + m = Matrix(m_lines) + v = vector([list(image)[r*j] for j in range(k+1)]) + pre_image = self.polring()(list((m**(-1)) * v)) + if self(pre_image) == image: + return pre_image + else: + return None + + def is_supersingular(self): + return self.characteristic().divides(self.frobenius_trace()) + + def is_ordinary(self): + return not self.is_supersingular() + def j(self): if self.rank() != 2: raise ValueError('The j-invariant is only defined for rank 2 ' \ From 0499221177f9c76783cd672cb65ae52242a57c42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 10 May 2022 10:46:36 +0200 Subject: [PATCH 008/197] Add methods to deal with isogenies in FiniteDrinfeldModule The added methods are: - is_morphism, - is_endomorphism, - is_automorphism, - is_isogeny, - velu. --- src/sage/modules/finite_drinfeld_module.py | 44 ++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/modules/finite_drinfeld_module.py index 5bc6280d694..86d4d3253da 100644 --- a/src/sage/modules/finite_drinfeld_module.py +++ b/src/sage/modules/finite_drinfeld_module.py @@ -276,6 +276,30 @@ def is_supersingular(self): def is_ordinary(self): return not self.is_supersingular() + def is_morphism(self, candidate): + return candidate == 0 and self.is_isogeny(candidate) + + def is_isogeny(self, candidate): + if not candidate in self.ore_polring(): + raise TypeError('The candidate must be in the Ore polynomial ' \ + 'ring') + if candidate == 0: + return False + elif candidate in self.ore_polring().base_ring(): + return True + else: + return self.characteristic().degree().divides(candidate.valuation()) \ + and candidate.right_divides(candidate * self.gen()) + + def is_endomorphism(self, candidate): + return candidate == 0 or self == self.velu(candidate) + + def is_automorphism(self, candidate): + if not candidate in self.ore_polring(): + raise TypeError('The candidate must be in the Ore polynomial ' \ + 'ring') + return candidate != 0 and candidate in self._Fq() + def j(self): if self.rank() != 2: raise ValueError('The j-invariant is only defined for rank 2 ' \ @@ -288,6 +312,26 @@ def j(self): def rank(self): return self.gen().degree() + def velu(self, candidate): + if not candidate in self.ore_polring(): + raise TypeError('The candidate must be in the Ore polynomial ' \ + 'ring') + # There are two main ways to give the result. The first way is + # to return the Drinfeld module generated by the right-quotient + # of `candidate * self(X)` right-divided by `candidate`. The + # second way is to recursively find the coefficients (see + # arXiv:2203.06970, Eq. 1.1). For now, the former is + # implemented, as it is very easy to write. + if candidate == 0: + return None + if not self.characteristic().degree().divides(candidate.valuation()): + return None + q, r = (candidate * self.gen()).right_quo_rem(candidate) + if r != 0: + return None + else: + return FiniteDrinfeldModule(self.polring(), q, self.characteristic()) + ########################## # Special Sage functions # ########################## From 3609c915438fd89da5686cc310be848b587f672a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 10 May 2022 14:38:36 +0200 Subject: [PATCH 009/197] Move FiniteDrinfeldModule to sage.rings.function_field --- src/sage/modules/all.py | 2 -- src/sage/rings/function_field/all.py | 5 +++++ .../function_field}/finite_drinfeld_module.py | 0 3 files changed, 5 insertions(+), 2 deletions(-) rename src/sage/{modules => rings/function_field}/finite_drinfeld_module.py (100%) diff --git a/src/sage/modules/all.py b/src/sage/modules/all.py index ab41fbcf564..a90258d7ec8 100644 --- a/src/sage/modules/all.py +++ b/src/sage/modules/all.py @@ -29,5 +29,3 @@ lazy_import('sage.modules.multi_filtered_vector_space', 'MultiFilteredVectorSpace') lazy_import('sage.modules.free_quadratic_module_integer_symmetric', 'IntegralLattice') lazy_import('sage.modules.torsion_quadratic_module', 'TorsionQuadraticForm') -lazy_import('sage.modules.finite_drinfeld_module', 'FiniteDrinfeldModule') -lazy_import('sage.modules.finite_drinfeld_module', 'FiniteDrinfeldModuleAction') diff --git a/src/sage/rings/function_field/all.py b/src/sage/rings/function_field/all.py index b6bba3369c3..449d6f3f7a1 100644 --- a/src/sage/rings/function_field/all.py +++ b/src/sage/rings/function_field/all.py @@ -1 +1,6 @@ from .constructor import FunctionField + +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.rings.function_field.finite_drinfeld_module', 'FiniteDrinfeldModule') +lazy_import('sage.rings.function_field.finite_drinfeld_module', 'FiniteDrinfeldModuleAction') diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py similarity index 100% rename from src/sage/modules/finite_drinfeld_module.py rename to src/sage/rings/function_field/finite_drinfeld_module.py From e3fc37f4ab6d0ea5034970751686d891429c6d05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 12 May 2022 15:52:45 +0200 Subject: [PATCH 010/197] Fix method is_morphism in FiniteDrinfeldModule --- src/sage/rings/function_field/finite_drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index 86d4d3253da..b287725d460 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -277,7 +277,7 @@ def is_ordinary(self): return not self.is_supersingular() def is_morphism(self, candidate): - return candidate == 0 and self.is_isogeny(candidate) + return candidate == 0 or self.is_isogeny(candidate) def is_isogeny(self, candidate): if not candidate in self.ore_polring(): From 0515d9b19b550ec27326bf978c9a0e8e555f4e65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 12 May 2022 16:32:01 +0200 Subject: [PATCH 011/197] Add blank line before FiniteDrinfeldModuleAction (PEP8) --- src/sage/rings/function_field/finite_drinfeld_module.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index b287725d460..4f9f21995b5 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -380,6 +380,7 @@ def ore_polring(self): def polring(self): return self.domain() + class FiniteDrinfeldModuleAction(Action): def __init__(self, finite_drinfeld_module): From 8c848d901a85e52e2a05fe0f13a16a25199db812 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 11 May 2022 18:11:50 +0200 Subject: [PATCH 012/197] Refactor doctest in FiniteDrinfeldModule --- .../function_field/finite_drinfeld_module.py | 233 ++++++++++++++---- 1 file changed, 185 insertions(+), 48 deletions(-) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index 4f9f21995b5..37e8e694803 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -53,27 +53,31 @@ class FiniteDrinfeldModule(RingHomomorphism_im_gens): INPUT: - ``polring`` -- the base polynomial ring - - ``gen`` -- the image of `X` + - ``gen`` -- the generator of the Drinfeld module, i.e. the image of `X` in + the Ore polynomial ring - ``characteristic`` -- the Fq[X]-characteristic of the Drinfeld - module, i.e. an irreducible polynomial in `polring` that defines a - subextension of L (the base field of the Ore polynomial ring) and - for which the constant coefficient of `gen` is a root. + module, i.e. the minimal polynomial in `polring` of the constant term of + the generator EXAMPLES: - - .. RUBRIC:: Basics - First, create base objects:: + .. RUBRIC:: Instantiation - sage: Fq. = GF(3^2) + We must first create the base objects:: + + sage: Fq = GF(3^2) + sage: z2 = Fq.gen() sage: FqX. = Fq[] sage: p = X^3 + (z2 + 2)*X^2 + (6*z2 + 1)*X + 3*z2 + 5 sage: L = Fq.extension(6) - sage: Ltau. = OrePolynomialRing(L, L.frobenius_endomorphism(2)) + sage: frob = L.frobenius_endomorphism(2) + sage: Ltau. = OrePolynomialRing(L, frob) sage: omega = p.roots(L, multiplicities=False)[0] sage: phi_X = omega + t + t^2 - Then we instanciate the Drinfeld module:: + Notice that we have freedom on choosing the polynomial `p`, but not + `omega`. It is generally more useful this way. Then we instantiate the + Drinfeld module:: sage: phi = FiniteDrinfeldModule(FqX, phi_X, p) sage: phi @@ -83,67 +87,200 @@ class FiniteDrinfeldModule(RingHomomorphism_im_gens): Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 - There are getters for the base objects:: + .. RUBRIC:: Getters + + With getters, we can retrieve many basic objects associated to a Drinfeld + module. + + First, we can retrieve the polynomial ring, the Ore polynomial ring, and + the generator. Note that the class inherits `RingHomomorphism_im_gens`, so + that `domain`, `codomain` and `im_gens` are available:: sage: phi.polring() Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + sage: phi.domain() is phi.polring() + True sage: phi.ore_polring() Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) + sage: phi.codomain() is phi.ore_polring() + True sage: phi.gen() t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 + sage: phi.im_gens()[0] is phi.gen() + True + + We can retrieve `omega`, the constant term of the generator, and ensure + that it is a root of `p`, the characteristic:: + + sage: phi.constant_term() + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 + sage: phi.constant_term() == omega + True + sage: phi.characteristic() + X^3 + (z2 + 2)*X^2 + X + 2 + sage: phi.characteristic() == p + True + sage: phi.characteristic()(phi.constant_term()) + 0 + + We can retrieve the rank and the height (note that the height is always one + here):: - And the class inherits `RingHomomorphism_im_gens`, so that one can - use:: + sage: phi.rank() + 2 + sage: phi.height() + 1 - sage: phi.domain() - Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - sage: phi.codomain() - Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) - sage: phi.im_gens() - [t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12] + And finally we can retrieve some rank-two specifics:: - We can retrieve basic quantities: + sage: phi.j() # j-invariant + 1 + sage: phi.g() # Standard notation + 1 + sage: phi.delta() # Standard notation + 1 + sage: phi(X) == phi.constant_term() + phi.g()*t + phi.delta()*t^2 + True - sage: phi.rank() - 2 - sage: phi.j() + .. RUBRIC:: Evaluation of the Drinfeld module + + By definition, a Drinfeld module is a ring morphism from an polynomial ring + to an Ore polynomial ring. We can compute the images under this morphism + using the standard `phi(...)` notation:: + + sage: phi(X) + t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 + sage: phi(X) == phi.gen() + True + sage: phi(1) 1 + sage: phi(z2) == z2 + True + sage: phi(X^2 + 1) + t^4 + 2*t^3 + (2*z12^11 + 2*z12^10 + z12^9 + z12^6 + z12^5 + 2*z12^4 + z12^3 + 2*z12^2 + z12 + 2)*t^2 + (2*z12^8 + z12^7 + 2*z12^6 + z12^5 + z12^4 + z12 + 1)*t + 2*z12^11 + 2*z12^10 + z12^8 + z12^7 + 2*z12^6 + 2*z12^5 + z12^4 + 2*z12 .. RUBRIC:: The module law induced by a Drinfeld module - The most important feature of Drinfeld modules is that they induce - an `\Fq[X]`-module law on any subextension of the algebraic closure - `\Fqbar` of `\Fq`. For this, we created the class - `FiniteDrinfeldModuleAction`, which inherits `Action`. For the sake - of simplicity, `phi` will only act on the base field (`L`) of its - Ore polynomial ring. If you want to act on a bigger field, you can - use the method `change_ring`. - - sage: M = L.extension(2) - sage: phi_M = phi.change_ring(M) # todo: not tested - sage: action = phi._get_action_() # todo: not tested - sage: action # todo: not tested - Action on Finite Field in z12 of size 7^12 induced by the Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z6 of size 7^6 - generated by t^2 + 1. + The most important feature of Drinfeld modules is that they endow any + subextension of `Fqbar` with an `Fq[X]`-module law. This action is + represented by the class `FiniteDrinfeldModuleAction`, which inherits + `Action`. For the sake of simplicity, `phi` will only act on the base field + (`L`) of its Ore polynomial ring. If you want to act on a bigger field, you + can define a new Drinfeld module using the method `change_ring`. + + sage: action = phi._get_action_() + sage: action + Action on Finite Field in z12 of size 3^12 induced by Finite Drinfeld module: + Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) + Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 + Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 - The calculation of the action is simple: + The calculation of the action is simple. Careful, the evaluation of Ore + polynomial is, at the moment, experimental:: - sage: x = M.gen() # todo: not tested - sage: g = X^3 + X + 5 # todo: not tested - sage: action(g, x) # todo: not tested + sage: x = L.gen() + 1 + sage: g = X^3 + X + 5 + sage: action(g, x) ... - 6*z12^11 + 5*z12^9 + 5*z12^8 + 2*z12^7 + 6*z12^6 + z12^5 + 6*z12^4 + 2*z12^3 + 3*z12^2 + 5*z12 + 4 + z12^11 + z12^10 + 2*z12^9 + z12^7 + z12^6 + z12^4 + 2*z12^2 + z12 + 1 + + To change ring, use:: + + sage: M = L.extension(5) + sage: psi = phi.change_ring(M) + sage: psi + Finite Drinfeld module: + Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Ore polring: Ore Polynomial Ring in t over Finite Field in z60 of size 3^60 twisted by z60 |--> z60^(3^2) + Generator: t^2 + t + 2*z60^59 + z60^57 + 2*z60^56 + 2*z60^54 + 2*z60^53 + 2*z60^52 + z60^51 + z60^50 + 2*z60^48 + z60^47 + z60^46 + 2*z60^45 + 2*z60^44 + 2*z60^42 + 2*z60^41 + z60^40 + z60^39 + z60^38 + z60^37 + 2*z60^34 + z60^33 + z60^31 + 2*z60^29 + z60^27 + z60^26 + z60^25 + 2*z60^24 + z60^22 + 2*z60^21 + z60^19 + 2*z60^17 + 2*z60^16 + 2*z60^15 + z60^14 + z60^12 + z60^11 + 2*z60^10 + z60^8 + z60^7 + 2*z60^6 + 2*z60^5 + 2*z60 + 1 + Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 + + .. RUBRIC:: Morphisms and isogenies + + Being given an Ore polynomial `m`, we can decide if `m` is a morphism or + isogeny of Drinfeld module whose domain is `phi`:: + + sage: m = phi(X) + sage: phi.is_morphism(m) + True + sage: phi.is_isogeny(m) + True + sage: phi.is_endomorphism(m) + True + sage: phi.is_automorphism(m) + False + sage: m = 0 + sage: phi.is_endomorphism(m) + True + sage: phi.is_automorphism(m) + False + sage: phi.is_isogeny(m) + False + sage: m = t^6 + sage: phi.is_endomorphism(m) + True + sage: phi.is_automorphism(m) + False + + We implemented the Vélu formula for Drinfeld modules, in the sense that + given `m`, we can compute (if it exists) the unique Drinfeld module `psi` + such that `m` is an isogeny from `phi` to `psi`:: + + sage: m = phi(X^2 + 1) + sage: phi.velu(m) + Finite Drinfeld module: + Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) + Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 + Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 + sage: phi.velu(m) == phi + True + sage: z12 = L.gen() + sage: m = (z12^7 + z12^6 + 2*z12^4)*t^3 + sage: phi.is_isogeny(m) + True + sage: phi.is_endomorphism(m) + False + sage: phi.velu(m) + Finite Drinfeld module: + Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) + Generator: (2*z12^10 + z12^9 + 2*z12^7 + 2*z12^6 + z12^3 + 2*z12^2 + 2)*t^2 + (2*z12^9 + z12^7 + 2*z12^5 + z12 + 1)*t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 + Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 + sage: phi.velu(m) == phi + False + + .. RUBRIC:: Complex multiplication of Drinfeld modules + + There are various methods to manipulate the complex multiplication theory + of Drinfeld modules. We can compute the Frobenius norm, Frobenius trace and + characteristic polynomial:: + + sage: phi.frobenius_norm() + X^6 + (2*z2 + 1)*X^5 + (2*z2 + 1)*X^4 + (2*z2 + 2)*X^3 + z2*X^2 + X + 1 + sage: phi.frobenius_trace() + 2*X^3 + (2*z2 + 1)*X^2 + 2*X + z2 + 2 + sage: phi.characteristic_polynomial() + T^2 + (2*X^3 + (2*z2 + 1)*X^2 + 2*X + z2 + 2)*T + X^6 + (2*z2 + 1)*X^5 + (2*z2 + 1)*X^4 + (2*z2 + 2)*X^3 + z2*X^2 + X + 1 + + With those methods, it is easy to decide if a Drinfeld module is ordinary + or supersingular:: + + sage: phi.is_ordinary() + True + sage: phi.is_supersingular() + False - .. NOTE:: + .. NOTE:: - The general definition of a Drinfeld module is out of the scope - of this implementation. + The general definition of a Drinfeld module is out of the scope of this + implementation. :: - You can see all available methods of `RingHomomorphism_im_gens` - with `dir(sage.rings.morphism.RingHomomorphism_im_gens)`. Same - for `Action`. + You can see all available methods of `RingHomomorphism_im_gens` with + `dir(sage.rings.morphism.RingHomomorphism_im_gens)`. Same for `Action`. .. SEEALSO:: :mod:`sage.categories.action.Action` From 7c96d1f73f6d0ee9054eff247ade1a597381a449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 12 May 2022 18:08:02 +0200 Subject: [PATCH 013/197] Alphabetically order methods in sage.rings.function_field.finite_drinfeld_module.py --- .../function_field/finite_drinfeld_module.py | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index 37e8e694803..0c75d78f3f1 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -407,14 +407,14 @@ def invert(self, image): else: return None - def is_supersingular(self): - return self.characteristic().divides(self.frobenius_trace()) - - def is_ordinary(self): - return not self.is_supersingular() + def is_automorphism(self, candidate): + if not candidate in self.ore_polring(): + raise TypeError('The candidate must be in the Ore polynomial ' \ + 'ring') + return candidate != 0 and candidate in self._Fq() - def is_morphism(self, candidate): - return candidate == 0 or self.is_isogeny(candidate) + def is_endomorphism(self, candidate): + return candidate == 0 or self == self.velu(candidate) def is_isogeny(self, candidate): if not candidate in self.ore_polring(): @@ -428,14 +428,14 @@ def is_isogeny(self, candidate): return self.characteristic().degree().divides(candidate.valuation()) \ and candidate.right_divides(candidate * self.gen()) - def is_endomorphism(self, candidate): - return candidate == 0 or self == self.velu(candidate) + def is_morphism(self, candidate): + return candidate == 0 or self.is_isogeny(candidate) - def is_automorphism(self, candidate): - if not candidate in self.ore_polring(): - raise TypeError('The candidate must be in the Ore polynomial ' \ - 'ring') - return candidate != 0 and candidate in self._Fq() + def is_ordinary(self): + return not self.is_supersingular() + + def is_supersingular(self): + return self.characteristic().divides(self.frobenius_trace()) def j(self): if self.rank() != 2: @@ -540,6 +540,9 @@ def finite_drinfeld_module(self): # Special Sage functions # ########################## + def _act_(self, g, x): + return self.finite_drinfeld_module()(g)(x) + def _latex_(self): return f'\\text{{Action{{ }}on{{ }}}}' \ f'{latex(self.extension())}\\text{{{{ }}' \ @@ -549,9 +552,6 @@ def _repr_(self): return f'Action on {self.domain()} induced by ' \ f'{self.finite_drinfeld_module()}' - def _act_(self, g, x): - return self.finite_drinfeld_module()(g)(x) - def _check_base_fields(Fq, L): if not (L.is_field() and L.is_finite() and Fq.is_subring(L)): From 1a7891f0bbbd8486a511407df223a436df3b08f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 13 May 2022 18:00:37 +0200 Subject: [PATCH 014/197] Create class FiniteDrinfeldModule_rank_two Rank two finite Drinfeld modules have specific enough properties to have their own class. Therefore, we created the class `FiniteDrinfeldModule_rank_two`. The following methods were moved from `FiniteDrinfeldModule` to `FiniteDrinfeldModule_rank_two`: - characteristic_polynomial, - delta, - frobenius_norm, - frobenius_trace, - g, - is_ordinary, - is_supersingular. Before this change, those methods were regular methods of just `FiniteDrinfeldModule`; an exception was raised when the rank of the Drinfeld module was not two. Furthermore, the methods `_latex_` and `_repr_` were slightly changed. --- src/sage/rings/function_field/all.py | 2 + .../function_field/finite_drinfeld_module.py | 108 +++++++++++------- 2 files changed, 66 insertions(+), 44 deletions(-) diff --git a/src/sage/rings/function_field/all.py b/src/sage/rings/function_field/all.py index 449d6f3f7a1..3b3f7bfc8bd 100644 --- a/src/sage/rings/function_field/all.py +++ b/src/sage/rings/function_field/all.py @@ -3,4 +3,6 @@ from sage.misc.lazy_import import lazy_import lazy_import('sage.rings.function_field.finite_drinfeld_module', 'FiniteDrinfeldModule') +lazy_import('sage.rings.function_field.finite_drinfeld_module', + 'FiniteDrinfeldModule_rank_two') lazy_import('sage.rings.function_field.finite_drinfeld_module', 'FiniteDrinfeldModuleAction') diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index 0c75d78f3f1..a9be93f93d4 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -350,35 +350,6 @@ def change_ring(self, R): return FiniteDrinfeldModule(self.polring(), new_ore_polring(self.gen()), self.characteristic()) - def characteristic_polynomial(self): - FqXT = PolynomialRing(self.polring(), 'T') - return FqXT([self.frobenius_norm(), self.frobenius_trace(), 1]) - - def delta(self): - if self.rank() != 2: - raise ValueError('The rank must equal 2') - return self.gen()[2] - - def frobenius_norm(self): - # Notations from Schost-Musleh: - n = self._L().over(self._Fq()).degree_over(self._Fq()) - d = self.characteristic().degree() - m = n // d - norm = self._L().over(self._Fq())(self.delta()).norm() - return ((-1)**n) * (self.characteristic()**m) / norm - - def frobenius_trace(self): - # Notations from Schost-Musleh: - n = self._L().over(self._Fq()).degree_over(self._Fq()) - B = self.frobenius_norm() - t = self.ore_polring().gen() - return self.invert(t**n + (self(B) // t**n)) - - def g(self): - if self.rank() != 2: - raise ValueError('The rank must equal 2') - return self.gen()[1] - def height(self): return Integer(1) @@ -431,21 +402,6 @@ def is_isogeny(self, candidate): def is_morphism(self, candidate): return candidate == 0 or self.is_isogeny(candidate) - def is_ordinary(self): - return not self.is_supersingular() - - def is_supersingular(self): - return self.characteristic().divides(self.frobenius_trace()) - - def j(self): - if self.rank() != 2: - raise ValueError('The j-invariant is only defined for rank 2 ' \ - 'Drinfeld modules') - g = self.gen()[1] - delta = self.gen()[2] - q = self.polring().base_ring().order() - return (g**(q+1)) / delta - def rank(self): return self.gen().degree() @@ -518,6 +474,70 @@ def polring(self): return self.domain() +class FiniteDrinfeldModule_rank_two(FiniteDrinfeldModule): + + def __init__(self, polring, gen, characteristic): + if not isinstance(gen, OrePolynomial): + raise TypeError('The generator must be an Ore polynomial') + if gen.degree() != 2: + raise ValueError('The degree of the generator must be 2') + super().__init__(polring, gen, characteristic) + + ########### + # Methods # + ########### + + def characteristic_polynomial(self): + FqXT = PolynomialRing(self.polring(), 'T') + return FqXT([self.frobenius_norm(), self.frobenius_trace(), 1]) + + def frobenius_norm(self): + # Notations from Schost-Musleh: + n = self._L().over(self._Fq()).degree_over(self._Fq()) + d = self.characteristic().degree() + m = n // d + norm = self._L().over(self._Fq())(self.delta()).norm() + return ((-1)**n) * (self.characteristic()**m) / norm + + def frobenius_trace(self): + # Notations from Schost-Musleh: + n = self._L().over(self._Fq()).degree_over(self._Fq()) + B = self.frobenius_norm() + t = self.ore_polring().gen() + return self.invert(t**n + (self(B) // t**n)) + + def is_ordinary(self): + return not self.is_supersingular() + + def is_supersingular(self): + return self.characteristic().divides(self.frobenius_trace()) + + ########################## + # Special Sage functions # + ########################## + + def _repr_(self): + super_repr = super()._repr_() + return f'Rank two f{super_repr[1:]}' + + def _latex_(self): + super_latex = super()._latex_() + return f'{super_latex[:6]}Rank{{ }}two{{ }}f{super_latex[7:]}' + + ########### + # Getters # + ########### + + def delta(self): + return self.gen()[2] + + def g(self): + return self.gen()[1] + + def j(self): + return (self.g()**(q+1)) / self.delta() + + class FiniteDrinfeldModuleAction(Action): def __init__(self, finite_drinfeld_module): From 9bcadf21f86e8e4e6fda8e151ecb33db7acd6552 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 9 Jun 2022 13:27:28 +0200 Subject: [PATCH 015/197] Fix j-invariant method for FiniteDrinfeldModule_rank_two --- src/sage/rings/function_field/finite_drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index a9be93f93d4..cf5b2ded53c 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -535,7 +535,7 @@ def g(self): return self.gen()[1] def j(self): - return (self.g()**(q+1)) / self.delta() + return (self.g()**(self._Fq().order()+1)) / self.delta() class FiniteDrinfeldModuleAction(Action): From 343506477529fab41fd6188b1e23bbcf59ca684f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 16 Jun 2022 11:48:10 +0200 Subject: [PATCH 016/197] Delete class FiniteDrinfeldModule_rank_two --- .../function_field/finite_drinfeld_module.py | 95 ++++++++----------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index cf5b2ded53c..481defdf284 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -331,6 +331,11 @@ def _Fq(self): def _L(self): return self.ore_polring().base_ring() + def _test_rank_two(self): + if self.rank() != 2: + raise NotImplementedError('this method is only available for ' \ + 'rank two Drinfeld modules') + ########### # Methods # ########### @@ -425,6 +430,38 @@ def velu(self, candidate): else: return FiniteDrinfeldModule(self.polring(), q, self.characteristic()) + # Rank two methods + + def characteristic_polynomial(self): + self._test_rank_two() + FqXT = PolynomialRing(self.polring(), 'T') + return FqXT([self.frobenius_norm(), self.frobenius_trace(), 1]) + + def frobenius_norm(self): + self._test_rank_two() + # Notations from Schost-Musleh: + n = self._L().over(self._Fq()).degree_over(self._Fq()) + d = self.characteristic().degree() + m = n // d + norm = self._L().over(self._Fq())(self.delta()).norm() + return ((-1)**n) * (self.characteristic()**m) / norm + + def frobenius_trace(self): + self._test_rank_two() + # Notations from Schost-Musleh: + n = self._L().over(self._Fq()).degree_over(self._Fq()) + B = self.frobenius_norm() + t = self.ore_polring().gen() + return self.invert(t**n + (self(B) // t**n)) + + def is_ordinary(self): + self._test_rank_two() + return not self.is_supersingular() + + def is_supersingular(self): + self._test_rank_two() + return self.characteristic().divides(self.frobenius_trace()) + ########################## # Special Sage functions # ########################## @@ -473,68 +510,18 @@ def ore_polring(self): def polring(self): return self.domain() - -class FiniteDrinfeldModule_rank_two(FiniteDrinfeldModule): - - def __init__(self, polring, gen, characteristic): - if not isinstance(gen, OrePolynomial): - raise TypeError('The generator must be an Ore polynomial') - if gen.degree() != 2: - raise ValueError('The degree of the generator must be 2') - super().__init__(polring, gen, characteristic) - - ########### - # Methods # - ########### - - def characteristic_polynomial(self): - FqXT = PolynomialRing(self.polring(), 'T') - return FqXT([self.frobenius_norm(), self.frobenius_trace(), 1]) - - def frobenius_norm(self): - # Notations from Schost-Musleh: - n = self._L().over(self._Fq()).degree_over(self._Fq()) - d = self.characteristic().degree() - m = n // d - norm = self._L().over(self._Fq())(self.delta()).norm() - return ((-1)**n) * (self.characteristic()**m) / norm - - def frobenius_trace(self): - # Notations from Schost-Musleh: - n = self._L().over(self._Fq()).degree_over(self._Fq()) - B = self.frobenius_norm() - t = self.ore_polring().gen() - return self.invert(t**n + (self(B) // t**n)) - - def is_ordinary(self): - return not self.is_supersingular() - - def is_supersingular(self): - return self.characteristic().divides(self.frobenius_trace()) - - ########################## - # Special Sage functions # - ########################## - - def _repr_(self): - super_repr = super()._repr_() - return f'Rank two f{super_repr[1:]}' - - def _latex_(self): - super_latex = super()._latex_() - return f'{super_latex[:6]}Rank{{ }}two{{ }}f{super_latex[7:]}' - - ########### - # Getters # - ########### + # Rank two methods def delta(self): + self._test_rank_two() return self.gen()[2] def g(self): + self._test_rank_two() return self.gen()[1] def j(self): + self._test_rank_two() return (self.g()**(self._Fq().order()+1)) / self.delta() From a32d290ec526a52c48ed34dd8464f0a070b96c29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 16 Jun 2022 15:22:36 +0200 Subject: [PATCH 017/197] Make FiniteDrinfeldModule inherit CategoryObject --- .../function_field/finite_drinfeld_module.py | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index 481defdf284..dba6444eb97 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -44,9 +44,10 @@ from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.structure.category_object import CategoryObject -class FiniteDrinfeldModule(RingHomomorphism_im_gens): +class FiniteDrinfeldModule(CategoryObject): r""" Class for finite Drinfeld modules. @@ -318,8 +319,12 @@ def __init__(self, polring, gen, characteristic): if gen.is_constant(): raise ValueError('The generator must not be constant') # Work - self.__characteristic = characteristic - super().__init__(Hom(FqX, Ltau), gen) + self._characteristic = characteristic + self._morphism = Hom(FqX, Ltau)(gen) + self._polring = FqX + self._ore_polring = Ltau + self._gen = gen + super().__init__() ################# # Private utils # @@ -340,6 +345,9 @@ def _test_rank_two(self): # Methods # ########### + def __call__(self, a): + return self._morphism(a) + def change_ring(self, R): # VERIFICATIONS if not R.is_field() and R.is_finite(): @@ -492,7 +500,7 @@ def _repr_(self): ########### def characteristic(self): - return self.__characteristic + return self._characteristic def constant_term(self): return self.gen()[0] @@ -501,14 +509,16 @@ def frobenius(self): return self.ore_polring().twisting_morphism() def gen(self): - [gen] = self.im_gens() - return gen + return self._gen + + def morphism(self): + return self._morphism def ore_polring(self): - return self.codomain() + return self._ore_polring def polring(self): - return self.domain() + return self._polring # Rank two methods @@ -524,9 +534,7 @@ def j(self): self._test_rank_two() return (self.g()**(self._Fq().order()+1)) / self.delta() - class FiniteDrinfeldModuleAction(Action): - def __init__(self, finite_drinfeld_module): # Verifications if not isinstance(finite_drinfeld_module, FiniteDrinfeldModule): From 135506941bfa1ad703abe0bd899c9a75f7e65fdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 16 Jun 2022 15:22:56 +0200 Subject: [PATCH 018/197] Fix FiniteDrinfeldModule characteristic polynomial --- src/sage/rings/function_field/finite_drinfeld_module.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index dba6444eb97..31d6e2e3558 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -35,6 +35,7 @@ from sage.categories.action import Action from sage.categories.homset import Hom +from sage.categories.drinfeld_modules import DrinfeldModules from sage.matrix.constructor import Matrix from sage.misc.latex import latex from sage.modules.free_module_element import vector @@ -319,12 +320,12 @@ def __init__(self, polring, gen, characteristic): if gen.is_constant(): raise ValueError('The generator must not be constant') # Work + super().__init__(category=DrinfeldModules(FqX, gen.parent())) self._characteristic = characteristic self._morphism = Hom(FqX, Ltau)(gen) self._polring = FqX self._ore_polring = Ltau self._gen = gen - super().__init__() ################# # Private utils # @@ -443,7 +444,7 @@ def velu(self, candidate): def characteristic_polynomial(self): self._test_rank_two() FqXT = PolynomialRing(self.polring(), 'T') - return FqXT([self.frobenius_norm(), self.frobenius_trace(), 1]) + return FqXT([self.frobenius_norm(), -self.frobenius_trace(), 1]) def frobenius_norm(self): self._test_rank_two() From 927c47c3a1583dd4dbe9bded6af4a869d2e30b65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 16 Jun 2022 16:21:06 +0200 Subject: [PATCH 019/197] Create category DrinfeldModules --- src/sage/categories/drinfeld_modules.py | 62 +++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 src/sage/categories/drinfeld_modules.py diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py new file mode 100644 index 00000000000..14ca0fe1b3a --- /dev/null +++ b/src/sage/categories/drinfeld_modules.py @@ -0,0 +1,62 @@ +r""" +Drinfeld modules +""" +#***************************************************************************** +# Copyright (C) 2022 Xavier Caruso +# Antoine Leudière +# +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +#****************************************************************************** + +from sage.categories.category import CategoryWithParameters + +# from sage.misc.cachefunc import cached_method +# from sage.categories.basic import Fields + +class DrinfeldModules(CategoryWithParameters): + r""" + The category of Drinfeld modules. + + EXAMPLES: + + We create the category of function fields:: + + sage: C = FunctionFields() + sage: C + Category of function fields + + TESTS:: + + sage: TestSuite(FunctionFields()).run() + """ + + def __init__(self, domain, codomain): + r""" + """ + self._domain = domain + self._codomain = codomain + + def _call_(self, phi_X): + r""" + Constructs an object in this category from the data in ``x``, + or throws a TypeError. + """ + return FiniteDrinfeldModule(self._domain, self._codomain(x), characteristic) + + def super_categories(self): + return [] + + def _repr_(self): + return f'Category of Drinfeld modules:\n'\ + f' Domain: {self._domain}\n' \ + f' Codomain: {self._codomain}' + + def _make_named_class_key(self, name): + return self._domain.category() + + class ParentMethods: + pass + + class ElementMethods: + pass From 2537cb3691639719f54ce3935bc03610028f542f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 16 Jun 2022 16:51:16 +0200 Subject: [PATCH 020/197] Define category DrinfeldModules with gamma --- src/sage/categories/drinfeld_modules.py | 28 ++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 14ca0fe1b3a..bc0f15ea313 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -10,6 +10,9 @@ #****************************************************************************** from sage.categories.category import CategoryWithParameters +from sage.misc.functional import log +from sage.rings.polynomial.polynomial_ring import PolynomialRing_general +from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing # from sage.misc.cachefunc import cached_method # from sage.categories.basic import Fields @@ -31,11 +34,30 @@ class DrinfeldModules(CategoryWithParameters): sage: TestSuite(FunctionFields()).run() """ - def __init__(self, domain, codomain): + def __init__(self, gamma, name='t'): r""" """ - self._domain = domain - self._codomain = codomain + self._gamma = gamma + self._domain = FqX = gamma.domain() + K = gamma.codomain() + if not isinstance(FqX, PolynomialRing_general): + raise NotImplementedError('domain must be a polynomial ring') + Fq = FqX.base_ring() + if not Fq.is_field() or not Fq.is_finite() : + raise TypeError('the base ring of the domain must be a finite field') + d = log(Fq.cardinality(), Fq.characteristic()) + tau = K.frobenius_endomorphism(d) + self._codomain = OrePolynomialRing(K, tau, names=name) + # Create characteristic + if K.is_finite(): + f = gamma * FqX.coerce_map_from(Fq) + E = K.over(f) + self._characteristic = E(gamma(FqX.gen())).minpoly() + + def characteristic(self): + if not K.is_finite(): + raise NotImplementedError + return self._characteristic def _call_(self, phi_X): r""" From 445efbe2739fe13640041c2397f506c1d4d13eca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 16 Jun 2022 16:51:30 +0200 Subject: [PATCH 021/197] Change DrinfeldModules _repr_ --- src/sage/categories/drinfeld_modules.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index bc0f15ea313..7a5f057bbc6 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -70,9 +70,7 @@ def super_categories(self): return [] def _repr_(self): - return f'Category of Drinfeld modules:\n'\ - f' Domain: {self._domain}\n' \ - f' Codomain: {self._codomain}' + return f'Category of Drinfeld modules defined by {self._gamma}' def _make_named_class_key(self, name): return self._domain.category() From d032810e44b1a6bd29d97b9bd600481808765c72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 16 Jun 2022 17:33:32 +0200 Subject: [PATCH 022/197] BACKUP COMMIT --- src/sage/categories/drinfeld_modules.py | 23 +++++- .../function_field/finite_drinfeld_module.py | 75 +++++++++---------- 2 files changed, 55 insertions(+), 43 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 7a5f057bbc6..e35b95a18a3 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -49,22 +49,27 @@ def __init__(self, gamma, name='t'): tau = K.frobenius_endomorphism(d) self._codomain = OrePolynomialRing(K, tau, names=name) # Create characteristic + self._characteristic = None if K.is_finite(): f = gamma * FqX.coerce_map_from(Fq) E = K.over(f) self._characteristic = E(gamma(FqX.gen())).minpoly() def characteristic(self): - if not K.is_finite(): + if self._characteristic is None: raise NotImplementedError return self._characteristic - def _call_(self, phi_X): + def _call_(self, gen): r""" Constructs an object in this category from the data in ``x``, or throws a TypeError. """ - return FiniteDrinfeldModule(self._domain, self._codomain(x), characteristic) + from sage.rings.function_field.finite_drinfeld_module import FiniteDrinfeldModule + gen = self._codomain(gen) + if self.characteristic()(gen[0]) != 0: + raise ValueError('incorrect characteristic') + return FiniteDrinfeldModule(self._domain, gen) def super_categories(self): return [] @@ -75,8 +80,18 @@ def _repr_(self): def _make_named_class_key(self, name): return self._domain.category() + def domain(self): + return self._domain + + def codomain(self): + return self._codomain + + def gamma(self): + return self._gamma + class ParentMethods: - pass + def characteristic(self): + return self.category().characteristic() class ElementMethods: pass diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index 31d6e2e3558..6eb18fde0c2 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -46,6 +46,7 @@ from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.structure.category_object import CategoryObject +from sage.structure.sequence import Sequence class FiniteDrinfeldModule(CategoryObject): @@ -290,42 +291,34 @@ class FiniteDrinfeldModule(CategoryObject): :mod:`sage.rings.polynomial.ore_polynomial_ring` """ - def __init__(self, polring, gen, characteristic): - # Verifications - if not isinstance(polring, PolynomialRing_dense_finite_field): - raise TypeError('First argument must be a polynomial ring') - if not characteristic in polring: - raise TypeError('The characteristic must be in the polynomial ' \ - 'ring') - if not characteristic.is_prime(): - raise ValueError('The characteristic must be irreducible') - if not isinstance(gen, OrePolynomial): - raise TypeError('The generator must be an Ore polynomial') - # We define those for convenience before continuing - FqX = polring - Ltau = gen.parent() - Fq = FqX.base_ring() - L = Ltau.base_ring() - _check_base_fields(Fq, L) - if not Ltau.twisting_derivation() is None: - raise ValueError('The Ore polynomial ring should have no ' \ - 'derivation') - if Ltau.twisting_morphism().power() != Fq.degree(): - raise ValueError('The twisting morphism of the Ore polynomial ' \ - 'ring must be the Frobenius endomorphism of the base ' \ - 'field of the polynomial ring') - if characteristic(gen[0]) != 0: - raise ValueError('The constant coefficient of the generator ' \ - 'must be a root of the characteristic') - if gen.is_constant(): - raise ValueError('The generator must not be constant') + def __init__(self, polring, gen, name='t'): + Fq = polring.base_ring() + if isinstance(gen, OrePolynomial): + Ltau = gen.parent() + L = Ltau.base_ring() + name = Ltau.variable_name() + elif isinstance(gen, (list, tuple)): + Ltau = None + L = Sequence(gen).universe() + else: + raise TypeError('generator must be an Ore polynomial or a list of coefficients') + if not L.has_coerce_map_from(Fq): + raise TypeError('base ring of polring must coerce to base ring ' \ + 'of Ore polynomial ring') + gamma = Hom(polring, L)(gen[0]) + category = DrinfeldModules(gamma, name=name) + if Ltau is not None and Ltau is not category.codomain(): + raise ValueError(f'generator must lie in {category.codomain()}') + Ltau = category.codomain() + self._gen = Ltau(gen) + if self._gen.degree() <= 0: + raise ValueError('generator must have positive degree') # Work - super().__init__(category=DrinfeldModules(FqX, gen.parent())) - self._characteristic = characteristic - self._morphism = Hom(FqX, Ltau)(gen) - self._polring = FqX + super().__init__(category=category) + self._morphism = Hom(polring, Ltau)(self._gen) + self._polring = polring self._ore_polring = Ltau - self._gen = gen + self._ore_variable = Ltau.gen() ################# # Private utils # @@ -346,6 +339,11 @@ def _test_rank_two(self): # Methods # ########### + def __eq__(self, other): + if not isinstance(other, FiniteDrinfeldModule): + return False + return self.category() is other.category() and self.gen() == other.gen() + def __call__(self, a): return self._morphism(a) @@ -493,16 +491,12 @@ def _repr_(self): return f'Finite Drinfeld module:\n' \ f' Polring: {self.polring()}\n' \ f' Ore polring: {self.ore_polring()}\n' \ - f' Generator: {self.gen()}\n' \ - f' Characteristic: {self.characteristic()}' + f' Generator: {self.gen()}' \ ########### # Getters # ########### - def characteristic(self): - return self._characteristic - def constant_term(self): return self.gen()[0] @@ -518,6 +512,9 @@ def morphism(self): def ore_polring(self): return self._ore_polring + def ore_variable(self): + return self._ore_variable + def polring(self): return self._polring From 94bad90cd43c800227f5cbdf62595ee67a02c61c Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 21 Jun 2022 12:33:46 +0200 Subject: [PATCH 023/197] characteristic polynomial of Frobenius in all rank --- .../rings/function_field/finite_drinfeld_module.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/finite_drinfeld_module.py index 6eb18fde0c2..55c48eb0772 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/finite_drinfeld_module.py @@ -290,7 +290,6 @@ class FiniteDrinfeldModule(CategoryObject): :mod:`sage.rings.polynomial.ore_polynomial_element` :mod:`sage.rings.polynomial.ore_polynomial_ring` """ - def __init__(self, polring, gen, name='t'): Fq = polring.base_ring() if isinstance(gen, OrePolynomial): @@ -435,7 +434,14 @@ def velu(self, candidate): if r != 0: return None else: - return FiniteDrinfeldModule(self.polring(), q, self.characteristic()) + return FiniteDrinfeldModule(self.polring(), q) + + def frobenius_charpoly(self, var='x'): + # Does not work when Fq is not a prime field... + chi = self._gen.reduced_charpoly() + A = self._polring + S = PolynomialRing(A, name=var) + return -chi(A.gen(), S.gen()) # Rank two methods From 3b47737b087305806a6d07d5f2da595f0183fef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 22 Jun 2022 13:29:11 +0200 Subject: [PATCH 024/197] Change FiniteDrinfeldModule to DrinfeldModule --- src/sage/rings/function_field/all.py | 6 ++-- ..._drinfeld_module.py => drinfeld_module.py} | 36 +++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) rename src/sage/rings/function_field/{finite_drinfeld_module.py => drinfeld_module.py} (95%) diff --git a/src/sage/rings/function_field/all.py b/src/sage/rings/function_field/all.py index 3b3f7bfc8bd..86d2372ff67 100644 --- a/src/sage/rings/function_field/all.py +++ b/src/sage/rings/function_field/all.py @@ -2,7 +2,5 @@ from sage.misc.lazy_import import lazy_import -lazy_import('sage.rings.function_field.finite_drinfeld_module', 'FiniteDrinfeldModule') -lazy_import('sage.rings.function_field.finite_drinfeld_module', - 'FiniteDrinfeldModule_rank_two') -lazy_import('sage.rings.function_field.finite_drinfeld_module', 'FiniteDrinfeldModuleAction') +lazy_import('sage.rings.function_field.drinfeld_module', 'DrinfeldModule') +lazy_import('sage.rings.function_field.drinfeld_module', 'DrinfeldModuleAction') diff --git a/src/sage/rings/function_field/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_module.py similarity index 95% rename from src/sage/rings/function_field/finite_drinfeld_module.py rename to src/sage/rings/function_field/drinfeld_module.py index 6eb18fde0c2..9e11f8c6ad6 100644 --- a/src/sage/rings/function_field/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_module.py @@ -1,7 +1,7 @@ r""" This module provides classes for finite Drinfeld modules -(`FiniteDrinfeldModule`) and their module action on the algebraic -closure of `\Fq` (`FiniteDrinfeldModuleAction`). +(`DrinfeldModule`) and their module action on the algebraic +closure of `\Fq` (`DrinfeldModuleAction`). AUTHORS: @@ -49,7 +49,7 @@ from sage.structure.sequence import Sequence -class FiniteDrinfeldModule(CategoryObject): +class DrinfeldModule(CategoryObject): r""" Class for finite Drinfeld modules. @@ -82,9 +82,9 @@ class FiniteDrinfeldModule(CategoryObject): `omega`. It is generally more useful this way. Then we instantiate the Drinfeld module:: - sage: phi = FiniteDrinfeldModule(FqX, phi_X, p) + sage: phi = DrinfeldModule(FqX, phi_X, p) sage: phi - Finite Drinfeld module: + Drinfeld module: Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 @@ -166,14 +166,14 @@ class FiniteDrinfeldModule(CategoryObject): The most important feature of Drinfeld modules is that they endow any subextension of `Fqbar` with an `Fq[X]`-module law. This action is - represented by the class `FiniteDrinfeldModuleAction`, which inherits + represented by the class `DrinfeldModuleAction`, which inherits `Action`. For the sake of simplicity, `phi` will only act on the base field (`L`) of its Ore polynomial ring. If you want to act on a bigger field, you can define a new Drinfeld module using the method `change_ring`. sage: action = phi._get_action_() sage: action - Action on Finite Field in z12 of size 3^12 induced by Finite Drinfeld module: + Action on Finite Field in z12 of size 3^12 induced by Drinfeld module: Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 @@ -193,7 +193,7 @@ class FiniteDrinfeldModule(CategoryObject): sage: M = L.extension(5) sage: psi = phi.change_ring(M) sage: psi - Finite Drinfeld module: + Drinfeld module: Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 Ore polring: Ore Polynomial Ring in t over Finite Field in z60 of size 3^60 twisted by z60 |--> z60^(3^2) Generator: t^2 + t + 2*z60^59 + z60^57 + 2*z60^56 + 2*z60^54 + 2*z60^53 + 2*z60^52 + z60^51 + z60^50 + 2*z60^48 + z60^47 + z60^46 + 2*z60^45 + 2*z60^44 + 2*z60^42 + 2*z60^41 + z60^40 + z60^39 + z60^38 + z60^37 + 2*z60^34 + z60^33 + z60^31 + 2*z60^29 + z60^27 + z60^26 + z60^25 + 2*z60^24 + z60^22 + 2*z60^21 + z60^19 + 2*z60^17 + 2*z60^16 + 2*z60^15 + z60^14 + z60^12 + z60^11 + 2*z60^10 + z60^8 + z60^7 + 2*z60^6 + 2*z60^5 + 2*z60 + 1 @@ -232,7 +232,7 @@ class FiniteDrinfeldModule(CategoryObject): sage: m = phi(X^2 + 1) sage: phi.velu(m) - Finite Drinfeld module: + Drinfeld module: Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 @@ -246,7 +246,7 @@ class FiniteDrinfeldModule(CategoryObject): sage: phi.is_endomorphism(m) False sage: phi.velu(m) - Finite Drinfeld module: + Drinfeld module: Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) Generator: (2*z12^10 + z12^9 + 2*z12^7 + 2*z12^6 + z12^3 + 2*z12^2 + 2)*t^2 + (2*z12^9 + z12^7 + 2*z12^5 + z12 + 1)*t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 @@ -340,7 +340,7 @@ def _test_rank_two(self): ########### def __eq__(self, other): - if not isinstance(other, FiniteDrinfeldModule): + if not isinstance(other, DrinfeldModule): return False return self.category() is other.category() and self.gen() == other.gen() @@ -359,7 +359,7 @@ def change_ring(self, R): new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, names=self.ore_polring().variable_names()) - return FiniteDrinfeldModule(self.polring(), + return DrinfeldModule(self.polring(), new_ore_polring(self.gen()), self.characteristic()) def height(self): @@ -435,7 +435,7 @@ def velu(self, candidate): if r != 0: return None else: - return FiniteDrinfeldModule(self.polring(), q, self.characteristic()) + return DrinfeldModule(self.polring(), q, self.characteristic()) # Rank two methods @@ -474,7 +474,7 @@ def is_supersingular(self): ########################## def _get_action_(self): - return FiniteDrinfeldModuleAction(self) + return DrinfeldModuleAction(self) def _latex_(self): return f'\\text{{Finite{{ }}Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}}\n' \ @@ -488,7 +488,7 @@ def _latex_(self): f'{latex(self.characteristic())}' def _repr_(self): - return f'Finite Drinfeld module:\n' \ + return f'Drinfeld module:\n' \ f' Polring: {self.polring()}\n' \ f' Ore polring: {self.ore_polring()}\n' \ f' Generator: {self.gen()}' \ @@ -532,11 +532,11 @@ def j(self): self._test_rank_two() return (self.g()**(self._Fq().order()+1)) / self.delta() -class FiniteDrinfeldModuleAction(Action): +class DrinfeldModuleAction(Action): def __init__(self, finite_drinfeld_module): # Verifications - if not isinstance(finite_drinfeld_module, FiniteDrinfeldModule): - raise TypeError('First argument must be a FiniteDrinfeldModule') + if not isinstance(finite_drinfeld_module, DrinfeldModule): + raise TypeError('First argument must be a DrinfeldModule') # Work self.__finite_drinfeld_module = finite_drinfeld_module super().__init__(finite_drinfeld_module.polring(), From 45be70180e9cc59760943399dd9758b81ac47217 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 22 Jun 2022 13:37:39 +0200 Subject: [PATCH 025/197] Clean DrinfeldModules category - Comment constructor - Check that codomain of input morphism is a field - Delete various useless docstrings (they will be written later, at a more final stage of development) - Change gamma to morphism - Various small enhancements --- src/sage/categories/drinfeld_modules.py | 51 +++++++++---------------- 1 file changed, 19 insertions(+), 32 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index e35b95a18a3..a989f86fbc4 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -18,42 +18,31 @@ # from sage.categories.basic import Fields class DrinfeldModules(CategoryWithParameters): - r""" - The category of Drinfeld modules. - EXAMPLES: - - We create the category of function fields:: - - sage: C = FunctionFields() - sage: C - Category of function fields - - TESTS:: - - sage: TestSuite(FunctionFields()).run() - """ - - def __init__(self, gamma, name='t'): - r""" - """ - self._gamma = gamma - self._domain = FqX = gamma.domain() - K = gamma.codomain() + def __init__(self, morphism, name='t'): + self._morphism = morphism + self._domain = morphism.domain() + # Check domain is Fq[X] + FqX = self._domain if not isinstance(FqX, PolynomialRing_general): raise NotImplementedError('domain must be a polynomial ring') Fq = FqX.base_ring() if not Fq.is_field() or not Fq.is_finite() : raise TypeError('the base ring of the domain must be a finite field') + # Check domain is field + K = morphism.codomain() + if not K.is_field(): + raise TypeError('the codomain must be a field') + # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) self._codomain = OrePolynomialRing(K, tau, names=name) # Create characteristic self._characteristic = None if K.is_finite(): - f = gamma * FqX.coerce_map_from(Fq) + f = morphism * FqX.coerce_map_from(Fq) E = K.over(f) - self._characteristic = E(gamma(FqX.gen())).minpoly() + self._characteristic = E(morphism(FqX.gen())).minpoly() def characteristic(self): if self._characteristic is None: @@ -61,21 +50,19 @@ def characteristic(self): return self._characteristic def _call_(self, gen): - r""" - Constructs an object in this category from the data in ``x``, - or throws a TypeError. - """ - from sage.rings.function_field.finite_drinfeld_module import FiniteDrinfeldModule + # Avoid circular import + from sage.rings.function_field.drinfeld_module import DrinfeldModule + # If gen is not in the codomain, an exception is raised gen = self._codomain(gen) if self.characteristic()(gen[0]) != 0: raise ValueError('incorrect characteristic') - return FiniteDrinfeldModule(self._domain, gen) + return DrinfeldModule(self._domain, gen) def super_categories(self): return [] def _repr_(self): - return f'Category of Drinfeld modules defined by {self._gamma}' + return f'Category of Drinfeld modules defined by {self._morphism}' def _make_named_class_key(self, name): return self._domain.category() @@ -86,8 +73,8 @@ def domain(self): def codomain(self): return self._codomain - def gamma(self): - return self._gamma + def morphism(self): + return self._morphism class ParentMethods: def characteristic(self): From 0ccdf26500825e2b778258831466f16939ac74dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 22 Jun 2022 14:13:50 +0200 Subject: [PATCH 026/197] Clean DrinfeldModule constructor Change some variables' name and comment. This includes changing some variables' name in the constructor of the category DrinfeldModules, as to be consistent with the constructor of DrinfeldModule. --- src/sage/categories/drinfeld_modules.py | 10 ++-- .../rings/function_field/drinfeld_module.py | 59 +++++++++++++------ 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index a989f86fbc4..7a169c31a11 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -23,12 +23,14 @@ def __init__(self, morphism, name='t'): self._morphism = morphism self._domain = morphism.domain() # Check domain is Fq[X] - FqX = self._domain - if not isinstance(FqX, PolynomialRing_general): + functions_ring = self._domain # functions_ring + if not isinstance(functions_ring, PolynomialRing_general): raise NotImplementedError('domain must be a polynomial ring') - Fq = FqX.base_ring() - if not Fq.is_field() or not Fq.is_finite() : + functions_ring_base = functions_ring.base_ring() + if not functions_ring_base.is_field() or not functions_ring_base.is_finite() : raise TypeError('the base ring of the domain must be a finite field') + Fq = functions_ring_base + FqX = functions_ring # Check domain is field K = morphism.codomain() if not K.is_field(): diff --git a/src/sage/rings/function_field/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_module.py index 9e11f8c6ad6..adec664c9a5 100644 --- a/src/sage/rings/function_field/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_module.py @@ -291,34 +291,55 @@ class DrinfeldModule(CategoryObject): :mod:`sage.rings.polynomial.ore_polynomial_ring` """ - def __init__(self, polring, gen, name='t'): - Fq = polring.base_ring() + def __init__(self, functions_ring, gen, name='t'): + + # Check all possible input types + # `gen` is an Ore polynomial: if isinstance(gen, OrePolynomial): - Ltau = gen.parent() - L = Ltau.base_ring() - name = Ltau.variable_name() + ore_polring = gen.parent() + ore_polring_base = ore_polring.base_ring() + name = ore_polring.variable_name() + # `gen` is a list of coefficients (functions_ring = Fq[X]): elif isinstance(gen, (list, tuple)): - Ltau = None - L = Sequence(gen).universe() + ore_polring = None + ore_polring_base = Sequence(gen).universe() + # `gen` is a list of list of coefficients (multiple gens): + elif isinstance(gen, (list, tuple)) \ + and all(isinstance(x, (list, tuple)) for x in gen): + ore_polring = None + all_coeffs = [] + for coeffs in gen: + all_coeffs += coeffs + ore_polring_base = Sequence(all_coeffs).universe() else: - raise TypeError('generator must be an Ore polynomial or a list of coefficients') - if not L.has_coerce_map_from(Fq): - raise TypeError('base ring of polring must coerce to base ring ' \ - 'of Ore polynomial ring') - gamma = Hom(polring, L)(gen[0]) + raise TypeError('generator must be a list of coefficients, a list' \ + 'of list of coefficients, or an Ore polynomial') + + # Build the morphism that defines the category + functions_ring_base = functions_ring.base_ring() + if not ore_polring_base.has_coerce_map_from(functions_ring_base): + raise TypeError('base ring of functions_ring must coerce to base ' \ + 'ring of Ore polynomial ring') + gamma = Hom(functions_ring, ore_polring_base)(gen[0]) + + # Mathematical integrity of the data is delegated to the category category = DrinfeldModules(gamma, name=name) - if Ltau is not None and Ltau is not category.codomain(): + # Check gen as Ore polynomial + if ore_polring is not None and ore_polring is not category.codomain(): raise ValueError(f'generator must lie in {category.codomain()}') - Ltau = category.codomain() - self._gen = Ltau(gen) + # Sanity cast + ore_polring = category.codomain() + # Be sure to have a generator that is an Ore polynomial + self._gen = ore_polring(gen) if self._gen.degree() <= 0: raise ValueError('generator must have positive degree') + # Work super().__init__(category=category) - self._morphism = Hom(polring, Ltau)(self._gen) - self._polring = polring - self._ore_polring = Ltau - self._ore_variable = Ltau.gen() + self._morphism = Hom(functions_ring, ore_polring)(self._gen) + self.functions_ring = functions_ring + self._ore_polring = ore_polring + self._ore_variable = ore_polring.gen() ################# # Private utils # From 92b9e68c04a9f39180236428099bd4a9d22b84b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 22 Jun 2022 14:37:38 +0200 Subject: [PATCH 027/197] Change polring to functions_ring in DrinfeldModule --- .../rings/function_field/drinfeld_module.py | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_module.py index adec664c9a5..f525aa1bb52 100644 --- a/src/sage/rings/function_field/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_module.py @@ -337,7 +337,7 @@ def __init__(self, functions_ring, gen, name='t'): # Work super().__init__(category=category) self._morphism = Hom(functions_ring, ore_polring)(self._gen) - self.functions_ring = functions_ring + self._functions_ring = functions_ring self._ore_polring = ore_polring self._ore_variable = ore_polring.gen() @@ -346,7 +346,7 @@ def __init__(self, functions_ring, gen, name='t'): ################# def _Fq(self): - return self.polring().base_ring() + return self.functions_ring().base_ring() def _L(self): return self.ore_polring().base_ring() @@ -375,12 +375,12 @@ def change_ring(self, R): if not self.ore_polring().base_ring().is_subring(R): raise ValueError('The new field must be a finite field ' \ 'extension of the base field of the Ore polynomial ring.') - _check_base_fields(self.polring().base_ring(), R) + _check_base_fields(self.functions_ring().base_ring(), R) # ACTUAL WORK new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, names=self.ore_polring().variable_names()) - return DrinfeldModule(self.polring(), + return DrinfeldModule(self.functions_ring(), new_ore_polring(self.gen()), self.characteristic()) def height(self): @@ -396,7 +396,7 @@ def invert(self, image): if image in self._L(): # Only works if `image` is in the image of self return self._Fq()(image) r = self.rank() - X = self.polring().gen() + X = self.functions_ring().gen() k = image.degree() // r m_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] for i in range(k+1): @@ -405,7 +405,7 @@ def invert(self, image): m_lines[j][i] = phi_X_i[r*j] m = Matrix(m_lines) v = vector([list(image)[r*j] for j in range(k+1)]) - pre_image = self.polring()(list((m**(-1)) * v)) + pre_image = self.functions_ring()(list((m**(-1)) * v)) if self(pre_image) == image: return pre_image else: @@ -456,13 +456,13 @@ def velu(self, candidate): if r != 0: return None else: - return DrinfeldModule(self.polring(), q, self.characteristic()) + return DrinfeldModule(self.functions_ring(), q, self.characteristic()) # Rank two methods def characteristic_polynomial(self): self._test_rank_two() - FqXT = PolynomialRing(self.polring(), 'T') + FqXT = PolynomialRing(self.functions_ring(), 'T') return FqXT([self.frobenius_norm(), -self.frobenius_trace(), 1]) def frobenius_norm(self): @@ -500,9 +500,9 @@ def _get_action_(self): def _latex_(self): return f'\\text{{Finite{{ }}Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}}\n' \ f'\\begin{{align}}\n' \ - f' {latex(self.polring())}\n' \ + f' {latex(self.functions_ring())}\n' \ f' &\\to {latex(self.ore_polring())} \\\\\n' \ - f' {latex(self.polring().gen())}\n' \ + f' {latex(self.functions_ring().gen())}\n' \ f' &\\mapsto {latex(self.gen())}\n' \ f'\\end{{align}}\n' \ f'\\text{{with{{ }}characteristic{{ }}}} ' \ @@ -510,7 +510,7 @@ def _latex_(self): def _repr_(self): return f'Drinfeld module:\n' \ - f' Polring: {self.polring()}\n' \ + f' Polring: {self.functions_ring()}\n' \ f' Ore polring: {self.ore_polring()}\n' \ f' Generator: {self.gen()}' \ @@ -536,8 +536,8 @@ def ore_polring(self): def ore_variable(self): return self._ore_variable - def polring(self): - return self._polring + def functions_ring(self): + return self._functions_ring # Rank two methods From 2b01218c051d92d84f0b85b25e73c479b2e8ec7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 22 Jun 2022 19:20:19 +0200 Subject: [PATCH 028/197] Create morphisms for DrinfeldModule --- src/sage/categories/drinfeld_modules.py | 8 ++++++ .../rings/function_field/drinfeld_module.py | 25 +++++++++++++++++-- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 7a169c31a11..8cf93536c72 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -14,6 +14,8 @@ from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing +from sage.categories.homsets import Homsets + # from sage.misc.cachefunc import cached_method # from sage.categories.basic import Fields @@ -69,6 +71,12 @@ def _repr_(self): def _make_named_class_key(self, name): return self._domain.category() + def Homsets(self): + return Homsets() + + def Endsets(self): + return Homsets() + def domain(self): return self._domain diff --git a/src/sage/rings/function_field/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_module.py index f525aa1bb52..bb2cb5a8c42 100644 --- a/src/sage/rings/function_field/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_module.py @@ -34,8 +34,9 @@ #***************************************************************************** from sage.categories.action import Action -from sage.categories.homset import Hom from sage.categories.drinfeld_modules import DrinfeldModules +from sage.categories.homset import Hom +from sage.categories.homset import Homset from sage.matrix.constructor import Matrix from sage.misc.latex import latex from sage.modules.free_module_element import vector @@ -48,7 +49,6 @@ from sage.structure.category_object import CategoryObject from sage.structure.sequence import Sequence - class DrinfeldModule(CategoryObject): r""" Class for finite Drinfeld modules. @@ -458,6 +458,12 @@ def velu(self, candidate): else: return DrinfeldModule(self.functions_ring(), q, self.characteristic()) + def End(self): + return DrinfeldModuleHomset(self, self) + + def Hom(self, other): + return DrinfeldModuleHomset(self, other) + # Rank two methods def characteristic_polynomial(self): @@ -553,6 +559,21 @@ def j(self): self._test_rank_two() return (self.g()**(self._Fq().order()+1)) / self.delta() +class DrinfeldModuleHomset(Homset): + + def __init__(self, X, Y, base=None, check=True): + if X.category() != Y.category() \ + and not isinstance(X.category(), DrinfeldModules): + raise TypeError('Drinfeld modules must be in the same category') + super().__init__(X, Y, category=Homsets(), base=base, check=check) + + def __contains__(self, candidate): + phi = self.domain() + psi = self.codomain() + if candidate not in phi.ore_polring(): + raise TypeError('morphism must be in the Ore polynomial ring') + return candidate * phi.gen() == psi.gen() * candidate + class DrinfeldModuleAction(Action): def __init__(self, finite_drinfeld_module): # Verifications From b823a5721884dd04c0d50557bb2d31b30af47d6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 22 Jun 2022 19:27:05 +0200 Subject: [PATCH 029/197] Remove is_morphism, etc, methods of DrinfeldModule --- .../rings/function_field/drinfeld_module.py | 24 ------------------- 1 file changed, 24 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_module.py index bb2cb5a8c42..40a920d6456 100644 --- a/src/sage/rings/function_field/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_module.py @@ -411,30 +411,6 @@ def invert(self, image): else: return None - def is_automorphism(self, candidate): - if not candidate in self.ore_polring(): - raise TypeError('The candidate must be in the Ore polynomial ' \ - 'ring') - return candidate != 0 and candidate in self._Fq() - - def is_endomorphism(self, candidate): - return candidate == 0 or self == self.velu(candidate) - - def is_isogeny(self, candidate): - if not candidate in self.ore_polring(): - raise TypeError('The candidate must be in the Ore polynomial ' \ - 'ring') - if candidate == 0: - return False - elif candidate in self.ore_polring().base_ring(): - return True - else: - return self.characteristic().degree().divides(candidate.valuation()) \ - and candidate.right_divides(candidate * self.gen()) - - def is_morphism(self, candidate): - return candidate == 0 or self.is_isogeny(candidate) - def rank(self): return self.gen().degree() From c01845237b3ea2b1f9a07edc3df2bad283a75b75 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 23 Jun 2022 11:59:57 +0200 Subject: [PATCH 030/197] refactor code + classcall_private --- src/sage/rings/function_field/all.py | 7 +- .../{ => drinfeld_modules}/drinfeld_module.py | 258 ++++++------------ 2 files changed, 86 insertions(+), 179 deletions(-) rename src/sage/rings/function_field/{ => drinfeld_modules}/drinfeld_module.py (79%) diff --git a/src/sage/rings/function_field/all.py b/src/sage/rings/function_field/all.py index 86d2372ff67..6904f3ef4fb 100644 --- a/src/sage/rings/function_field/all.py +++ b/src/sage/rings/function_field/all.py @@ -1,6 +1 @@ -from .constructor import FunctionField - -from sage.misc.lazy_import import lazy_import - -lazy_import('sage.rings.function_field.drinfeld_module', 'DrinfeldModule') -lazy_import('sage.rings.function_field.drinfeld_module', 'DrinfeldModuleAction') +from .drinfeld_modules.all import * diff --git a/src/sage/rings/function_field/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py similarity index 79% rename from src/sage/rings/function_field/drinfeld_module.py rename to src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 43a6a7763ca..ddfbec4961e 100644 --- a/src/sage/rings/function_field/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -33,23 +33,20 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.categories.action import Action -from sage.categories.drinfeld_modules import DrinfeldModules -from sage.categories.homset import Hom -from sage.categories.homset import Homset -from sage.matrix.constructor import Matrix -from sage.misc.latex import latex -from sage.modules.free_module_element import vector from sage.rings.integer import Integer -from sage.rings.morphism import RingHomomorphism_im_gens +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.category_object import CategoryObject + +from sage.categories.drinfeld_modules import DrinfeldModules from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing -from sage.rings.polynomial.polynomial_ring import PolynomialRing_dense_finite_field -from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.structure.category_object import CategoryObject + +from sage.misc.latex import latex from sage.structure.sequence import Sequence +from sage.matrix.constructor import Matrix +from sage.modules.free_module_element import vector -class DrinfeldModule(CategoryObject): +class DrinfeldModule(UniqueRepresentation, CategoryObject): r""" Class for finite Drinfeld modules. @@ -290,7 +287,8 @@ class DrinfeldModule(CategoryObject): :mod:`sage.rings.polynomial.ore_polynomial_element` :mod:`sage.rings.polynomial.ore_polynomial_ring` """ - def __init__(self, functions_ring, gen, name='t'): + @staticmethod + def __classcall_private__(cls, functions_ring, gen, name='t'): # Check all possible input types # `gen` is an Ore polynomial: if isinstance(gen, OrePolynomial): @@ -318,9 +316,9 @@ def __init__(self, functions_ring, gen, name='t'): if not ore_polring_base.has_coerce_map_from(functions_ring_base): raise TypeError('base ring of functions_ring must coerce to base ' \ 'ring of Ore polynomial ring') - gamma = Hom(functions_ring, ore_polring_base)(gen[0]) + gamma = functions_ring.hom([ore_polring_base(gen[0])]) - # Mathematical integrity of the data is delegated to the category + # Mathematical integrity of the data is delegated to the category category = DrinfeldModules(gamma, name=name) # Check gen as Ore polynomial if ore_polring is not None and ore_polring is not category.codomain(): @@ -328,32 +326,84 @@ def __init__(self, functions_ring, gen, name='t'): # Sanity cast ore_polring = category.codomain() # Be sure to have a generator that is an Ore polynomial - self._gen = ore_polring(gen) - if self._gen.degree() <= 0: + gen = ore_polring(gen) + if gen.degree() <= 0: raise ValueError('generator must have positive degree') - # Work - super().__init__(category=category) - self._morphism = Hom(functions_ring, ore_polring)(self._gen) - self._functions_ring = functions_ring - self._ore_polring = ore_polring - self._ore_variable = ore_polring.gen() + # Instantiate the appropriate class + if ore_polring_base.is_finite(): + from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule + return FiniteDrinfeldModule(gen, category) + else: + return cls.__classcall__(cls, gen, category) + + def __init__(self, gen, category): + CategoryObject.__init__(self, category=category) + self._functions_ring = category.domain() + self._Fq = self._functions_ring.base_ring() + self._ore_polring = gen.parent() + self._L = self._ore_polring.base_ring() + self._gen = gen + self._morphism = self._functions_ring.hom([gen]) ################# # Private utils # ################# - def _Fq(self): - return self.functions_ring().base_ring() - - def _L(self): - return self.ore_polring().base_ring() - def _test_rank_two(self): if self.rank() != 2: raise NotImplementedError('this method is only available for ' \ 'rank two Drinfeld modules') + ########################## + # Special Sage functions # + ########################## + + def _get_action_(self): + from sage.rings.function_fields.drinfeld_modules.action import DrinfeldModuleAction + return DrinfeldModuleAction(self) + + def _latex_(self): + return f'\\text{{Finite{{ }}Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}}\n' \ + f'\\begin{{align}}\n' \ + f' {latex(self.functions_ring())}\n' \ + f' &\\to {latex(self.ore_polring())} \\\\\n' \ + f' {latex(self.functions_ring().gen())}\n' \ + f' &\\mapsto {latex(self.gen())}\n' \ + f'\\end{{align}}\n' \ + f'\\text{{with{{ }}characteristic{{ }}}} ' \ + f'{latex(self.characteristic())}' + + def _repr_(self): + return "Drinfeld module defined by %s |--> %s over %s" % (self._functions_ring.gen(), self._gen, self._L) + + + ########### + # Getters # + ########### + + def constant_term(self): + return self.gen()[0] + + def frobenius(self): + return self.ore_polring().twisting_morphism() + + def gen(self): + return self._gen + + def morphism(self): + return self._morphism + + def ore_polring(self): + return self._ore_polring + + def ore_variable(self): + return self._ore_polring.gen() + + def functions_ring(self): + return self._functions_ring + + ########### # Methods # ########### @@ -373,7 +423,7 @@ def change_ring(self, R): if not self.ore_polring().base_ring().is_subring(R): raise ValueError('The new field must be a finite field ' \ 'extension of the base field of the Ore polynomial ring.') - _check_base_fields(self.functions_ring().base_ring(), R) + _check_base_fields(self._Fq, R) # ACTUAL WORK new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, @@ -391,8 +441,8 @@ def invert(self, image): if not image in self.ore_polring(): raise TypeError('The tested image should be in the Ore ' \ 'polynomial ring') - if image in self._L(): # Only works if `image` is in the image of self - return self._Fq()(image) + if image in self._L: # Only works if `image` is in the image of self + return self._Fq(image) r = self.rank() X = self.functions_ring().gen() k = image.degree() // r @@ -432,102 +482,12 @@ def velu(self, candidate): else: return FiniteDrinfeldModule(self.polring(), q) - def frobenius_charpoly(self, var='x'): - # Does not work when Fq is not a prime field... - chi = self._gen.reduced_charpoly() - A = self._polring - S = PolynomialRing(A, name=var) - return -chi(A.gen(), S.gen()) - - def End(self): - return DrinfeldModuleHomset(self, self) - - def Hom(self, other): + def _Hom_(self, other): + from sage.rings.function_fields.drinfeld_modules.morphism import DrinfeldModuleHomset return DrinfeldModuleHomset(self, other) # Rank two methods - def characteristic_polynomial(self): - self._test_rank_two() - FqXT = PolynomialRing(self.functions_ring(), 'T') - return FqXT([self.frobenius_norm(), -self.frobenius_trace(), 1]) - - def frobenius_norm(self): - self._test_rank_two() - # Notations from Schost-Musleh: - n = self._L().over(self._Fq()).degree_over(self._Fq()) - d = self.characteristic().degree() - m = n // d - norm = self._L().over(self._Fq())(self.delta()).norm() - return ((-1)**n) * (self.characteristic()**m) / norm - - def frobenius_trace(self): - self._test_rank_two() - # Notations from Schost-Musleh: - n = self._L().over(self._Fq()).degree_over(self._Fq()) - B = self.frobenius_norm() - t = self.ore_polring().gen() - return self.invert(t**n + (self(B) // t**n)) - - def is_ordinary(self): - self._test_rank_two() - return not self.is_supersingular() - - def is_supersingular(self): - self._test_rank_two() - return self.characteristic().divides(self.frobenius_trace()) - - ########################## - # Special Sage functions # - ########################## - - def _get_action_(self): - return DrinfeldModuleAction(self) - - def _latex_(self): - return f'\\text{{Finite{{ }}Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}}\n' \ - f'\\begin{{align}}\n' \ - f' {latex(self.functions_ring())}\n' \ - f' &\\to {latex(self.ore_polring())} \\\\\n' \ - f' {latex(self.functions_ring().gen())}\n' \ - f' &\\mapsto {latex(self.gen())}\n' \ - f'\\end{{align}}\n' \ - f'\\text{{with{{ }}characteristic{{ }}}} ' \ - f'{latex(self.characteristic())}' - - def _repr_(self): - return f'Drinfeld module:\n' \ - f' Polring: {self.functions_ring()}\n' \ - f' Ore polring: {self.ore_polring()}\n' \ - f' Generator: {self.gen()}' \ - - ########### - # Getters # - ########### - - def constant_term(self): - return self.gen()[0] - - def frobenius(self): - return self.ore_polring().twisting_morphism() - - def gen(self): - return self._gen - - def morphism(self): - return self._morphism - - def ore_polring(self): - return self._ore_polring - - def ore_variable(self): - return self._ore_variable - - def functions_ring(self): - return self._functions_ring - - # Rank two methods - def delta(self): self._test_rank_two() return self.gen()[2] @@ -538,55 +498,7 @@ def g(self): def j(self): self._test_rank_two() - return (self.g()**(self._Fq().order()+1)) / self.delta() - -class DrinfeldModuleHomset(Homset): - - def __init__(self, X, Y, base=None, check=True): - if X.category() != Y.category() \ - and not isinstance(X.category(), DrinfeldModules): - raise TypeError('Drinfeld modules must be in the same category') - super().__init__(X, Y, category=Homsets(), base=base, check=check) - - def __contains__(self, candidate): - phi = self.domain() - psi = self.codomain() - if candidate not in phi.ore_polring(): - raise TypeError('morphism must be in the Ore polynomial ring') - return candidate * phi.gen() == psi.gen() * candidate - -class DrinfeldModuleAction(Action): - def __init__(self, finite_drinfeld_module): - # Verifications - if not isinstance(finite_drinfeld_module, DrinfeldModule): - raise TypeError('First argument must be a DrinfeldModule') - # Work - self.__finite_drinfeld_module = finite_drinfeld_module - super().__init__(finite_drinfeld_module.polring(), - finite_drinfeld_module.ore_polring().base_ring()) - - ########### - # Methods # - ########### - - def finite_drinfeld_module(self): - return self.__finite_drinfeld_module - - ########################## - # Special Sage functions # - ########################## - - def _act_(self, g, x): - return self.finite_drinfeld_module()(g)(x) - - def _latex_(self): - return f'\\text{{Action{{ }}on{{ }}}}' \ - f'{latex(self.extension())}\\text{{{{ }}' \ - f'induced{{ }}by{{ }}}}{self.finite_drinfeld_module()}' - - def _repr_(self): - return f'Action on {self.domain()} induced by ' \ - f'{self.finite_drinfeld_module()}' + return (self.g()**(self._Fq.order()+1)) / self.delta() def _check_base_fields(Fq, L): From b26c8138ca4cf610880efb25e19ac0037825e9c8 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 23 Jun 2022 12:00:24 +0200 Subject: [PATCH 031/197] with all files --- .../drinfeld_modules/__init__.py | 0 .../function_field/drinfeld_modules/action.py | 37 +++++++++++++++++ .../function_field/drinfeld_modules/all.py | 3 ++ .../finite_drinfeld_module.py | 40 +++++++++++++++++++ .../drinfeld_modules/morphism.py | 15 +++++++ 5 files changed, 95 insertions(+) create mode 100644 src/sage/rings/function_field/drinfeld_modules/__init__.py create mode 100644 src/sage/rings/function_field/drinfeld_modules/action.py create mode 100644 src/sage/rings/function_field/drinfeld_modules/all.py create mode 100644 src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py create mode 100644 src/sage/rings/function_field/drinfeld_modules/morphism.py diff --git a/src/sage/rings/function_field/drinfeld_modules/__init__.py b/src/sage/rings/function_field/drinfeld_modules/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py new file mode 100644 index 00000000000..06ed070c7f5 --- /dev/null +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -0,0 +1,37 @@ +from sage.categories.action import Action + +from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule + + +class DrinfeldModuleAction(Action): + def __init__(self, finite_drinfeld_module): + # Verifications + if not isinstance(finite_drinfeld_module, DrinfeldModule): + raise TypeError('First argument must be a DrinfeldModule') + # Work + self.__finite_drinfeld_module = finite_drinfeld_module + super().__init__(finite_drinfeld_module.polring(), + finite_drinfeld_module.ore_polring().base_ring()) + + ########### + # Methods # + ########### + + def finite_drinfeld_module(self): + return self.__finite_drinfeld_module + + ########################## + # Special Sage functions # + ########################## + + def _act_(self, g, x): + return self.finite_drinfeld_module()(g)(x) + + def _latex_(self): + return f'\\text{{Action{{ }}on{{ }}}}' \ + f'{latex(self.extension())}\\text{{{{ }}' \ + f'induced{{ }}by{{ }}}}{self.finite_drinfeld_module()}' + + def _repr_(self): + return f'Action on {self.domain()} induced by ' \ + f'{self.finite_drinfeld_module()}' diff --git a/src/sage/rings/function_field/drinfeld_modules/all.py b/src/sage/rings/function_field/drinfeld_modules/all.py new file mode 100644 index 00000000000..71d56f75a35 --- /dev/null +++ b/src/sage/rings/function_field/drinfeld_modules/all.py @@ -0,0 +1,3 @@ +from sage.misc.lazy_import import lazy_import + +lazy_import('sage.rings.function_field.drinfeld_modules.drinfeld_module', 'DrinfeldModule') diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py new file mode 100644 index 00000000000..b4fb43079c0 --- /dev/null +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -0,0 +1,40 @@ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule + +class FiniteDrinfeldModule(DrinfeldModule): + def frobenius_charpoly(self, var='x'): + # Does not work when Fq is not a prime field... + chi = self._gen.reduced_charpoly() + A = self._polring + S = PolynomialRing(A, name=var) + return -chi(A.gen(), S.gen()) + + def characteristic_polynomial(self): + self._test_rank_two() + FqXT = PolynomialRing(self.functions_ring(), 'T') + return FqXT([self.frobenius_norm(), -self.frobenius_trace(), 1]) + + def frobenius_norm(self): + self._test_rank_two() + # Notations from Schost-Musleh: + n = self._L.over(self._Fq).degree_over(self._Fq) + d = self.characteristic().degree() + m = n // d + norm = self._L.over(self._Fq)(self.delta()).norm() + return ((-1)**n) * (self.characteristic()**m) / norm + + def frobenius_trace(self): + self._test_rank_two() + # Notations from Schost-Musleh: + n = self._L.over(self._Fq).degree_over(self._Fq) + B = self.frobenius_norm() + t = self.ore_polring().gen() + return self.invert(t**n + (self(B) // t**n)) + + def is_ordinary(self): + self._test_rank_two() + return not self.is_supersingular() + + def is_supersingular(self): + self._test_rank_two() + return self.characteristic().divides(self.frobenius_trace()) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py new file mode 100644 index 00000000000..a9d128646f0 --- /dev/null +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -0,0 +1,15 @@ +from sage.categories.homset import Homset, Hom + +class DrinfeldModuleHomset(Homset): + def __init__(self, X, Y, base=None, check=True): + if X.category() != Y.category() \ + and not isinstance(X.category(), DrinfeldModules): + raise NotImplementedError('Drinfeld modules must be in the same category') + Homset.__init__(self, X, Y, base=base, check=check) + + def __contains__(self, candidate): + phi = self.domain() + psi = self.codomain() + if candidate not in phi.ore_polring(): + raise TypeError('morphism must be in the Ore polynomial ring') + return candidate * phi.gen() == psi.gen() * candidate From 22e4f83972f3e1f37c5f934a2cb851307834ced0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 23 Jun 2022 18:09:05 +0200 Subject: [PATCH 032/197] Rename morphism.py to homset.py --- .../function_field/drinfeld_modules/{morphism.py => homset.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/sage/rings/function_field/drinfeld_modules/{morphism.py => homset.py} (100%) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/homset.py similarity index 100% rename from src/sage/rings/function_field/drinfeld_modules/morphism.py rename to src/sage/rings/function_field/drinfeld_modules/homset.py From a9848aec0fa50d13cfa47adb3729a22d7f543406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 23 Jun 2022 21:03:26 +0200 Subject: [PATCH 033/197] Fix DrinfeldModuleHomset import in DrinfeldModule --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index ddfbec4961e..498e14d118e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -483,7 +483,7 @@ def velu(self, candidate): return FiniteDrinfeldModule(self.polring(), q) def _Hom_(self, other): - from sage.rings.function_fields.drinfeld_modules.morphism import DrinfeldModuleHomset + from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset return DrinfeldModuleHomset(self, other) # Rank two methods From 1bbad0c8d10a11bfcf006cd61bd64a29dee3aa58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 29 Jun 2022 19:51:29 +0200 Subject: [PATCH 034/197] Create DrinfeldModuleMorphism Comments: - `DrinfeldModuleMorphism` inherits `Element` and not `Morphism`, as the latter requires the domain to be a parent. - `DrinfeldModuleHomset` was modified to integrate `DrinfeldModuleMorphism`. The method `__contains__` only tests the parents. - The method `_Hom_` of `DrinfeldModule` is fixed and one can use `Hom(phi, psi)`. - The method `base` for the category `DrinfeldModules` was implemented. It returns the base ring of the Ore pol. ring; this may change. --- src/sage/categories/drinfeld_modules.py | 3 + .../drinfeld_modules/drinfeld_module.py | 6 +- .../finite_drinfeld_module.py | 1 + .../function_field/drinfeld_modules/homset.py | 43 ++++++++++---- .../drinfeld_modules/morphism.py | 58 +++++++++++++++++++ 5 files changed, 95 insertions(+), 16 deletions(-) create mode 100644 src/sage/rings/function_field/drinfeld_modules/morphism.py diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 8cf93536c72..dcb0e985efc 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -48,6 +48,9 @@ def __init__(self, morphism, name='t'): E = K.over(f) self._characteristic = E(morphism(FqX.gen())).minpoly() + def base(self): + return self.codomain().base_ring() + def characteristic(self): if self._characteristic is None: raise NotImplementedError diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 498e14d118e..aaa997ebd94 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -377,7 +377,6 @@ def _latex_(self): def _repr_(self): return "Drinfeld module defined by %s |--> %s over %s" % (self._functions_ring.gen(), self._gen, self._L) - ########### # Getters # ########### @@ -403,7 +402,6 @@ def ore_variable(self): def functions_ring(self): return self._functions_ring - ########### # Methods # ########### @@ -482,9 +480,9 @@ def velu(self, candidate): else: return FiniteDrinfeldModule(self.polring(), q) - def _Hom_(self, other): + def _Hom_(self, other, category): from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset - return DrinfeldModuleHomset(self, other) + return DrinfeldModuleHomset(self, other, category) # Rank two methods diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index b4fb43079c0..5cd98685031 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -2,6 +2,7 @@ from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule class FiniteDrinfeldModule(DrinfeldModule): + def frobenius_charpoly(self, var='x'): # Does not work when Fq is not a prime field... chi = self._gen.reduced_charpoly() diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index a9d128646f0..a7176994572 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -1,15 +1,34 @@ +from sage.structure.parent import Parent +from sage.categories.drinfeld_modules import DrinfeldModules +from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism from sage.categories.homset import Homset, Hom class DrinfeldModuleHomset(Homset): - def __init__(self, X, Y, base=None, check=True): - if X.category() != Y.category() \ - and not isinstance(X.category(), DrinfeldModules): - raise NotImplementedError('Drinfeld modules must be in the same category') - Homset.__init__(self, X, Y, base=base, check=check) - - def __contains__(self, candidate): - phi = self.domain() - psi = self.codomain() - if candidate not in phi.ore_polring(): - raise TypeError('morphism must be in the Ore polynomial ring') - return candidate * phi.gen() == psi.gen() * candidate + + Element = DrinfeldModuleMorphism + __contains__ = Parent.__contains__ + + def __init__(self, X, Y, category=None, check=True): + if category is None: + category = X.category() + if check: + if X.category() != Y.category() \ + or not isinstance(X.category(), DrinfeldModules): + raise NotImplementedError('Drinfeld modules must be in the same category') + if category != X.category(): + raise NotImplementedError('category should be DrinfeldModules') + base = category.base() + Homset.__init__(self, X, Y, category=category, base=base, check=check) + + def __contains__(self, x): + try: + x = self(x) + return True + except (AttributeError, ValueError, TypeError): + return False + + def _element_constructor_(self, *args, **kwds): + return self.element_class(self, *args, **kwds) + + def _repr_(self): + return 'Our homset' diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py new file mode 100644 index 00000000000..a8da28b6d80 --- /dev/null +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -0,0 +1,58 @@ +#***************************************************************************** +# Copyright (C) 2022 Antoine Leudière +# +# 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. +# http://www.gnu.org/licenses/ +#***************************************************************************** + +# from sage.categories.morphism import Morphism +from sage.structure.element import Element +from sage.rings.polynomial.ore_polynomial_element import OrePolynomial +from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing +from sage.categories.drinfeld_modules import DrinfeldModules + + +class DrinfeldModuleMorphism(Element): + + def __init__(self, parent, x): + super().__init__(parent) + domain = parent.domain() + codomain = parent.codomain() + if x.parent() is parent: + ore_polynomial = x.ore_polynomial() + else: + ore_polynomial = domain.ore_polring()(x) + # Test well-definition of the morphism + if domain.gen() * ore_polynomial != ore_polynomial * codomain.gen(): + raise ValueError('the Ore polynomial does not define a morphism') + # Instantiation + self._domain = domain + self._codomain = codomain + self._ore_polynomial = ore_polynomial + + def _repr_(self): + return f'Drinfeld Module morphism:\n' \ + f' From: {self._domain}\n' \ + f' To: {self._codomain}\n' \ + f' Defn: {self._ore_polynomial}' + + def codomain(self): + return self._codomain + + def domain(self): + return self._domain + + def ore_polynomial(self): + return self._ore_polynomial + + def is_zero(self): + return self._ore_polynomial.is_zero() + + def is_isogeny(self): + return not self.is_zero() + + def ore_polynomial(self): + return self._ore_polynomial From 578ccaac3e98db2284e67deb21e55db798b7e238 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 11:29:32 +0200 Subject: [PATCH 035/197] Rename method functions_ring to function_ring in DrinfeldModule --- .../drinfeld_modules/drinfeld_module.py | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index aaa997ebd94..5a615cc125d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -288,14 +288,14 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): :mod:`sage.rings.polynomial.ore_polynomial_ring` """ @staticmethod - def __classcall_private__(cls, functions_ring, gen, name='t'): + def __classcall_private__(cls, function_ring, gen, name='t'): # Check all possible input types # `gen` is an Ore polynomial: if isinstance(gen, OrePolynomial): ore_polring = gen.parent() ore_polring_base = ore_polring.base_ring() name = ore_polring.variable_name() - # `gen` is a list of coefficients (functions_ring = Fq[X]): + # `gen` is a list of coefficients (function_ring = Fq[X]): elif isinstance(gen, (list, tuple)): ore_polring = None ore_polring_base = Sequence(gen).universe() @@ -312,11 +312,11 @@ def __classcall_private__(cls, functions_ring, gen, name='t'): 'of list of coefficients, or an Ore polynomial') # Build the morphism that defines the category - functions_ring_base = functions_ring.base_ring() - if not ore_polring_base.has_coerce_map_from(functions_ring_base): - raise TypeError('base ring of functions_ring must coerce to base ' \ + function_ring_base = function_ring.base_ring() + if not ore_polring_base.has_coerce_map_from(function_ring_base): + raise TypeError('base ring of function_ring must coerce to base ' \ 'ring of Ore polynomial ring') - gamma = functions_ring.hom([ore_polring_base(gen[0])]) + gamma = function_ring.hom([ore_polring_base(gen[0])]) # Mathematical integrity of the data is delegated to the category category = DrinfeldModules(gamma, name=name) @@ -339,12 +339,12 @@ def __classcall_private__(cls, functions_ring, gen, name='t'): def __init__(self, gen, category): CategoryObject.__init__(self, category=category) - self._functions_ring = category.domain() - self._Fq = self._functions_ring.base_ring() + self._function_ring = category.domain() + self._Fq = self._function_ring.base_ring() self._ore_polring = gen.parent() self._L = self._ore_polring.base_ring() self._gen = gen - self._morphism = self._functions_ring.hom([gen]) + self._morphism = self._function_ring.hom([gen]) ################# # Private utils # @@ -366,16 +366,16 @@ def _get_action_(self): def _latex_(self): return f'\\text{{Finite{{ }}Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}}\n' \ f'\\begin{{align}}\n' \ - f' {latex(self.functions_ring())}\n' \ + f' {latex(self.function_ring())}\n' \ f' &\\to {latex(self.ore_polring())} \\\\\n' \ - f' {latex(self.functions_ring().gen())}\n' \ + f' {latex(self.function_ring().gen())}\n' \ f' &\\mapsto {latex(self.gen())}\n' \ f'\\end{{align}}\n' \ f'\\text{{with{{ }}characteristic{{ }}}} ' \ f'{latex(self.characteristic())}' def _repr_(self): - return "Drinfeld module defined by %s |--> %s over %s" % (self._functions_ring.gen(), self._gen, self._L) + return "Drinfeld module defined by %s |--> %s over %s" % (self._function_ring.gen(), self._gen, self._L) ########### # Getters # @@ -399,8 +399,8 @@ def ore_polring(self): def ore_variable(self): return self._ore_polring.gen() - def functions_ring(self): - return self._functions_ring + def function_ring(self): + return self._function_ring ########### # Methods # @@ -426,7 +426,7 @@ def change_ring(self, R): new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, names=self.ore_polring().variable_names()) - return DrinfeldModule(self.functions_ring(), + return DrinfeldModule(self.function_ring(), new_ore_polring(self.gen()), self.characteristic()) def height(self): @@ -442,7 +442,7 @@ def invert(self, image): if image in self._L: # Only works if `image` is in the image of self return self._Fq(image) r = self.rank() - X = self.functions_ring().gen() + X = self.function_ring().gen() k = image.degree() // r m_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] for i in range(k+1): @@ -451,7 +451,7 @@ def invert(self, image): m_lines[j][i] = phi_X_i[r*j] m = Matrix(m_lines) v = vector([list(image)[r*j] for j in range(k+1)]) - pre_image = self.functions_ring()(list((m**(-1)) * v)) + pre_image = self.function_ring()(list((m**(-1)) * v)) if self(pre_image) == image: return pre_image else: From b2e597830b6e25adaddbe7de6fa247eb9e4d5ea2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 11:38:08 +0200 Subject: [PATCH 036/197] Fix frobenius_charpoly method in FiniteDrinfeldModule Refactor frobenius_charpoly --- .../drinfeld_modules/finite_drinfeld_module.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 5cd98685031..2d229625795 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -3,17 +3,13 @@ class FiniteDrinfeldModule(DrinfeldModule): - def frobenius_charpoly(self, var='x'): + def frobenius_charpoly(self, var='T'): + A = self._function_ring # Fq[X] + S = PolynomialRing(A, name=var) # Fq[X][T] # Does not work when Fq is not a prime field... - chi = self._gen.reduced_charpoly() - A = self._polring - S = PolynomialRing(A, name=var) - return -chi(A.gen(), S.gen()) - - def characteristic_polynomial(self): - self._test_rank_two() - FqXT = PolynomialRing(self.functions_ring(), 'T') - return FqXT([self.frobenius_norm(), -self.frobenius_trace(), 1]) + # chi = self._gen.reduced_charpoly() + # return -chi(A.gen(), S.gen()) + return S([self.frobenius_norm(), -self.frobenius_trace(), 1]) def frobenius_norm(self): self._test_rank_two() From 72f16428bbbc209cd0dac6aa9edb8946533d3dda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 14:24:15 +0200 Subject: [PATCH 037/197] Remove __eq__ method in DrinfeldModule This was useless as the class inherits UniqueRepresentation. --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 5a615cc125d..8ab1b4c5227 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -406,11 +406,6 @@ def function_ring(self): # Methods # ########### - def __eq__(self, other): - if not isinstance(other, DrinfeldModule): - return False - return self.category() is other.category() and self.gen() == other.gen() - def __call__(self, a): return self._morphism(a) From 8f9c18ad0b2f96c78aaa18f2b812b8f4b8730598 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 14:43:05 +0200 Subject: [PATCH 038/197] Refactor characteristic method in DrinfeldModules The characteristic is computed as a minpoly. It is cast to the polring. Also, some minor cosmetic changes that should be somewhere else. Refactor _latex_ for DrinfeldModule --- src/sage/categories/drinfeld_modules.py | 22 ++++++++++--------- .../drinfeld_modules/drinfeld_module.py | 13 ++++------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index dcb0e985efc..a64c59ea693 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -22,19 +22,21 @@ class DrinfeldModules(CategoryWithParameters): def __init__(self, morphism, name='t'): + gamma = morphism self._morphism = morphism - self._domain = morphism.domain() + self._domain = gamma.domain() # Check domain is Fq[X] - functions_ring = self._domain # functions_ring - if not isinstance(functions_ring, PolynomialRing_general): + function_ring = self._domain + if not isinstance(function_ring, PolynomialRing_general): raise NotImplementedError('domain must be a polynomial ring') - functions_ring_base = functions_ring.base_ring() - if not functions_ring_base.is_field() or not functions_ring_base.is_finite() : + function_ring_base = function_ring.base_ring() + if not function_ring_base.is_field() or not function_ring_base.is_finite() : raise TypeError('the base ring of the domain must be a finite field') - Fq = functions_ring_base - FqX = functions_ring + Fq = function_ring_base + FqX = function_ring + X = FqX.gen() # Check domain is field - K = morphism.codomain() + K = gamma.codomain() if not K.is_field(): raise TypeError('the codomain must be a field') # Build K{t} @@ -44,9 +46,9 @@ def __init__(self, morphism, name='t'): # Create characteristic self._characteristic = None if K.is_finite(): - f = morphism * FqX.coerce_map_from(Fq) + f = gamma * FqX.coerce_map_from(Fq) # Fq -> K E = K.over(f) - self._characteristic = E(morphism(FqX.gen())).minpoly() + self._characteristic = FqX(E(gamma(X)).minpoly()) def base(self): return self.codomain().base_ring() diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 8ab1b4c5227..e46c3657894 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -364,15 +364,10 @@ def _get_action_(self): return DrinfeldModuleAction(self) def _latex_(self): - return f'\\text{{Finite{{ }}Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}}\n' \ - f'\\begin{{align}}\n' \ - f' {latex(self.function_ring())}\n' \ - f' &\\to {latex(self.ore_polring())} \\\\\n' \ - f' {latex(self.function_ring().gen())}\n' \ - f' &\\mapsto {latex(self.gen())}\n' \ - f'\\end{{align}}\n' \ - f'\\text{{with{{ }}characteristic{{ }}}} ' \ - f'{latex(self.characteristic())}' + return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ + f'{latex(self._function_ring.gen())} '\ + f'\\mapsto {latex(self._gen)}' \ + f'\\text{{{{ }}over{{ }}}}{latex(self._L)}' def _repr_(self): return "Drinfeld module defined by %s |--> %s over %s" % (self._function_ring.gen(), self._gen, self._L) From 1c81b5ba55fd963edf3d8d54aaafd6c5a63018f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 15:05:55 +0200 Subject: [PATCH 039/197] Remove a constructor in DrinfeldModule Remove the constructor with a list of lists of coefficients, as it bloats the code but is not useful as long as the function ring can only be a polring. --- .../drinfeld_modules/drinfeld_module.py | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index e46c3657894..5654dd7bec2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -299,17 +299,9 @@ def __classcall_private__(cls, function_ring, gen, name='t'): elif isinstance(gen, (list, tuple)): ore_polring = None ore_polring_base = Sequence(gen).universe() - # `gen` is a list of list of coefficients (multiple gens): - elif isinstance(gen, (list, tuple)) \ - and all(isinstance(x, (list, tuple)) for x in gen): - ore_polring = None - all_coeffs = [] - for coeffs in gen: - all_coeffs += coeffs - ore_polring_base = Sequence(all_coeffs).universe() else: - raise TypeError('generator must be a list of coefficients, a list' \ - 'of list of coefficients, or an Ore polynomial') + raise TypeError('generator must be a list of coefficients '\ + 'or an Ore polynomial') # Build the morphism that defines the category function_ring_base = function_ring.base_ring() From 3fe93dabbee77523861d975ea03aa3de165d1835 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 15:24:05 +0200 Subject: [PATCH 040/197] Change _L to _base_ring in DrinfeldModule --- .../drinfeld_modules/drinfeld_module.py | 16 ++++++++++------ .../drinfeld_modules/finite_drinfeld_module.py | 6 +++--- .../function_field/drinfeld_modules/homset.py | 3 --- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 5654dd7bec2..f193ec1c178 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -331,12 +331,12 @@ def __classcall_private__(cls, function_ring, gen, name='t'): def __init__(self, gen, category): CategoryObject.__init__(self, category=category) + self._base_ring = category.base() self._function_ring = category.domain() - self._Fq = self._function_ring.base_ring() - self._ore_polring = gen.parent() - self._L = self._ore_polring.base_ring() self._gen = gen self._morphism = self._function_ring.hom([gen]) + self._ore_polring = gen.parent() + self._Fq = self._function_ring.base_ring() ################# # Private utils # @@ -359,15 +359,19 @@ def _latex_(self): return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ f'{latex(self._function_ring.gen())} '\ f'\\mapsto {latex(self._gen)}' \ - f'\\text{{{{ }}over{{ }}}}{latex(self._L)}' + f'\\text{{{{ }}over{{ }}}}{latex(self._base_ring)}' def _repr_(self): - return "Drinfeld module defined by %s |--> %s over %s" % (self._function_ring.gen(), self._gen, self._L) + return f'Drinfeld module defined by {self._function_ring.gen()} ' \ + f'|--> {self._gen} over {self._base_ring}' ########### # Getters # ########### + def base_ring(self): + return self._base_ring + def constant_term(self): return self.gen()[0] @@ -421,7 +425,7 @@ def invert(self, image): if not image in self.ore_polring(): raise TypeError('The tested image should be in the Ore ' \ 'polynomial ring') - if image in self._L: # Only works if `image` is in the image of self + if image in self._base_ring: # Only works if `image` is in the image of self return self._Fq(image) r = self.rank() X = self.function_ring().gen() diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 2d229625795..ca5f83b20ee 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -14,16 +14,16 @@ def frobenius_charpoly(self, var='T'): def frobenius_norm(self): self._test_rank_two() # Notations from Schost-Musleh: - n = self._L.over(self._Fq).degree_over(self._Fq) + n = self._base_ring.over(self._Fq).degree_over(self._Fq) d = self.characteristic().degree() m = n // d - norm = self._L.over(self._Fq)(self.delta()).norm() + norm = self._base_ring.over(self._Fq)(self.delta()).norm() return ((-1)**n) * (self.characteristic()**m) / norm def frobenius_trace(self): self._test_rank_two() # Notations from Schost-Musleh: - n = self._L.over(self._Fq).degree_over(self._Fq) + n = self._base_ring.over(self._Fq).degree_over(self._Fq) B = self.frobenius_norm() t = self.ore_polring().gen() return self.invert(t**n + (self(B) // t**n)) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index a7176994572..d4ccf4880c8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -29,6 +29,3 @@ def __contains__(self, x): def _element_constructor_(self, *args, **kwds): return self.element_class(self, *args, **kwds) - - def _repr_(self): - return 'Our homset' From 01a42cce1c21207955db5a874235276713eaafe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 15:35:46 +0200 Subject: [PATCH 041/197] Fix velu method in DrinfeldModule --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index f193ec1c178..eb4636d9dff 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -464,7 +464,7 @@ def velu(self, candidate): if r != 0: return None else: - return FiniteDrinfeldModule(self.polring(), q) + return DrinfeldModule(self._function_ring, q) def _Hom_(self, other, category): from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset From c8049b0a4b538028f4db1421e9e4ec75e3bd2cff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 15:40:26 +0200 Subject: [PATCH 042/197] Remove duplicate code in DrinfeldModuleMorphism --- src/sage/rings/function_field/drinfeld_modules/morphism.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index a8da28b6d80..0adb5fa18e3 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -53,6 +53,3 @@ def is_zero(self): def is_isogeny(self): return not self.is_zero() - - def ore_polynomial(self): - return self._ore_polynomial From aeeea81d5ca73a08be2ea0b37ffe079dee2ef97a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 16:02:00 +0200 Subject: [PATCH 043/197] Create frobenius_endomorphism method in FiniteDrinfeldModule --- .../drinfeld_modules/finite_drinfeld_module.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index ca5f83b20ee..d6d55fa4706 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -3,6 +3,13 @@ class FiniteDrinfeldModule(DrinfeldModule): + def frobenius_endomorphism(self): + t = self.ore_variable() + L = self._base_ring + Fq = self._function_ring.base_ring() + deg = L.over(Fq).degree(Fq) + return self._Hom_(self, category=self.category())(t**deg) + def frobenius_charpoly(self, var='T'): A = self._function_ring # Fq[X] S = PolynomialRing(A, name=var) # Fq[X][T] From 055e46ca33d556027615139187fc8c2d39f3bddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 16:41:54 +0200 Subject: [PATCH 044/197] Comment out frobenius method in DrinfeldModule --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index eb4636d9dff..87f240472ac 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -375,8 +375,8 @@ def base_ring(self): def constant_term(self): return self.gen()[0] - def frobenius(self): - return self.ore_polring().twisting_morphism() + # def frobenius(self): + # return self.ore_polring().twisting_morphism() def gen(self): return self._gen From f8fdad5022fabf4328607e9252de0556aa2c2a24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 18:01:50 +0200 Subject: [PATCH 045/197] Write first draft of DrinfeldModule docstring --- .../drinfeld_modules/drinfeld_module.py | 566 +++++++++--------- 1 file changed, 275 insertions(+), 291 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 87f240472ac..eebb64d5b8c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1,26 +1,10 @@ r""" -This module provides classes for finite Drinfeld modules -(`DrinfeldModule`) and their module action on the algebraic -closure of `\Fq` (`DrinfeldModuleAction`). +Drinfeld modules AUTHORS: -- Antoine Leudière (2022-04): initial version - -Let `\tau` be the `\Fq`-linear Frobenius endomorphism of `\Fqbar` -defined by `x \mapsto x^q`. Let `L\{\tau\}` be the ring of Ore -polynomials in `\tau` with coefficients in L. Fix an element `\omega` in -`L` (global parameter). A finite Drinfeld module is an `\Fq`-algebra -morphism `\phi: \Fq[X] \to L\{\tau\]` such that: - - the constant coefficient of `\phi(X)` is `\omega`, - - there exists at least one `a \in \Fq[X]` such that `\phi(a)` has a - non zero `\tau`-degree. - -As an `\Fq[X]`-algebra morphism, a finite Drinfeld module is only -determined by the image of `X`. - -Crucially, the Drinfeld module `\phi` gives rise to the `\Fq[X]`-module -law on `\Fqbar` defined by `(a, x) = \phi(a)(x)`. +- Antoine Leudière (2022-04) +- Xavier Caruso (2022-06) """ #***************************************************************************** @@ -47,246 +31,6 @@ from sage.modules.free_module_element import vector class DrinfeldModule(UniqueRepresentation, CategoryObject): - r""" - Class for finite Drinfeld modules. - - INPUT: - - - ``polring`` -- the base polynomial ring - - ``gen`` -- the generator of the Drinfeld module, i.e. the image of `X` in - the Ore polynomial ring - - ``characteristic`` -- the Fq[X]-characteristic of the Drinfeld - module, i.e. the minimal polynomial in `polring` of the constant term of - the generator - - EXAMPLES: - - .. RUBRIC:: Instantiation - - We must first create the base objects:: - - sage: Fq = GF(3^2) - sage: z2 = Fq.gen() - sage: FqX. = Fq[] - sage: p = X^3 + (z2 + 2)*X^2 + (6*z2 + 1)*X + 3*z2 + 5 - sage: L = Fq.extension(6) - sage: frob = L.frobenius_endomorphism(2) - sage: Ltau. = OrePolynomialRing(L, frob) - sage: omega = p.roots(L, multiplicities=False)[0] - sage: phi_X = omega + t + t^2 - - Notice that we have freedom on choosing the polynomial `p`, but not - `omega`. It is generally more useful this way. Then we instantiate the - Drinfeld module:: - - sage: phi = DrinfeldModule(FqX, phi_X, p) - sage: phi - Drinfeld module: - Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) - Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 - Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 - - .. RUBRIC:: Getters - - With getters, we can retrieve many basic objects associated to a Drinfeld - module. - - First, we can retrieve the polynomial ring, the Ore polynomial ring, and - the generator. Note that the class inherits `RingHomomorphism_im_gens`, so - that `domain`, `codomain` and `im_gens` are available:: - - sage: phi.polring() - Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - sage: phi.domain() is phi.polring() - True - sage: phi.ore_polring() - Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) - sage: phi.codomain() is phi.ore_polring() - True - sage: phi.gen() - t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 - sage: phi.im_gens()[0] is phi.gen() - True - - We can retrieve `omega`, the constant term of the generator, and ensure - that it is a root of `p`, the characteristic:: - - sage: phi.constant_term() - z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 - sage: phi.constant_term() == omega - True - sage: phi.characteristic() - X^3 + (z2 + 2)*X^2 + X + 2 - sage: phi.characteristic() == p - True - sage: phi.characteristic()(phi.constant_term()) - 0 - - We can retrieve the rank and the height (note that the height is always one - here):: - - sage: phi.rank() - 2 - sage: phi.height() - 1 - - And finally we can retrieve some rank-two specifics:: - - sage: phi.j() # j-invariant - 1 - sage: phi.g() # Standard notation - 1 - sage: phi.delta() # Standard notation - 1 - sage: phi(X) == phi.constant_term() + phi.g()*t + phi.delta()*t^2 - True - - .. RUBRIC:: Evaluation of the Drinfeld module - - By definition, a Drinfeld module is a ring morphism from an polynomial ring - to an Ore polynomial ring. We can compute the images under this morphism - using the standard `phi(...)` notation:: - - sage: phi(X) - t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 - sage: phi(X) == phi.gen() - True - sage: phi(1) - 1 - sage: phi(z2) == z2 - True - sage: phi(X^2 + 1) - t^4 + 2*t^3 + (2*z12^11 + 2*z12^10 + z12^9 + z12^6 + z12^5 + 2*z12^4 + z12^3 + 2*z12^2 + z12 + 2)*t^2 + (2*z12^8 + z12^7 + 2*z12^6 + z12^5 + z12^4 + z12 + 1)*t + 2*z12^11 + 2*z12^10 + z12^8 + z12^7 + 2*z12^6 + 2*z12^5 + z12^4 + 2*z12 - - .. RUBRIC:: The module law induced by a Drinfeld module - - The most important feature of Drinfeld modules is that they endow any - subextension of `Fqbar` with an `Fq[X]`-module law. This action is - represented by the class `DrinfeldModuleAction`, which inherits - `Action`. For the sake of simplicity, `phi` will only act on the base field - (`L`) of its Ore polynomial ring. If you want to act on a bigger field, you - can define a new Drinfeld module using the method `change_ring`. - - sage: action = phi._get_action_() - sage: action - Action on Finite Field in z12 of size 3^12 induced by Drinfeld module: - Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) - Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 - Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 - - The calculation of the action is simple. Careful, the evaluation of Ore - polynomial is, at the moment, experimental:: - - sage: x = L.gen() + 1 - sage: g = X^3 + X + 5 - sage: action(g, x) - ... - z12^11 + z12^10 + 2*z12^9 + z12^7 + z12^6 + z12^4 + 2*z12^2 + z12 + 1 - - To change ring, use:: - - sage: M = L.extension(5) - sage: psi = phi.change_ring(M) - sage: psi - Drinfeld module: - Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - Ore polring: Ore Polynomial Ring in t over Finite Field in z60 of size 3^60 twisted by z60 |--> z60^(3^2) - Generator: t^2 + t + 2*z60^59 + z60^57 + 2*z60^56 + 2*z60^54 + 2*z60^53 + 2*z60^52 + z60^51 + z60^50 + 2*z60^48 + z60^47 + z60^46 + 2*z60^45 + 2*z60^44 + 2*z60^42 + 2*z60^41 + z60^40 + z60^39 + z60^38 + z60^37 + 2*z60^34 + z60^33 + z60^31 + 2*z60^29 + z60^27 + z60^26 + z60^25 + 2*z60^24 + z60^22 + 2*z60^21 + z60^19 + 2*z60^17 + 2*z60^16 + 2*z60^15 + z60^14 + z60^12 + z60^11 + 2*z60^10 + z60^8 + z60^7 + 2*z60^6 + 2*z60^5 + 2*z60 + 1 - Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 - - .. RUBRIC:: Morphisms and isogenies - - Being given an Ore polynomial `m`, we can decide if `m` is a morphism or - isogeny of Drinfeld module whose domain is `phi`:: - - sage: m = phi(X) - sage: phi.is_morphism(m) - True - sage: phi.is_isogeny(m) - True - sage: phi.is_endomorphism(m) - True - sage: phi.is_automorphism(m) - False - sage: m = 0 - sage: phi.is_endomorphism(m) - True - sage: phi.is_automorphism(m) - False - sage: phi.is_isogeny(m) - False - sage: m = t^6 - sage: phi.is_endomorphism(m) - True - sage: phi.is_automorphism(m) - False - - We implemented the Vélu formula for Drinfeld modules, in the sense that - given `m`, we can compute (if it exists) the unique Drinfeld module `psi` - such that `m` is an isogeny from `phi` to `psi`:: - - sage: m = phi(X^2 + 1) - sage: phi.velu(m) - Drinfeld module: - Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) - Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 - Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 - sage: phi.velu(m) == phi - True - sage: z12 = L.gen() - sage: m = (z12^7 + z12^6 + 2*z12^4)*t^3 - sage: phi.is_isogeny(m) - True - sage: phi.is_endomorphism(m) - False - sage: phi.velu(m) - Drinfeld module: - Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) - Generator: (2*z12^10 + z12^9 + 2*z12^7 + 2*z12^6 + z12^3 + 2*z12^2 + 2)*t^2 + (2*z12^9 + z12^7 + 2*z12^5 + z12 + 1)*t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 - Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 - sage: phi.velu(m) == phi - False - - .. RUBRIC:: Complex multiplication of Drinfeld modules - - There are various methods to manipulate the complex multiplication theory - of Drinfeld modules. We can compute the Frobenius norm, Frobenius trace and - characteristic polynomial:: - - sage: phi.frobenius_norm() - X^6 + (2*z2 + 1)*X^5 + (2*z2 + 1)*X^4 + (2*z2 + 2)*X^3 + z2*X^2 + X + 1 - sage: phi.frobenius_trace() - 2*X^3 + (2*z2 + 1)*X^2 + 2*X + z2 + 2 - sage: phi.characteristic_polynomial() - T^2 + (2*X^3 + (2*z2 + 1)*X^2 + 2*X + z2 + 2)*T + X^6 + (2*z2 + 1)*X^5 + (2*z2 + 1)*X^4 + (2*z2 + 2)*X^3 + z2*X^2 + X + 1 - - With those methods, it is easy to decide if a Drinfeld module is ordinary - or supersingular:: - - sage: phi.is_ordinary() - True - sage: phi.is_supersingular() - False - - .. NOTE:: - - The general definition of a Drinfeld module is out of the scope of this - implementation. - - :: - - You can see all available methods of `RingHomomorphism_im_gens` with - `dir(sage.rings.morphism.RingHomomorphism_im_gens)`. Same for `Action`. - - .. SEEALSO:: - :mod:`sage.categories.action.Action` - :mod:`sage.rings.polynomial.ore_polynomial_element` - :mod:`sage.rings.polynomial.ore_polynomial_ring` - """ @staticmethod def __classcall_private__(cls, function_ring, gen, name='t'): # Check all possible input types @@ -304,9 +48,8 @@ def __classcall_private__(cls, function_ring, gen, name='t'): 'or an Ore polynomial') # Build the morphism that defines the category - function_ring_base = function_ring.base_ring() - if not ore_polring_base.has_coerce_map_from(function_ring_base): - raise TypeError('base ring of function_ring must coerce to base ' \ + if not ore_polring_base.has_coerce_map_from(function_ring.base_ring()): + raise TypeError('base ring of function ring must coerce to base ' \ 'ring of Ore polynomial ring') gamma = function_ring.hom([ore_polring_base(gen[0])]) @@ -343,9 +86,11 @@ def __init__(self, gen, category): ################# def _test_rank_two(self): + r""" + Raise ``NotImplementedError`` if the rank is not two. + """ if self.rank() != 2: - raise NotImplementedError('this method is only available for ' \ - 'rank two Drinfeld modules') + raise NotImplementedError('the rank must be 2') ########################## # Special Sage functions # @@ -370,27 +115,105 @@ def _repr_(self): ########### def base_ring(self): + r""" + Return the base ring of the Drinfeld module. + + This is always a field. This is the base field of the codomain + (Ore polynomial ring) of the morphism that defines the Drinfeld + module. + + A Drinfeld module is said to be finite if the base ring is + finite. + + OUTPUT: + + - a field + """ return self._base_ring def constant_term(self): + r""" + Return the constant term of the generator (`\phi_X`). + + The `A`-characteristic of the base field (see + :meth:`sage.categories.drinfeld_modules.DrinfeldModules.characteristic`) + is the minimal polynomial of this constant term, over the base + ring of the function ring. Equivalently, the constant term is + the image, by the morphism (`\gamma`) that defines the category, + of the generator (`X`) of the polynomial ring. + + OUTPUT: + + - an element in the base ring + """ return self.gen()[0] # def frobenius(self): # return self.ore_polring().twisting_morphism() def gen(self): + r""" + Return the generator (`\phi_X`) of the Drinfeld module. + + This method makes sense because, in our case, the function ring is + a polynomial ring; it is generated by one element, whose image + characterizes the morphism that defines the Drinfeld module. + + OUTPUT: + + - an Ore polynomial + """ return self._gen def morphism(self): + r""" + Return the morphism object that defines the Drinfeld module. + + OUTPUT: + + - a ring morphism, from the function ring to the Ore polynomial + ring + """ return self._morphism def ore_polring(self): + r""" + Return the Ore polynomial ring of the Drinfeld module. + + If the Drinfeld module is defined by a morphism `A \to + K\{\tau\}`, we return the codomain `K\{\tau\}`. + + OUTPUT: + + - an Ore polynomial ring + """ return self._ore_polring def ore_variable(self): + r""" + Return the Ore variable. + + This is generator of the Ore polynomial ring. + + OUTPUT: + + - an Ore polynomial + """ return self._ore_polring.gen() def function_ring(self): + r""" + Return the function ring of the Drinfeld module. + + If the Drinfeld module is defined by a morphism `A \to + K\{\tau\}`, we return the domain `A`. + + In our case, this is a polynomial ring. + + OUTPUT: + + - a polynomial ring + """ return self._function_ring ########### @@ -398,17 +221,52 @@ def function_ring(self): ########### def __call__(self, a): + r""" + Return the image of ``a`` by the morphism that defines the + Drinfeld module, i.e. `\phi_a` if the Drinfeld module is denoted + `phi`. + + INPUT: + + - ``a`` -- the element in the function ring whose image is to + compute + + OUTPUT: + + - an element of the base ring + """ + return self._morphism(a) - def change_ring(self, R): - # VERIFICATIONS + def change_ring(self, new_field): + r""" + If ``new_field`` is a field extension of the base ring, return a + new Drinfeld module that extends ``self`` to the base ring + ``new_field``. + + Let `f` be the morphism that defines ``self``, let `i` be the + inclusion of ``self.ore_polring()`` into the Ore pol. ring whose + base is ``new_field``. The morphism that defines the new + Drinfeld module is the composition `i \circ f`. + + INPUT: + + - ``new_field`` -- the field extension of the base ring that + serves as base ring for the new Drinfeld module + + OUTPUT: + + - a Drinfeld module + """ + # TODO: Remove _check_base_field + R = new_field if not R.is_field() and R.is_finite(): raise TypeError('Argument must be a finite field') if not self.ore_polring().base_ring().is_subring(R): raise ValueError('The new field must be a finite field ' \ 'extension of the base field of the Ore polynomial ring.') _check_base_fields(self._Fq, R) - # ACTUAL WORK + new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, names=self.ore_polring().variable_names()) @@ -416,71 +274,197 @@ def change_ring(self, R): new_ore_polring(self.gen()), self.characteristic()) def height(self): - return Integer(1) + r""" + Return the height of the Drinfeld module. - def invert(self, image): + When the function ring is a polynomial ring, the height is 1. + + OUTPUT: + + - an integer """ - Given an Ore polynomial `image` of the form `phi(c)`, find c. + return Integer(1) + + def invert(self, ore_pol): + r""" + Find the inverse of ``ore_pol`` by the morphism that defines the + Drinfeld module. If ``ore_pol`` is not in the image of the + morphism, return ``None``. + + Said otherwise, return `a` if ``ore_pol`` is `phi_a`, otherwise + return ``None``. + + INPUT: + + - ``ore_pol`` -- the Ore polynomial whose preimage we want to + compute + + OUTPUT: + + - a polynomial + + ALGORITHM: + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO """ - if not image in self.ore_polring(): - raise TypeError('The tested image should be in the Ore ' \ - 'polynomial ring') - if image in self._base_ring: # Only works if `image` is in the image of self - return self._Fq(image) + if not ore_pol in self.ore_polring(): + raise TypeError('ore_pol must be an Ore polynomial ring') + if ore_pol in self._base_ring: + return self._Fq(ore_pol) r = self.rank() X = self.function_ring().gen() - k = image.degree() // r + k = ore_pol.degree() // r m_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] for i in range(k+1): phi_X_i = self(X**i) for j in range(i+1): m_lines[j][i] = phi_X_i[r*j] m = Matrix(m_lines) - v = vector([list(image)[r*j] for j in range(k+1)]) + v = vector([list(ore_pol)[r*j] for j in range(k+1)]) pre_image = self.function_ring()(list((m**(-1)) * v)) - if self(pre_image) == image: + if self(pre_image) == ore_pol: return pre_image else: return None def rank(self): + r""" + Return the rank of the Drinfeld module. + + When the function ring is a polynomial ring, the rank is the + degree of the generator. + + OUTPUT: + + - an integer + """ return self.gen().degree() def velu(self, candidate): + r""" + Return a new Drinfeld module such that ``candidate`` is an + isogeny to this module with domain ``self``. If no such isogeny + exists, return ``None``. + + If the candidate is zero, return ``None``, as an isogeny is + required to be non zero. + + INPUT: + + - ``candidate`` -- an Ore polynomial that defines the isogeny + with domain ``self`` and codomain the output of the method + + OUTPUT: + + - a Drinfeld module + + ALGORITHM: + + We write the Ore Euclidean division `\phi_X = + \mathrm{candidate}*q + r`, and return + The candidate is an isogeny if only if: + + 1. The degree of the characteristic devides the height + of the candidate. (The height of an Ore polynomial + `P(t)` is the maximum `n` such that `t^n` right-divides + `P(t)`.) + + 2. The candidate right-divides the generator, which can + be tested with Euclidean division. + + We test if the candidate is an isogeny, and, if it is, we + return the quotient of the Euclidean division. + + Height and Euclidean division of Ore polynomials are + implemented as methods of class + :class:`sage.rings.polynomial.ore_polynomial_element.OrePolynomial`. + + Another possible algorithm is to recursively solve a system, + see :arxiv:`2203.06970`, eq. 1.1. + """ if not candidate in self.ore_polring(): - raise TypeError('The candidate must be in the Ore polynomial ' \ - 'ring') - # There are two main ways to give the result. The first way is - # to return the Drinfeld module generated by the right-quotient - # of `candidate * self(X)` right-divided by `candidate`. The - # second way is to recursively find the coefficients (see - # arXiv:2203.06970, Eq. 1.1). For now, the former is - # implemented, as it is very easy to write. + raise TypeError('candidate must be an Ore polynomial') if candidate == 0: return None if not self.characteristic().degree().divides(candidate.valuation()): return None - q, r = (candidate * self.gen()).right_quo_rem(candidate) - if r != 0: - return None - else: - return DrinfeldModule(self._function_ring, q) + quo, rem = (candidate * self.gen()).right_quo_rem(candidate) + return None if rem != 0 else DrinfeldModule(self._function_ring, quo) def _Hom_(self, other, category): + r""" + Return ``DrinfeldModuleHomset(self, other, category)``. + + Validity of the input is checked at the instantiation of + ``DrinfeldModuleHomset``. ``self`` and ``other`` only need be in + the same category. + + INPUT: + + - ``other`` -- the codomain of the homset + - ``category`` -- the category in which we consider the + morphisms, usually `self.category()` + + OUTPUT: + + - an homset + """ from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset return DrinfeldModuleHomset(self, other, category) # Rank two methods def delta(self): + r""" + If the rank is two, return `\Delta` such that the generator is + `phi_X = \gamma(X) + g\tau + \Delta\tau^2`; if the rank is not + two, raise an exception. + + OUTPUT: + + - an element in the base ring if the rank is two; an + exception is raised otherwise + """ self._test_rank_two() return self.gen()[2] def g(self): + r""" + If the rank is two, return `g` such that the generator is `phi_X + = \gamma(X) + g\tau + \Delta\tau^2`; if the rank is not two, + raise an exception. + + OUTPUT: + + - an element in the base ring if the rank is two; an + exception is raised otherwise + """ self._test_rank_two() return self.gen()[1] def j(self): + r""" + If the rank is two, return the j-invariant of the Drinfeld + module; if the rank is not two, raise an exception. + + Write the generator `\phi_X = \gamma(X) + g\tau + \Delta\tau^2`. + The j-invariant is defined by `\frac{g^{q+1}}{\Delta}`, `q` + being the order of the base field of the polynomial ring. In our + case, this base field is always finite, as we force the function + ring to be of the form `\Fq[X]`. + + OUTPUT: + + - an element in the base ring if the rank is two; an + exception is raised otherwise + """ self._test_rank_two() return (self.g()**(self._Fq.order()+1)) / self.delta() From a1b47cecc7879d42fe15ca963c4f50947d499aa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 4 Aug 2022 19:24:56 +0200 Subject: [PATCH 046/197] Add Drinfeld modules to the reference I just added the entry to the section *Function fields*. --- src/doc/en/reference/function_fields/index.rst | 1 + .../drinfeld_modules/drinfeld_module.py | 11 +++++------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/doc/en/reference/function_fields/index.rst b/src/doc/en/reference/function_fields/index.rst index 50c04560a33..bc2350116ad 100644 --- a/src/doc/en/reference/function_fields/index.rst +++ b/src/doc/en/reference/function_fields/index.rst @@ -21,6 +21,7 @@ algebraic closure of `\QQ`. sage/rings/function_field/maps sage/rings/function_field/extensions sage/rings/function_field/constructor + sage/rings/function_field/drinfeld_modules/drinfeld_module A basic reference for the theory of algebraic function fields is [Stich2009]_. diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index eebb64d5b8c..7725343d6c5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -118,9 +118,8 @@ def base_ring(self): r""" Return the base ring of the Drinfeld module. - This is always a field. This is the base field of the codomain - (Ore polynomial ring) of the morphism that defines the Drinfeld - module. + This is the base field of Ore polynomial ring. In particular, the base + ring is always a field. A Drinfeld module is said to be finite if the base ring is finite. @@ -181,7 +180,7 @@ def ore_polring(self): Return the Ore polynomial ring of the Drinfeld module. If the Drinfeld module is defined by a morphism `A \to - K\{\tau\}`, we return the codomain `K\{\tau\}`. + K\{\tau\}`, this is the codomain `K\{\tau\}`. OUTPUT: @@ -206,9 +205,9 @@ def function_ring(self): Return the function ring of the Drinfeld module. If the Drinfeld module is defined by a morphism `A \to - K\{\tau\}`, we return the domain `A`. + K\{\tau\}`, this is the domain `A`. - In our case, this is a polynomial ring. + In our case, the function ring is a polynomial ring. OUTPUT: From cac6471f62d233c462b4b17f3e1cb2fb990fe7e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 5 Aug 2022 18:26:33 +0200 Subject: [PATCH 047/197] Fix constructor of DrinfeldModuleMorphism The constructor wrongfully raised TypeError when the input already was a morphism. See my comment inside the definition of the constructor. --- .../function_field/drinfeld_modules/morphism.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 0adb5fa18e3..7af3ad0230b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -21,17 +21,18 @@ def __init__(self, parent, x): super().__init__(parent) domain = parent.domain() codomain = parent.codomain() - if x.parent() is parent: - ore_polynomial = x.ore_polynomial() - else: - ore_polynomial = domain.ore_polring()(x) - # Test well-definition of the morphism - if domain.gen() * ore_polynomial != ore_polynomial * codomain.gen(): + # NOTE: it used to be x.parent() is parent, but this was False. + # DrinfeldModuleHomset inherits Homset, which does NOT inherit + # UniqueRepresentation + if x.parent() == parent: # x is a DrinfeldModuleMorphism + ore_pol = x.ore_polynomial() + else: # x is an Ore polynomial + ore_pol = domain.ore_polring()(x) + if ore_pol * domain.gen() != codomain.gen() * ore_pol: raise ValueError('the Ore polynomial does not define a morphism') - # Instantiation self._domain = domain self._codomain = codomain - self._ore_polynomial = ore_polynomial + self._ore_polynomial = ore_pol def _repr_(self): return f'Drinfeld Module morphism:\n' \ From d4eddc8346f69058d3b9f958e2ea24e8c9a659b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 5 Aug 2022 19:12:39 +0200 Subject: [PATCH 048/197] _Minor change --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 7725343d6c5..1485e0b4278 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -227,8 +227,7 @@ def __call__(self, a): INPUT: - - ``a`` -- the element in the function ring whose image is to - compute + - ``a`` -- an element in the function ring OUTPUT: From 4d9e5a096f73e7d78195e36e1b64f4a592a6e26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 5 Aug 2022 18:51:18 +0200 Subject: [PATCH 049/197] Create __eq__ method in DrinfeldModuleMorphism As the class does not inherit UniqueRepresentation or something similar, this is necessary. --- .../rings/function_field/drinfeld_modules/morphism.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 7af3ad0230b..4eeb5adf214 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -34,6 +34,15 @@ def __init__(self, parent, x): self._codomain = codomain self._ore_polynomial = ore_pol + # NOTE: Should I inherit UniqueRepresentation to avoid this? + def __eq__(self, other): + try: + if self.parent() == other.parent(): + return self.defining_ore_polynomial() == other.defining_ore_polynomial() + except AttributeError: + return False + return False + def _repr_(self): return f'Drinfeld Module morphism:\n' \ f' From: {self._domain}\n' \ @@ -46,7 +55,7 @@ def codomain(self): def domain(self): return self._domain - def ore_polynomial(self): + def defining_ore_polynomial(self): return self._ore_polynomial def is_zero(self): From fbddd9c5b1bb2ad5a2787693bcddd084169c9edc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 5 Aug 2022 19:13:03 +0200 Subject: [PATCH 050/197] Create is_finite method in DrinfeldModule --- .../drinfeld_modules/drinfeld_module.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 1485e0b4278..97c50353847 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -332,6 +332,18 @@ def invert(self, ore_pol): else: return None + def is_finite(self): + r""" + Return ``True`` if the Drinfeld module is finite, return + ``False`` otherwise. + + OUTPUT: + + - ``True`` or ``False`` + """ + from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule + return isinstance(self, FiniteDrinfeldModule) + def rank(self): r""" Return the rank of the Drinfeld module. From 9ef98d313cc78827c8dfd88a4323e8098ef3e945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 8 Aug 2022 22:42:56 +0100 Subject: [PATCH 051/197] Create is_isomorphism method in DrinfeldModuleMorphism --- src/sage/rings/function_field/drinfeld_modules/morphism.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 4eeb5adf214..5bfe179adfb 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -63,3 +63,6 @@ def is_zero(self): def is_isogeny(self): return not self.is_zero() + + def is_isomorphism(self): + return self.is_isogeny() and self._ore_polynomial.degree() == 0 From f4bb02d7d13ce42a0849c82e0c07241f65cb9b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 5 Aug 2022 19:13:28 +0200 Subject: [PATCH 052/197] Refactor _repr_ method in DrinfeldModuleHomset Simply change the output. --- src/sage/rings/function_field/drinfeld_modules/homset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index d4ccf4880c8..4dddb310049 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -20,6 +20,10 @@ def __init__(self, X, Y, category=None, check=True): base = category.base() Homset.__init__(self, X, Y, category=category, base=base, check=check) + def _repr_(self): + return f'Set of Drinfeld module morphisms from ' \ + f'{self.domain().gen()} to {self.codomain().gen()}' + def __contains__(self, x): try: x = self(x) From a21e5260e793600dbb61aabd5a58ed036c8a8d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 5 Aug 2022 19:32:32 +0200 Subject: [PATCH 053/197] Remove _check_base_fields function from drinfeld_module.py --- .../drinfeld_modules/drinfeld_module.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 97c50353847..337f507fbaa 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -147,9 +147,6 @@ def constant_term(self): """ return self.gen()[0] - # def frobenius(self): - # return self.ore_polring().twisting_morphism() - def gen(self): r""" Return the generator (`\phi_X`) of the Drinfeld module. @@ -256,16 +253,18 @@ def change_ring(self, new_field): - a Drinfeld module """ - # TODO: Remove _check_base_field R = new_field if not R.is_field() and R.is_finite(): raise TypeError('Argument must be a finite field') if not self.ore_polring().base_ring().is_subring(R): raise ValueError('The new field must be a finite field ' \ 'extension of the base field of the Ore polynomial ring.') - _check_base_fields(self._Fq, R) + if not (R.is_field() and R.is_finite() and self._Fq.is_subring(R)): + raise ValueError(f'the new base ring must be an extension of the ' \ + 'old base ring') - new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) + frobenius = self.ore_polring().twisting_morphism() + new_frobenius = R.frobenius_endomorphism(frobenius.power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, names=self.ore_polring().variable_names()) return DrinfeldModule(self.function_ring(), @@ -477,10 +476,3 @@ def j(self): """ self._test_rank_two() return (self.g()**(self._Fq.order()+1)) / self.delta() - - -def _check_base_fields(Fq, L): - if not (L.is_field() and L.is_finite() and Fq.is_subring(L)): - raise ValueError(f'The base field of the Ore polynomial ring must ' \ - 'be a finite field extension of the base field of the ' \ - 'polynomial ring') From a7c48a41addcc77880d4eaa90d8817f1758cc6ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 5 Aug 2022 19:32:55 +0200 Subject: [PATCH 054/197] Fix __eq__ method in DrinfeldModuleMorphism I had previously introduced a bug by changing method `ore_polynomial` to `defining_ore_polynomial`. This is fixed. --- src/sage/rings/function_field/drinfeld_modules/morphism.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 5bfe179adfb..4a7e4f175f7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -38,7 +38,7 @@ def __init__(self, parent, x): def __eq__(self, other): try: if self.parent() == other.parent(): - return self.defining_ore_polynomial() == other.defining_ore_polynomial() + return self.ore_polynomial() == other.ore_polynomial() except AttributeError: return False return False @@ -55,7 +55,7 @@ def codomain(self): def domain(self): return self._domain - def defining_ore_polynomial(self): + def ore_polynomial(self): return self._ore_polynomial def is_zero(self): From a77f850a0da081228f32377b74f7d43cb9742cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Sun, 7 Aug 2022 19:00:26 +0200 Subject: [PATCH 055/197] Refactor exception messages in DrinfeldModule This mostly consisted in simplifying formulations and removing uppercases and stops. --- .../drinfeld_modules/drinfeld_module.py | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 337f507fbaa..52fceff9613 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -44,8 +44,9 @@ def __classcall_private__(cls, function_ring, gen, name='t'): ore_polring = None ore_polring_base = Sequence(gen).universe() else: - raise TypeError('generator must be a list of coefficients '\ - 'or an Ore polynomial') + + raise TypeError('generator must be list of coefficients or an ' \ + 'Ore polynomial') # Build the morphism that defines the category if not ore_polring_base.has_coerce_map_from(function_ring.base_ring()): @@ -254,15 +255,10 @@ def change_ring(self, new_field): - a Drinfeld module """ R = new_field - if not R.is_field() and R.is_finite(): - raise TypeError('Argument must be a finite field') - if not self.ore_polring().base_ring().is_subring(R): - raise ValueError('The new field must be a finite field ' \ - 'extension of the base field of the Ore polynomial ring.') - if not (R.is_field() and R.is_finite() and self._Fq.is_subring(R)): - raise ValueError(f'the new base ring must be an extension of the ' \ - 'old base ring') - + if not (R.is_field() and R.is_finite() and self._Fq.is_subring(R)) \ + and self.ore_polring().base_ring().is_subring(R): + raise ValueError('new base field must be a finite extension of ' \ + 'the base ring') frobenius = self.ore_polring().twisting_morphism() new_frobenius = R.frobenius_endomorphism(frobenius.power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, @@ -312,7 +308,7 @@ def invert(self, ore_pol): TODO """ if not ore_pol in self.ore_polring(): - raise TypeError('ore_pol must be an Ore polynomial ring') + raise TypeError('input must be an Ore polynomial') if ore_pol in self._base_ring: return self._Fq(ore_pol) r = self.rank() @@ -399,7 +395,7 @@ def velu(self, candidate): see :arxiv:`2203.06970`, eq. 1.1. """ if not candidate in self.ore_polring(): - raise TypeError('candidate must be an Ore polynomial') + raise TypeError('input must be an Ore polynomial') if candidate == 0: return None if not self.characteristic().degree().divides(candidate.valuation()): From a60bede03b545fd0d9c3e36c645511c0eb382b40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 5 Aug 2022 19:35:30 +0200 Subject: [PATCH 056/197] Rewrite DrinfeldModule docstring --- .../drinfeld_modules/drinfeld_module.py | 293 +++++++++++++++++- 1 file changed, 289 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 52fceff9613..38ff6f64f6e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1,6 +1,12 @@ r""" Drinfeld modules +This module provides the class +:class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. +For *finite* Drinfeld modules and their theory of complex multiplication, see +class +:class:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule`. + AUTHORS: - Antoine Leudière (2022-04) @@ -31,6 +37,287 @@ from sage.modules.free_module_element import vector class DrinfeldModule(UniqueRepresentation, CategoryObject): + r""" + Let `q` be the order of a finite field `\Fq`. Let `K` be a field + equiped a ring morphism `\gamma: \Fq[X] \to K` --- the field `K` is + said to be an *`\Fq[X]`-field*, and the monic polynomial that + generates `\Ker(\gamma)` is called the *`\Fq[X]`-characteristic of + the `\Fq[X]`-field `K`* (this `\Fq[X]`-characteristic plays the role in + `\Fq[X]` of the standard characteristic, in `\ZZ`, of a finite + field). Let `K\{\tau\}` be the ring of Ore polynomials with + coefficients in `K` and Frobenius variable `\tau: x \mapsto x^q`. A + *Drinfeld `\Fq[X]`-module over the `\Fq[X]`-field `K`* is a ring + morphism `\phi: \Fq[X] \to K\{\tau\}` such that: + + 1. The image of `\phi` has non-constant Ore polynomials. + 2. For every `a \in \Fq[X]`, the constant coefficient of the + Ore polynomial `\phi(a)` is `\gamma(a)`. + + For `a \in \Fq[X]`, `\phi(a)` is denoted `\phi_a`. + + We say that `K` is the *base ring of `\phi`*, `\Fq[X]` is the + *function ring of*, *K\{\tau\}* is the *Ore polynomial ring*, + `t` is the *Ore variable*. The *generator of `\phi`* is `\phi_X`, + its *constant coefficient* is the constant coefficient of `\phi_X`. + + The Drinfeld module `\phi` is uniquely determined by the image + `\phi_X` of `X`. This Ore polynomial is an input of the class + constructor. + + Classical references on Drinfeld modules include [Gos1998]_, + [Rosen2002]_, [VS06]_ and [Gek1998]_. + + .. NOTE:: + + Drinfeld modules are defined in a larger setting, in which + `\Fq[X]` is replaced by a more general ring: the ring of + functions in `k` that are regular outside `\infty`, where `k` is + a function field whose constant field is `\Fq` and with + transcendance degree `1`, and `\infty` is a fixed place of `k`. + This is out of the scope of this implementation. + + .. RUBRIC:: Construction + + A Drinfeld module object (class + :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`) + is constructed as follows:: + + sage: Fq. = GF(3^2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [K.gen(), 1, 1]) + sage: phi + Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + + In this example, we used a list of coefficients (``[K.gen(), 1, + 1]``) to represent the Ore polynomial `\phi_X = z + t + t^2`, `K + = \Fq(z)`. We can also use regular Ore polynomials:: + + sage: ore_polring = phi.ore_polring() + sage: t = phi.ore_variable() # Equals ore_polring.gen() + sage: psi_X = K.gen() + t^3 + sage: psi = DrinfeldModule(FqX, psi_X) + sage: psi + Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 3^12 + + Note that ``phi`` and ``psi`` are *finite* Drinfeld modules, in the + sense that `K` is finite. This is not mandatory:: + + sage: K_infinite = Frac(FqX) + sage: phi_infinite = DrinfeldModule(FqX, [K_infinite.gen(), 1, 1]) + sage: phi_infinite + Drinfeld module defined by X |--> t^2 + t + X over Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + sage: phi_infinite.is_finite() + False + + Drinfeld modules have their own category (see class + :class:`sage.categories.drinfeld_modules.DrinfeldModules`):: + + sage: phi.category() + Category of Drinfeld modules defined by Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z + sage: phi.category() is psi.category() + True + sage: phi.category() is phi_infinite.category() + False + + This category holds crucial information, like the + `\Fq[X]`-characteristic of `K`:: + + sage: char = phi.category().characteristic() + + .. NOTE:: + + As the output of + :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule.category` + suggests, the morphism `\gamma` uniquely determines the category of a Drinfeld + module. + + .. RUBRIC:: More general `K` + + The field `K` does not need be generated, over `\Fq`, by `\gamma(X)` + --- `K` can be an extension of `\Fq(\gamma(X))`. In the following + example, `K` is still a degree six extension of `\Fq`, but `\gamma` + is a projection over `\Fq[X]/p(X)`, with `p(X) = X^3 + (z_2 + 2)X^2 + + (6*z_2 + 1)X + 3z_2 + 5`:: + + sage: p = X^2 + z2 + 2 # Prime polynomial + sage: p_root = z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z # Root of p + sage: phi_inter = DrinfeldModule(FqX, [p_root, 1, 1]) + sage: phi_inter + Drinfeld module defined by X |--> t^2 + t + z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z over Finite Field in z of size 3^12 + + We can check that the morphisms `\gamma` are not the same for + ``phi`` and ``phi_inter``, and that the `\gamma` associated to + `\phi` is surjective, while the other one is not:: + + sage: phi_inter.category().morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z + sage: phi.category().morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z + + .. RUBRIC:: Basic methods + + For a polynomial `a \in \Fq[X]`, compute `\phi_a` by calling `phi`:: + + sage: phi(X) # phi_X + t^2 + t + z + sage: phi(X^3 + X + 1) # phi_X^3 +X + 1 + t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 + sage: phi(1) # phi_1 + 1 + + We can retrieve basic properties:: + + sage: phi.base_ring() # K + Finite Field in z of size 3^12 + sage: phi.ore_polring() # K{t} + Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) + sage: phi.ore_variable() # t + t + sage: phi.function_ring() # Fq[X] + Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + sage: phi.gen() # phi_X + t^2 + t + z + sage: phi.gen() == phi(X) + True + sage: phi.constant_coefficient() # Constant coefficient of phi_X + z + sage: phi.morphism() # The Drinfeld module as a morphism + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) + Defn: X |--> t^2 + t + z + + We can compute the rank and height:: + + sage: phi.rank() + 2 + sage: phi.height() + 1 + + And there are some rank-two specifics:: + + sage: phi.j() # j-invariant + 1 + sage: phi.g() # write phi_X = z + g*t + Delta*t^2 + 1 + sage: phi.delta() # Write phi_X = z + g*t + Delta*t^2 + 1 + + .. RUBRIC:: Morphisms, isogenies + + A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore + polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for + every $a \in \Fq[X]`. In our case, this is equivalent to verifying + `f \phi_X = \psi_X f`. An *isogeny* is a non-zero morphism. + + Use the ``in`` syntax to test if an Ore polynomial defines an + isogeny:: + + sage: phi(X) in Hom(phi, phi) + True + sage: t^6 in Hom(phi, phi) + True + sage: t^5 + 2*t^3 + 1 in Hom(phi, phi) + False + sage: 1 in Hom(phi, psi) + False + sage: 1 in Hom(phi, phi) + True + sage: 0 in Hom(phi, psi) + True + + To create a SageMath object representing the morphism, call the + homset ``hom``:: + + sage: hom = Hom(phi, phi) + sage: frob = hom(t^6) + sage: identity_morphism = hom(1) + sage: zero_morphism = hom(0) + sage: frob + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + Defn: t^6 + sage: identity_morphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + Defn: 1 + sage: zero_morphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + Defn: 0 + + We can retrieve the underlying Ore polynomial with the method + :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule.ore_polynomial`:: + + sage: frob.ore_polynomial() + t^6 + + And we can easily check if a morphism defines an isogeny or an + isomorphism (i.e. an isogeny whose underlying Ore polynomial has + degree `0`):: + + sage: frob.is_isogeny() + True + sage: identity_morphism.is_isogeny() + True + sage: zero_morphism.is_isogeny() + False + sage: frob.is_isomorphism() + False + sage: identity_morphism.is_isomorphism() + True + sage: zero_morphism.is_isomorphism() + False + + .. RUBRIC:: The Vélu formula + + Let ``ore_pol`` be a non-zero Ore polynomial ``ore_pol``. For + Drinfeld module, it is easy to decide --- and find as the case may + be --- if there exists a Drinfeld module ``new_drin_mod``, such that + ``ore_pol`` is an isogeny from ``self`` to ``new_drin_mod``. If this + Drinfeld module exists, it is unique. + + sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z + sage: new_drin_mod = phi.velu(ore_pol) + sage: new_drin_mod + Drinfeld module defined by X |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over Finite Field in z of size 3^12 + sage: ore_pol in Hom(phi, new_drin_mod) + True + sage: ore_pol * phi(X) == new_drin_mod(X) * ore_pol + True + + .. RUBRIC:: Other methods + + It is possible to change the base ring:: + + sage: L = K.extension(2) + sage: phi_rebased = phi.change_ring(L) + sage: Ltau = phi_rebased.ore_polring() + sage: Ltau(phi(X)) == phi_rebased(X) + True + + Given an Ore polynomial that equals `\phi(a)` for some `a \in + \Fq[X]`, we can retrieve `a` (as a morphism, a Drinfeld + module is injective, see [Gos1998]_, cor. 4.5.2.):: + + sage: a = FqX.random_element() + sage: phi.invert(phi(a)) == a + True + """ + @staticmethod def __classcall_private__(cls, function_ring, gen, name='t'): # Check all possible input types @@ -131,9 +418,9 @@ def base_ring(self): """ return self._base_ring - def constant_term(self): + def constant_coefficient(self): r""" - Return the constant term of the generator (`\phi_X`). + Return the constant coefficient of the generator (`\phi_X`). The `A`-characteristic of the base field (see :meth:`sage.categories.drinfeld_modules.DrinfeldModules.characteristic`) @@ -375,12 +662,10 @@ def velu(self, candidate): We write the Ore Euclidean division `\phi_X = \mathrm{candidate}*q + r`, and return The candidate is an isogeny if only if: - 1. The degree of the characteristic devides the height of the candidate. (The height of an Ore polynomial `P(t)` is the maximum `n` such that `t^n` right-divides `P(t)`.) - 2. The candidate right-divides the generator, which can be tested with Euclidean division. From a29334556c55b6ed88f433f61004d31f2fc09188 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Sun, 7 Aug 2022 19:40:30 +0200 Subject: [PATCH 057/197] Add classic Drinfeld module references to bib. master file --- src/doc/en/reference/references/index.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 35725366e97..f83df6e7228 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -2497,6 +2497,9 @@ REFERENCES: TR-737-05, (2005). ftp://ftp.cs.princeton.edu/reports/2005/737.pdf +.. [Gek1991] \E.-U. Gekeler. On finite Drinfeld modules. Journal of + algebra, 1(141):187–203, 1991. + .. [GG2012] Jim Geelen and Bert Gerards, Characterizing graphic matroids by a system of linear equations, submitted, 2012. Preprint: @@ -2656,6 +2659,9 @@ REFERENCES: .. [Gos1972] Bill Gosper, "Continued Fraction Arithmetic" https://perl.plover.com/classes/cftalk/INFO/gosper.txt +.. [Gos1998] \D. Goss. Basic structures of function field arithmetic. Springer, + 1998. + .. [Gor1980] Daniel Gorenstein, Finite Groups (New York: Chelsea Publishing, 1980) @@ -4452,6 +4458,11 @@ REFERENCES: Int. Math. Res. Not. (2015). :doi:`10.1093/imrn/rnv194`, :arxiv:`1408.0320`. +.. [MS2019] \Y. Musleh and É. Schost. Computing the characteristic polynomial + of a finite rank two Drinfeld module. In Proceedings of the 2019 + ACM on International Symposium on Symbolic and Algebraic + Computation, pages 307–314. ACM, 2019. + .. [MSSY2001] Mateescu, A., Salomaa, A., Salomaa, K. and Yu, S., *A sharpening of the Parikh mapping*. Theoret. Informatics Appl. 35 (2001) 551-564. @@ -5028,6 +5039,8 @@ REFERENCES: .. [Ros2002] Rosenfeld, Vladimir Raphael, 2002: Enumerating De Bruijn Sequences. *Communications in Math. and in Computer Chem.* +.. [Rosen2002] \M. Rosen. Number theory in function fields. Springer, 2022. + .. [Rot2001] Gunter Rote, *Division-Free Algorithms for the Determinant and the Pfaffian: Algebraic and Combinatorial Approaches*, H. Alt (Ed.): Computational Discrete @@ -5853,6 +5866,9 @@ REFERENCES: .. [Voi2012] \J. Voight. Identifying the matrix ring: algorithms for quaternion algebras and quadratic forms, to appear. +.. [VS06] \G.D. Villa Salvador. Topics in the Theory of Algebraic Function + Fields. Birkhäuser, 2006. + .. [VW1994] Leonard Van Wyk. *Graph groups are biautomatic*. J. Pure Appl. Alg. **94** (1994). no. 3, 341-352. From 50b0501c4ea31ff0287c16951223fa4f64f2d10f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 10 Aug 2022 10:30:19 +0100 Subject: [PATCH 058/197] Check input morphism in DrinfeldModules constructor Check that the input `morphism` in DrinfeldModules constructor is indeed a morphism. --- src/sage/categories/drinfeld_modules.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index a64c59ea693..c46e328ec75 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -11,6 +11,7 @@ from sage.categories.category import CategoryWithParameters from sage.misc.functional import log +from sage.rings.morphism import RingHomomorphism from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing @@ -23,6 +24,9 @@ class DrinfeldModules(CategoryWithParameters): def __init__(self, morphism, name='t'): gamma = morphism + # Check input is a ring Morphism + if not isinstance(gamma, RingHomomorphism): + raise TypeError('input must be a Ring morphism') self._morphism = morphism self._domain = gamma.domain() # Check domain is Fq[X] From 73aa69be1623ddc8ad59689cde0abcbdfa388d66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 10 Aug 2022 10:58:39 +0100 Subject: [PATCH 059/197] Check input in DrinfeldModule constructor Check that arg `function_ring` is an Fq[X]. This was not done, even though methods of `PolynomialRing` were used. Code is not great, see my comment in the code. --- .../drinfeld_modules/drinfeld_module.py | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 38ff6f64f6e..1bc6fb79551 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -30,6 +30,7 @@ from sage.categories.drinfeld_modules import DrinfeldModules from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing +from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.misc.latex import latex from sage.structure.sequence import Sequence @@ -320,7 +321,23 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): @staticmethod def __classcall_private__(cls, function_ring, gen, name='t'): - # Check all possible input types + + # FIXME: function_ring must be checked before calling base_ring + # on it. But then it is checked twice: firstly here, secondly in + # the category. Another problem is that those lines are + # duplicate. As a general comment, there are sanity checks both + # here and in the category constructor, which is not ideal. + # Check domain is Fq[X] + if not isinstance(function_ring, PolynomialRing_general): + raise NotImplementedError('domain must be a polynomial ring') + function_ring_base = function_ring.base_ring() + if not function_ring_base.is_field() or not function_ring_base.is_finite() : + raise TypeError('the base ring of the domain must be a finite field') + Fq = function_ring_base + FqX = function_ring + X = FqX.gen() + + # Check all possible input types for gen # `gen` is an Ore polynomial: if isinstance(gen, OrePolynomial): ore_polring = gen.parent() @@ -331,7 +348,6 @@ def __classcall_private__(cls, function_ring, gen, name='t'): ore_polring = None ore_polring_base = Sequence(gen).universe() else: - raise TypeError('generator must be list of coefficients or an ' \ 'Ore polynomial') @@ -341,15 +357,14 @@ def __classcall_private__(cls, function_ring, gen, name='t'): 'ring of Ore polynomial ring') gamma = function_ring.hom([ore_polring_base(gen[0])]) - # Mathematical integrity of the data is delegated to the category + # Other checks in the category definition category = DrinfeldModules(gamma, name=name) + # Check gen as Ore polynomial if ore_polring is not None and ore_polring is not category.codomain(): raise ValueError(f'generator must lie in {category.codomain()}') - # Sanity cast - ore_polring = category.codomain() - # Be sure to have a generator that is an Ore polynomial - gen = ore_polring(gen) + ore_polring = category.codomain() # Sanity cast + gen = ore_polring(gen) # Sanity cast if gen.degree() <= 0: raise ValueError('generator must have positive degree') From f8b7030df0953fb52ee0b06c37be37edca5f16ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 10 Aug 2022 11:01:50 +0100 Subject: [PATCH 060/197] Add a note in DrinfeldModule docstring Add a note in DrinfeldModule docstring on the situation in which the user gives, as an input of the constructor, a list of integer coefficients for the generator. --- .../function_field/drinfeld_modules/drinfeld_module.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 1bc6fb79551..8123f4781a6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -101,6 +101,12 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: psi Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 3^12 + .. NOTE:: + + If the Ore polynomial has coefficients in the integers, the + constructor does not try to guess if the user wants to see the + coefficients as elements of Fq, or an extension like `K`. + Note that ``phi`` and ``psi`` are *finite* Drinfeld modules, in the sense that `K` is finite. This is not mandatory:: From db42b9935d8e514aa1a1443d0c6729b8750d710a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 10 Aug 2022 11:27:46 +0100 Subject: [PATCH 061/197] Change _domain and _codomain names in DrinfeldModules Those names were misleading, as there was a conflict with names in DrinfeldModule class. I replaced `domain` by `function_ring`, `codomain` by `ore_polring`, and did the necessary replacements in this module and other ones. I did *not* tested every module in `sage.rings.function_field.drinfeld_modules`; surely there are bugs. --- src/sage/categories/drinfeld_modules.py | 51 ++++++++++--------- .../drinfeld_modules/drinfeld_module.py | 12 ++--- 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index c46e328ec75..a81bb68e42e 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -28,9 +28,9 @@ def __init__(self, morphism, name='t'): if not isinstance(gamma, RingHomomorphism): raise TypeError('input must be a Ring morphism') self._morphism = morphism - self._domain = gamma.domain() + self._function_ring = gamma.domain() # Check domain is Fq[X] - function_ring = self._domain + function_ring = self._function_ring if not isinstance(function_ring, PolynomialRing_general): raise NotImplementedError('domain must be a polynomial ring') function_ring_base = function_ring.base_ring() @@ -39,14 +39,14 @@ def __init__(self, morphism, name='t'): Fq = function_ring_base FqX = function_ring X = FqX.gen() - # Check domain is field + # Check codomain of gamma is field K = gamma.codomain() if not K.is_field(): - raise TypeError('the codomain must be a field') + raise TypeError('the codomain of the morphism must be a field') # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) - self._codomain = OrePolynomialRing(K, tau, names=name) + self._ore_polring = OrePolynomialRing(K, tau, names=name) # Create characteristic self._characteristic = None if K.is_finite(): @@ -55,30 +55,43 @@ def __init__(self, morphism, name='t'): self._characteristic = FqX(E(gamma(X)).minpoly()) def base(self): - return self.codomain().base_ring() + return self._ore_polring.base_ring() def characteristic(self): if self._characteristic is None: raise NotImplementedError return self._characteristic + def function_ring(self): + return self._function_ring + + def morphism(self): + return self._morphism + + def ore_polring(self): + return self._ore_polring + + # Sage methods + def _call_(self, gen): # Avoid circular import - from sage.rings.function_field.drinfeld_module import DrinfeldModule + from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule # If gen is not in the codomain, an exception is raised - gen = self._codomain(gen) + gen = self._ore_polring(gen) if self.characteristic()(gen[0]) != 0: - raise ValueError('incorrect characteristic') - return DrinfeldModule(self._domain, gen) - - def super_categories(self): - return [] + raise ValueError('constant coefficient is not a root of the characteristic') + return DrinfeldModule(self._function_ring, gen) def _repr_(self): return f'Category of Drinfeld modules defined by {self._morphism}' + # Sage requires those: + def _make_named_class_key(self, name): - return self._domain.category() + return self._function_ring.category() + + def super_categories(self): + return [] def Homsets(self): return Homsets() @@ -86,16 +99,8 @@ def Homsets(self): def Endsets(self): return Homsets() - def domain(self): - return self._domain - - def codomain(self): - return self._codomain - - def morphism(self): - return self._morphism - class ParentMethods: + def characteristic(self): return self.category().characteristic() diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 8123f4781a6..de14a38df7d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -367,9 +367,9 @@ def __classcall_private__(cls, function_ring, gen, name='t'): category = DrinfeldModules(gamma, name=name) # Check gen as Ore polynomial - if ore_polring is not None and ore_polring is not category.codomain(): - raise ValueError(f'generator must lie in {category.codomain()}') - ore_polring = category.codomain() # Sanity cast + if ore_polring not in (None, category.ore_polring()): + raise ValueError(f'generator must lie in {category.ore_polring()}') + ore_polring = category.ore_polring() # Sanity cast gen = ore_polring(gen) # Sanity cast if gen.degree() <= 0: raise ValueError('generator must have positive degree') @@ -384,11 +384,11 @@ def __classcall_private__(cls, function_ring, gen, name='t'): def __init__(self, gen, category): CategoryObject.__init__(self, category=category) self._base_ring = category.base() - self._function_ring = category.domain() + self._function_ring = category.function_ring() self._gen = gen - self._morphism = self._function_ring.hom([gen]) + self._morphism = category._function_ring.hom([gen]) self._ore_polring = gen.parent() - self._Fq = self._function_ring.base_ring() + self._Fq = self._function_ring.base_ring() # Must be last ################# # Private utils # From 96776b71af5144fc6ee5ae869ce69c5e4cf22263 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 10 Aug 2022 11:30:58 +0100 Subject: [PATCH 062/197] Fix change_ring method in DrinfeldModule class The import and the call of the DrinfeldModule constructor were wrong. --- .../function_field/drinfeld_modules/drinfeld_module.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index de14a38df7d..22e08e23e89 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -567,12 +567,11 @@ def change_ring(self, new_field): and self.ore_polring().base_ring().is_subring(R): raise ValueError('new base field must be a finite extension of ' \ 'the base ring') - frobenius = self.ore_polring().twisting_morphism() + frobenius = self._ore_polring.twisting_morphism() new_frobenius = R.frobenius_endomorphism(frobenius.power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, - names=self.ore_polring().variable_names()) - return DrinfeldModule(self.function_ring(), - new_ore_polring(self.gen()), self.characteristic()) + names=self._ore_polring.variable_names()) + return DrinfeldModule(self._function_ring, new_ore_polring(self._gen)) def height(self): r""" From 5a640835045f561d74dcedc449cd94cf9bf5daea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 12 Aug 2022 22:18:43 +0200 Subject: [PATCH 063/197] Add polcast flag to build Drinfeld modules This flag makes sure OrePolynomialRing does not build a regular polynomial ring when the twisting morphism is the identity. See ticket 34001 : https://trac.sagemath.org/ticket/34001. --- src/sage/categories/drinfeld_modules.py | 3 ++- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index a81bb68e42e..c7d3ced6d59 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -46,7 +46,8 @@ def __init__(self, morphism, name='t'): # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) - self._ore_polring = OrePolynomialRing(K, tau, names=name) + self._ore_polring = OrePolynomialRing(K, tau, names=name, + polcast=False) # Create characteristic self._characteristic = None if K.is_finite(): diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 22e08e23e89..37774759af7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -570,7 +570,7 @@ def change_ring(self, new_field): frobenius = self._ore_polring.twisting_morphism() new_frobenius = R.frobenius_endomorphism(frobenius.power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, - names=self._ore_polring.variable_names()) + names=self._ore_polring.variable_names(), polcast=False) return DrinfeldModule(self._function_ring, new_ore_polring(self._gen)) def height(self): From 750d797641ec0cc00436737eabc1ba9a29f13977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Sat, 13 Aug 2022 11:35:44 +0200 Subject: [PATCH 064/197] (minor) Fix comment in DrinfeldModule constructor --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 37774759af7..b23656ab29c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -370,7 +370,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): if ore_polring not in (None, category.ore_polring()): raise ValueError(f'generator must lie in {category.ore_polring()}') ore_polring = category.ore_polring() # Sanity cast - gen = ore_polring(gen) # Sanity cast + gen = ore_polring(gen) if gen.degree() <= 0: raise ValueError('generator must have positive degree') From ab02ba1ff7e97555284c458897895bb5e06bea91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Sat, 13 Aug 2022 11:36:10 +0200 Subject: [PATCH 065/197] Add name flag to change_ring method in DrinfeldModule This is to allow choosing the Ore variable name of the new Drinfeld module. --- .../drinfeld_modules/drinfeld_module.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index b23656ab29c..c4486cd4f40 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -542,7 +542,7 @@ def __call__(self, a): return self._morphism(a) - def change_ring(self, new_field): + def change_ring(self, new_field, name=None): r""" If ``new_field`` is a field extension of the base ring, return a new Drinfeld module that extends ``self`` to the base ring @@ -562,16 +562,11 @@ def change_ring(self, new_field): - a Drinfeld module """ - R = new_field - if not (R.is_field() and R.is_finite() and self._Fq.is_subring(R)) \ - and self.ore_polring().base_ring().is_subring(R): - raise ValueError('new base field must be a finite extension of ' \ - 'the base ring') - frobenius = self._ore_polring.twisting_morphism() - new_frobenius = R.frobenius_endomorphism(frobenius.power()) - new_ore_polring = OrePolynomialRing(R, new_frobenius, - names=self._ore_polring.variable_names(), polcast=False) - return DrinfeldModule(self._function_ring, new_ore_polring(self._gen)) + coeffs = self._gen.coefficients() + new_coeffs = list(map(new_field, coeffs)) + if name == None: + name = self._ore_polring.variable_name() + return DrinfeldModule(self._function_ring, new_coeffs, name=name) def height(self): r""" From 4c9b81dfc492384b07d5fdf1eab4eeac08c2c7e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 12 Aug 2022 23:23:51 +0200 Subject: [PATCH 066/197] Add examples in DrinfeldModule doc and rewrite docstrings EXAMPLES: fields were added to each method. Some now have TESTS:: fields, but not all of them. Unfortunately this commit is not very clean. It also includes: - some cosmectif refactoring of the velu method; - deletion of methods g and delta (thanks David, see https://trac.sagemath.org/ticket/33713#comment:80); - changing name of method _test_rank_two to _check_rank_two. --- .../drinfeld_modules/drinfeld_module.py | 571 ++++++++++++++---- .../finite_drinfeld_module.py | 8 +- 2 files changed, 473 insertions(+), 106 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index c4486cd4f40..266119365fa 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -77,6 +77,13 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): transcendance degree `1`, and `\infty` is a fixed place of `k`. This is out of the scope of this implementation. + INPUT: + + - ``function_ring`` -- the polynomial ring `\Fq[X]` + - ``gen`` -- the generator `\phi_X`, as a list of coefficients or an + Ore polynomial + - ``name`` (optional) the name of the Ore variable + .. RUBRIC:: Construction A Drinfeld module object (class @@ -116,6 +123,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): Drinfeld module defined by X |--> t^2 + t + X over Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 sage: phi_infinite.is_finite() False + sage: phi.is_finite() + True Drinfeld modules have their own category (see class :class:`sage.categories.drinfeld_modules.DrinfeldModules`):: @@ -144,11 +153,12 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: More general `K` - The field `K` does not need be generated, over `\Fq`, by `\gamma(X)` - --- `K` can be an extension of `\Fq(\gamma(X))`. In the following - example, `K` is still a degree six extension of `\Fq`, but `\gamma` - is a projection over `\Fq[X]/p(X)`, with `p(X) = X^3 + (z_2 + 2)X^2 + - (6*z_2 + 1)X + 3z_2 + 5`:: + The field `K` does not need be generated by the constant coefficient + (i.e. generated, as an extension of `\Fq`, by the image + `\gamma(X)`). In the following example, `K` is still a + degree six extension of `\Fq`, but `\gamma` is a projection over + `\Fq[X]/p(X)`, with `p(X) = X^3 + (z_2 + 2)X^2 + (6*z_2 + 1)X + 3z_2 + + 5`:: sage: p = X^2 + z2 + 2 # Prime polynomial sage: p_root = z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z # Root of p @@ -211,14 +221,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: phi.height() 1 - And there are some rank-two specifics:: + As well as the j-invariant if the rank is two:: sage: phi.j() # j-invariant 1 - sage: phi.g() # write phi_X = z + g*t + Delta*t^2 - 1 - sage: phi.delta() # Write phi_X = z + g*t + Delta*t^2 - 1 .. RUBRIC:: Morphisms, isogenies @@ -293,17 +299,17 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): Let ``ore_pol`` be a non-zero Ore polynomial ``ore_pol``. For Drinfeld module, it is easy to decide --- and find as the case may - be --- if there exists a Drinfeld module ``new_drin_mod``, such that - ``ore_pol`` is an isogeny from ``self`` to ``new_drin_mod``. If this + be --- if there exists a Drinfeld module ``psi``, such that + ``ore_pol`` is an isogeny from ``self`` to ``psi``. If this Drinfeld module exists, it is unique. sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z - sage: new_drin_mod = phi.velu(ore_pol) - sage: new_drin_mod + sage: psi = phi.velu(ore_pol) + sage: psi Drinfeld module defined by X |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over Finite Field in z of size 3^12 - sage: ore_pol in Hom(phi, new_drin_mod) + sage: ore_pol in Hom(phi, psi) True - sage: ore_pol * phi(X) == new_drin_mod(X) * ore_pol + sage: ore_pol * phi(X) == psi(X) * ore_pol True .. RUBRIC:: Other methods @@ -390,17 +396,6 @@ def __init__(self, gen, category): self._ore_polring = gen.parent() self._Fq = self._function_ring.base_ring() # Must be last - ################# - # Private utils # - ################# - - def _test_rank_two(self): - r""" - Raise ``NotImplementedError`` if the rank is not two. - """ - if self.rank() != 2: - raise NotImplementedError('the rank must be 2') - ########################## # Special Sage functions # ########################## @@ -436,6 +431,51 @@ def base_ring(self): OUTPUT: - a field + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.base_ring() + Finite Field in z12 of size 5^12 + + This is always true:: + + sage: phi.base_ring() is phi.ore_polring().base_ring() + True + sage: phi.base_ring() is K + True + + Note that in the above example, the base ring does is not + generated by the constant coefficient (i.e. generated, as an + extension of `\Fq`, by the image `\gamma(X)`). The base + ring may also be the same as ``Fq``:: + + sage: psi = DrinfeldModule(FqX, [Fq(1), Fq.gen()]) + sage: psi.base_ring() + Finite Field in z2 of size 5^2 + sage: psi.base_ring() is Fq + True + + In which case the Ore polynomial ring is isomorphic to a regular + polynomial ring:: + + sage: psi.ore_polring() + Ore Polynomial Ring in t over Finite Field in z2 of size 5^2 twisted by Identity + sage: psi.ore_polring().twisting_morphism() + Identity endomorphism of Finite Field in z2 of size 5^2 + + TESTS:: + + sage: psi.ore_polring().twisting_morphism().is_identity() + True + + sage: psi.base_ring() is psi.function_ring().base_ring() + True + """ return self._base_ring @@ -453,6 +493,47 @@ def constant_coefficient(self): OUTPUT: - an element in the base ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.constant_coefficient() == p_root + True + + The constant coefficient is the image of ``X`` by the + morphism that defines the category of ``phi``:: + + sage: cat = phi.category() + sage: gamma = cat.morphism() + sage: gamma(X) == phi.constant_coefficient() + True + + Two Drinfeld modules in the same category have the same constant + coefficient:: + + sage: t = phi.ore_variable() + sage: psi = cat(phi.constant_coefficient() + t^3) + sage: psi + Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + + Reciprocally, it is impossible to create two Drinfeld modules in + this category if they don't share the same constant + coefficient:: + + sage: rho = cat(phi.constant_coefficient() + 1 + t^3) + Traceback (most recent call last): + ... + ValueError: constant coefficient is not a root of the characteristic + + One cal also retrieve the constant coefficient using + ``phi(X)[0]`:: + + sage: phi.constant_coefficient() == phi(X)[0] + True """ return self.gen()[0] @@ -467,6 +548,16 @@ def gen(self): OUTPUT: - an Ore polynomial + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.gen() == phi(X) + True """ return self._gen @@ -478,7 +569,45 @@ def morphism(self): - a ring morphism, from the function ring to the Ore polynomial ring - """ + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + Defn: X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: from sage.rings.morphism import RingHomomorphism + sage: isinstance(phi.morphism(), RingHomomorphism) + True + + Actually, the ``DrinfeldModule`` method ``__call__`` simply + class the ``__call__`` method of this morphism:: + + sage: phi.morphism()(X) == phi(X) + True + sage: a = FqX.random_element() + sage: phi.morphism()(a) == phi(a) + True + + And many methods of the Drinfeld module have a counterpart in + the morphism object:: + + sage: m = phi.morphism() + sage: m.domain() is phi.function_ring() + True + sage: m.codomain() is phi.ore_polring() + True + sage: m.im_gens() + [z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12] + sage: phi(X) == m.im_gens()[0] + True + """ return self._morphism def ore_polring(self): @@ -491,6 +620,37 @@ def ore_polring(self): OUTPUT: - an Ore polynomial ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: ore_polring = phi.ore_polring() + sage: ore_polring + Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + + The Ore polynomial ring can also be retrieved from the category + of the Drinfeld module:: + + sage: ore_polring is phi.category().ore_polring() + True + + The generator of the Drinfeld module is in the Ore polynomial + ring:: + + sage: phi(X) in ore_polring + True + + The Ore variable is just the generator of the Ore polynomial + ring:: + + sage: ore_polring.gen() + t + sage: phi.ore_variable() is ore_polring.gen() + True """ return self._ore_polring @@ -503,6 +663,30 @@ def ore_variable(self): OUTPUT: - an Ore polynomial + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.ore_variable() + t + sage: phi.ore_variable() is phi.ore_polring().gen() + True + + We can use the Ore variable to instanciate new Drinfeld + modules...:: + + sage: t = phi.ore_variable() + sage: psi_X = phi.constant_coefficient() + 3*t + 2*t^4 + sage: psi = DrinfeldModule(FqX, psi_X) + + ...or morphisms and isogenies:: + + sage: t^6 in End(phi) # Frobenius endomorphism + True """ return self._ore_polring.gen() @@ -518,6 +702,16 @@ def function_ring(self): OUTPUT: - a polynomial ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.function_ring() is FqX + True """ return self._function_ring @@ -561,6 +755,52 @@ def change_ring(self, new_field, name=None): OUTPUT: - a Drinfeld module + + EXAMPLES: + + The new ring can be an extension of the base ring:: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, p_root^3, 2]) + sage: K2 = K.extension(2) + sage: phi_2 = phi.change_ring(K2) + sage: phi_2 + Drinfeld module defined by X |--> 2*t^2 + (3*z24^23 + 2*z24^22 + 2*z24^20 + z24^19 + 4*z24^18 + 3*z24^17 + 4*z24^15 + 2*z24^13 + 4*z24^12 + 4*z24^11 + 3*z24^10 + 3*z24^9 + 4*z24^8 + 4*z24^6 + 3*z24^5 + 4*z24^4 + 4*z24^3 + 2*z24)*t + 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 over Finite Field in z24 of size 5^24 + + And we can check various things:: + + sage: phi.change_ring(K2).change_ring(K) is phi + True + sage: phi_2.base_ring() is K2 + True + + Naturally, the category has changed:: + + sage: phi_2.category() + Category of Drinfeld modules defined by Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z24 of size 5^24 + Defn: X |--> 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 + + We can also change the base ring to a subfield, even though some things + do not work as expected:: + + sage: K0 = Fq.extension(2) + sage: phi_0 = phi.change_ring(K0) + sage: phi_0.base_ring() is K0 + True + sage: phi.change_ring(K0).change_ring(K) # known bug + Traceback (most recent call last) + ... + TypeError: no coercion defined + + Furthermore:: + + sage: phi.change_ring(K) is phi + True """ coeffs = self._gen.coefficients() new_coeffs = list(map(new_field, coeffs)) @@ -572,11 +812,21 @@ def height(self): r""" Return the height of the Drinfeld module. - When the function ring is a polynomial ring, the height is 1. + In our case, the height is always 1. OUTPUT: - an integer + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.height() == 1 + True """ return Integer(1) @@ -597,17 +847,54 @@ def invert(self, ore_pol): OUTPUT: - a polynomial - + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: a = FqX.random_element() + sage: phi.invert(phi(a)) == a + True + sage: phi.invert(phi(X)) == X + True + sage: phi.invert(phi(Fq.gen())) == Fq.gen() + True + + When the input is not in the image of the Drinfeld module, the + method returns None:: + + sage: t = phi.ore_variable() + sage: phi.invert(t + 1) is None + True + sage: phi.invert(t^3 + t^2 + 1) is None + True + ALGORITHM: - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO + + See [MS2019]_, 3.2.5. + + TESTS:: + + sage: a = FqX.random_element() + sage: cat = phi.category() + sage: phi_r1 = cat.random_element(1) + sage: phi_r1.invert(phi_r1(a)) == a + True + sage: phi_r2 = cat.random_element(2) + sage: phi_r2.invert(phi_r2(a)) == a + True + sage: phi_r3 = cat.random_element(3) + sage: phi_r3.invert(phi_r3(a)) == a + True + sage: phi_r4 = cat.random_element(4) + sage: phi_r4.invert(phi_r4(a)) == a + True + sage: phi_r5 = cat.random_element(5) + sage: phi_r5.invert(phi_r5(a)) == a + True """ if not ore_pol in self.ore_polring(): raise TypeError('input must be an Ore polynomial') @@ -637,10 +924,73 @@ def is_finite(self): OUTPUT: - ``True`` or ``False`` + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.is_finite() + True + sage: L = Frac(FqX) + sage: psi = DrinfeldModule(FqX, [L(2), L(1)]) + sage: psi.is_finite() + False """ from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return isinstance(self, FiniteDrinfeldModule) + def j(self): + r""" + Return the j-invariant of the Drinfeld module. + + Only the rank two case has been implemented. An + NotImplementedError is raised if the rank is not two. + + Assume the rank is two. Write the generator `\phi_X = \gamma(X) + + g\tau + \Delta\tau^2`. The j-invariant is defined by + `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base field + of the polynomial ring. In our case, this base field is always + finite, as we force the function ring to be of the form + `\Fq[X]`. + + OUTPUT: + + - an element in the base ring if the rank is two; an + exception is raised otherwise + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.j() + z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2 + sage: psi = DrinfeldModule(FqX, [p_root, 1, 1]) + sage: psi.j() + 1 + sage: rho = DrinfeldModule(FqX, [p_root, 0, 1]) + sage: rho.j() + 0 + + The rank must be two:: + + sage: theta = DrinfeldModule(FqX, [p_root, 1, 0]) + sage: theta.j() + Traceback (most recent call last): + ... + NotImplementedError: the rank must be 2 + """ + self._check_rank_two() + g = self._gen[1] + delta = self._gen[2] + q = self._Fq.order() + return (g**(q+1)) / delta + def rank(self): r""" Return the rank of the Drinfeld module. @@ -651,22 +1001,37 @@ def rank(self): OUTPUT: - an integer + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.rank() + 2 + sage: psi = DrinfeldModule(FqX, [p_root, 2]) + sage: psi.rank() + 1 + sage: rho = DrinfeldModule(FqX, [p_root, 0, 0, 0, 1]) + sage: rho.rank() + 4 """ return self.gen().degree() - def velu(self, candidate): + def velu(self, isog): r""" - Return a new Drinfeld module such that ``candidate`` is an + Return a new Drinfeld module such that ``isog`` is an isogeny to this module with domain ``self``. If no such isogeny exists, return ``None``. - If the candidate is zero, return ``None``, as an isogeny is + If the input is zero, return ``None``, as an isogeny is required to be non zero. INPUT: - - ``candidate`` -- an Ore polynomial that defines the isogeny - with domain ``self`` and codomain the output of the method + - ``isog`` -- the Ore polynomial that defines the isogeny OUTPUT: @@ -674,17 +1039,15 @@ def velu(self, candidate): ALGORITHM: - We write the Ore Euclidean division `\phi_X = - \mathrm{candidate}*q + r`, and return - The candidate is an isogeny if only if: + The input defines an isogeny if only if: 1. The degree of the characteristic devides the height - of the candidate. (The height of an Ore polynomial + of the input. (The height of an Ore polynomial `P(t)` is the maximum `n` such that `t^n` right-divides `P(t)`.) - 2. The candidate right-divides the generator, which can + 2. The input right-divides the generator, which can be tested with Euclidean division. - We test if the candidate is an isogeny, and, if it is, we + We test if the input is an isogeny, and, if it is, we return the quotient of the Euclidean division. Height and Euclidean division of Ore polynomials are @@ -693,14 +1056,43 @@ def velu(self, candidate): Another possible algorithm is to recursively solve a system, see :arxiv:`2203.06970`, eq. 1.1. + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: t = phi.ore_variable() + sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + sage: psi = phi.velu(isog) + sage: psi + Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + sage: isog in Hom(phi, psi) + True + + This method works for endomorphisms as well:: + + sage: phi.velu(phi(X)) is phi + True + sage: phi.velu(t^6) is phi + True + + The following inputs do not define isogenies, and the method + returns None:: + + sage: phi.velu(0) + sage: phi.velu(t) + sage: phi.velu(t^3 + t + 2) """ - if not candidate in self.ore_polring(): + if not isog in self.ore_polring(): raise TypeError('input must be an Ore polynomial') - if candidate == 0: + if isog == 0: return None - if not self.characteristic().degree().divides(candidate.valuation()): + if not self.characteristic().degree().divides(isog.valuation()): return None - quo, rem = (candidate * self.gen()).right_quo_rem(candidate) + quo, rem = (isog * self.gen()).right_quo_rem(isog) return None if rem != 0 else DrinfeldModule(self._function_ring, quo) def _Hom_(self, other, category): @@ -720,55 +1112,30 @@ def _Hom_(self, other, category): OUTPUT: - an homset + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: t = phi.ore_variable() + sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + sage: psi = phi.velu(isog) + sage: hom = phi._Hom_(psi, category=phi.category()) + sage: hom is Hom(phi, psi) # known bug + True + sage: from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset + sage: isinstance(hom, DrinfeldModuleHomset) + True """ from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset return DrinfeldModuleHomset(self, other, category) - # Rank two methods - - def delta(self): - r""" - If the rank is two, return `\Delta` such that the generator is - `phi_X = \gamma(X) + g\tau + \Delta\tau^2`; if the rank is not - two, raise an exception. - - OUTPUT: - - - an element in the base ring if the rank is two; an - exception is raised otherwise - """ - self._test_rank_two() - return self.gen()[2] - - def g(self): - r""" - If the rank is two, return `g` such that the generator is `phi_X - = \gamma(X) + g\tau + \Delta\tau^2`; if the rank is not two, - raise an exception. - - OUTPUT: - - - an element in the base ring if the rank is two; an - exception is raised otherwise - """ - self._test_rank_two() - return self.gen()[1] - - def j(self): - r""" - If the rank is two, return the j-invariant of the Drinfeld - module; if the rank is not two, raise an exception. - - Write the generator `\phi_X = \gamma(X) + g\tau + \Delta\tau^2`. - The j-invariant is defined by `\frac{g^{q+1}}{\Delta}`, `q` - being the order of the base field of the polynomial ring. In our - case, this base field is always finite, as we force the function - ring to be of the form `\Fq[X]`. - - OUTPUT: - - - an element in the base ring if the rank is two; an - exception is raised otherwise - """ - self._test_rank_two() - return (self.g()**(self._Fq.order()+1)) / self.delta() + def _check_rank_two(self): + r""" + Raise ``NotImplementedError`` if the rank is not two. + """ + if self.rank() != 2: + raise NotImplementedError('the rank must be 2') diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index d6d55fa4706..4fa9c15eea2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -19,7 +19,7 @@ def frobenius_charpoly(self, var='T'): return S([self.frobenius_norm(), -self.frobenius_trace(), 1]) def frobenius_norm(self): - self._test_rank_two() + self._check_rank_two() # Notations from Schost-Musleh: n = self._base_ring.over(self._Fq).degree_over(self._Fq) d = self.characteristic().degree() @@ -28,7 +28,7 @@ def frobenius_norm(self): return ((-1)**n) * (self.characteristic()**m) / norm def frobenius_trace(self): - self._test_rank_two() + self._check_rank_two() # Notations from Schost-Musleh: n = self._base_ring.over(self._Fq).degree_over(self._Fq) B = self.frobenius_norm() @@ -36,9 +36,9 @@ def frobenius_trace(self): return self.invert(t**n + (self(B) // t**n)) def is_ordinary(self): - self._test_rank_two() + self._check_rank_two() return not self.is_supersingular() def is_supersingular(self): - self._test_rank_two() + self._check_rank_two() return self.characteristic().divides(self.frobenius_trace()) From ec1d6bd1c598177f1bea4cc59a570143067a97fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Sat, 13 Aug 2022 15:38:27 +0200 Subject: [PATCH 067/197] Refactor invert method in DrinfeldModule Handle the case when the degree of the input is not a multiple of the rank, and change notations inside the method definition. --- .../drinfeld_modules/drinfeld_module.py | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 266119365fa..0aaaf206627 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -896,21 +896,26 @@ def invert(self, ore_pol): sage: phi_r5.invert(phi_r5(a)) == a True """ - if not ore_pol in self.ore_polring(): + deg = ore_pol.degree() + r = self.rank() + if not ore_pol in self._ore_polring: raise TypeError('input must be an Ore polynomial') if ore_pol in self._base_ring: return self._Fq(ore_pol) - r = self.rank() - X = self.function_ring().gen() - k = ore_pol.degree() // r - m_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] + if deg % r != 0: + return None + + k = deg // r + X = self._function_ring.gen() + mat_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] for i in range(k+1): phi_X_i = self(X**i) for j in range(i+1): - m_lines[j][i] = phi_X_i[r*j] - m = Matrix(m_lines) - v = vector([list(ore_pol)[r*j] for j in range(k+1)]) - pre_image = self.function_ring()(list((m**(-1)) * v)) + mat_lines[j][i] = phi_X_i[r*j] + mat = Matrix(mat_lines) + vec = vector([list(ore_pol)[r*j] for j in range(k+1)]) + pre_image = self._function_ring(list((mat**(-1)) * vec)) + if self(pre_image) == ore_pol: return pre_image else: From dca2f7964e11e9999196a90dcfcb7dce590c5aa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 16 Aug 2022 17:19:20 +0200 Subject: [PATCH 068/197] Create method random_element in DrinfeldModules --- src/sage/categories/drinfeld_modules.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index c7d3ced6d59..12d32cb230b 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -11,6 +11,7 @@ from sage.categories.category import CategoryWithParameters from sage.misc.functional import log +from sage.rings.integer import Integer from sage.rings.morphism import RingHomomorphism from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing @@ -72,6 +73,25 @@ def morphism(self): def ore_polring(self): return self._ore_polring + def random_element(self, rank): + + if not isinstance(rank, Integer): + raise TypeError('rank must be a positive integer') + if rank <= 0: + raise ValueError('rank must be a positive integer') + + K = self.base() + coeffs = [self._constant_coefficient] + for _ in range(rank-1): + coeffs.append(K.random_element()) + dom_coeff = 0 + while dom_coeff == 0: + dom_coeff = K.random_element() + coeffs.append(dom_coeff) + + return self(coeffs) + + # Sage methods def _call_(self, gen): From ad98d81dba0cfe02572d3369ad2e6a27a1df0d25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 16 Aug 2022 17:19:56 +0200 Subject: [PATCH 069/197] Create method constant_coefficient in DrinfeldModules --- src/sage/categories/drinfeld_modules.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 12d32cb230b..229afc0a36c 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -49,6 +49,8 @@ def __init__(self, morphism, name='t'): tau = K.frobenius_endomorphism(d) self._ore_polring = OrePolynomialRing(K, tau, names=name, polcast=False) + # Create constant coefficient + self._constant_coefficient = gamma(X) # Create characteristic self._characteristic = None if K.is_finite(): @@ -64,6 +66,9 @@ def characteristic(self): raise NotImplementedError return self._characteristic + def constant_coefficient(self): + return self._constant_coefficient + def function_ring(self): return self._function_ring From 20e2eadfb1460fdf3cafff08ab7ae7bc4c7973d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 16 Aug 2022 17:20:08 +0200 Subject: [PATCH 070/197] Refactor _repr_ method in DrinfeldModuleHomset The previous version was really hard to parse. However, I don't know if the current version is standard. --- src/sage/rings/function_field/drinfeld_modules/homset.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 4dddb310049..aa295646e42 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -21,8 +21,9 @@ def __init__(self, X, Y, category=None, check=True): Homset.__init__(self, X, Y, category=category, base=base, check=check) def _repr_(self): - return f'Set of Drinfeld module morphisms from ' \ - f'{self.domain().gen()} to {self.codomain().gen()}' + return f'Set of Drinfeld module morphisms:\n' \ + f' From: {self.domain()}\n' \ + f' To: {self.codomain()}' def __contains__(self, x): try: From d61a0cf2e8330f8d8bacb6c7c3bf52bd2c5a6988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 16 Aug 2022 18:57:38 +0200 Subject: [PATCH 071/197] Apply David's suggestions (ticket 33713 comment 80) See https://trac.sagemath.org/ticket/33713#comment:80. --- .../drinfeld_modules/drinfeld_module.py | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 0aaaf206627..cf7ccec125c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -73,9 +73,9 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): Drinfeld modules are defined in a larger setting, in which `\Fq[X]` is replaced by a more general ring: the ring of functions in `k` that are regular outside `\infty`, where `k` is - a function field whose constant field is `\Fq` and with - transcendance degree `1`, and `\infty` is a fixed place of `k`. - This is out of the scope of this implementation. + a function field over `\Fq` with transcendance degree `1` and + `\infty` is a fixed place of `k`. This is out of the scope of + this implementation. INPUT: @@ -230,7 +230,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for - every $a \in \Fq[X]`. In our case, this is equivalent to verifying + every `a \in \Fq[X]`. In our case, this is equivalent to verifying `f \phi_X = \psi_X f`. An *isogeny* is a non-zero morphism. Use the ``in`` syntax to test if an Ore polynomial defines an @@ -322,7 +322,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: Ltau(phi(X)) == phi_rebased(X) True - Given an Ore polynomial that equals `\phi(a)` for some `a \in + Given an Ore polynomial that equals `\phi_a` for some `a \in \Fq[X]`, we can retrieve `a` (as a morphism, a Drinfeld module is injective, see [Gos1998]_, cor. 4.5.2.):: @@ -360,8 +360,8 @@ def __classcall_private__(cls, function_ring, gen, name='t'): ore_polring = None ore_polring_base = Sequence(gen).universe() else: - raise TypeError('generator must be list of coefficients or an ' \ - 'Ore polynomial') + raise TypeError('generator must be a list of coefficients '\ + 'or an Ore polynomial') # Build the morphism that defines the category if not ore_polring_base.has_coerce_map_from(function_ring.base_ring()): @@ -738,14 +738,12 @@ def __call__(self, a): def change_ring(self, new_field, name=None): r""" - If ``new_field`` is a field extension of the base ring, return a - new Drinfeld module that extends ``self`` to the base ring - ``new_field``. + Return a Drinfeld module defined like ``self``, but with base + ring ``new_field``. - Let `f` be the morphism that defines ``self``, let `i` be the - inclusion of ``self.ore_polring()`` into the Ore pol. ring whose - base is ``new_field``. The morphism that defines the new - Drinfeld module is the composition `i \circ f`. + The new base can either be a field extension of the base ring, + or field that has a coercion map from the field of definitions + of the coefficients of the generator. INPUT: @@ -832,8 +830,8 @@ def height(self): def invert(self, ore_pol): r""" - Find the inverse of ``ore_pol`` by the morphism that defines the - Drinfeld module. If ``ore_pol`` is not in the image of the + Return the inverse ``ore_pol`` by the morphism that defines the + Drinfeld module; if ``ore_pol`` is not in the image of the morphism, return ``None``. Said otherwise, return `a` if ``ore_pol`` is `phi_a`, otherwise @@ -923,7 +921,7 @@ def invert(self, ore_pol): def is_finite(self): r""" - Return ``True`` if the Drinfeld module is finite, return + Return ``True`` if the Drinfeld module is finite; return ``False`` otherwise. OUTPUT: @@ -949,10 +947,9 @@ def is_finite(self): def j(self): r""" - Return the j-invariant of the Drinfeld module. - - Only the rank two case has been implemented. An - NotImplementedError is raised if the rank is not two. + Return the j-invariant of the Drinfeld module; only the rank two + case has been implemented, a NotImplementedError is raised if + the rank is not two. Assume the rank is two. Write the generator `\phi_X = \gamma(X) + g\tau + \Delta\tau^2`. The j-invariant is defined by @@ -1028,7 +1025,7 @@ def rank(self): def velu(self, isog): r""" Return a new Drinfeld module such that ``isog`` is an - isogeny to this module with domain ``self``. If no such isogeny + isogeny to this module with domain ``self``; if no such isogeny exists, return ``None``. If the input is zero, return ``None``, as an isogeny is From f8e34f2d2ccea453f0cc98ca0fe6d154b352c10d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 17 Aug 2022 13:15:59 +0200 Subject: [PATCH 072/197] Make DrinfeldModuleMorphism inherit UniqueRepresentation Thanks Travis for the help. Not too confident about the code here, but preliminary tests pass. --- .../function_field/drinfeld_modules/homset.py | 5 +++- .../drinfeld_modules/morphism.py | 30 +++++++++---------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index aa295646e42..595a8836df2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -33,4 +33,7 @@ def __contains__(self, x): return False def _element_constructor_(self, *args, **kwds): - return self.element_class(self, *args, **kwds) + # NOTE: This used to be self.element_class(self, ...), but this + # would call __init__ instead of __classcall_private__. This + # seems to work, but I don't know what I'm doing. + return DrinfeldModuleMorphism(self, *args, **kwds) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 4a7e4f175f7..c1d6b29404e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -8,17 +8,22 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -# from sage.categories.morphism import Morphism +from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.structure.element import Element +from sage.structure.unique_representation import UniqueRepresentation from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing from sage.categories.drinfeld_modules import DrinfeldModules -class DrinfeldModuleMorphism(Element): +class DrinfeldModuleMorphism(UniqueRepresentation, Element, + metaclass=InheritComparisonClasscallMetaclass): - def __init__(self, parent, x): - super().__init__(parent) + @staticmethod + def __classcall_private__(cls, parent, x): + from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset + if not isinstance(parent, DrinfeldModuleHomset): + raise TypeError('parent should be a DrinfeldModuleHomset') domain = parent.domain() codomain = parent.codomain() # NOTE: it used to be x.parent() is parent, but this was False. @@ -30,18 +35,13 @@ def __init__(self, parent, x): ore_pol = domain.ore_polring()(x) if ore_pol * domain.gen() != codomain.gen() * ore_pol: raise ValueError('the Ore polynomial does not define a morphism') - self._domain = domain - self._codomain = codomain - self._ore_polynomial = ore_pol + return cls.__classcall__(cls, parent, ore_pol) - # NOTE: Should I inherit UniqueRepresentation to avoid this? - def __eq__(self, other): - try: - if self.parent() == other.parent(): - return self.ore_polynomial() == other.ore_polynomial() - except AttributeError: - return False - return False + def __init__(self, parent, ore_pol): + Element.__init__(Element(parent), parent) + self._domain = parent.domain() + self._codomain = parent.codomain() + self._ore_polynomial = ore_pol def _repr_(self): return f'Drinfeld Module morphism:\n' \ From e9b8913dd4abfcb3adbe2aa47df27fa534a00eef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 17 Aug 2022 13:47:35 +0200 Subject: [PATCH 073/197] Create _latex_ method in DrinfeldModuleMorphism --- .../rings/function_field/drinfeld_modules/morphism.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index c1d6b29404e..3e764661cb1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -9,6 +9,7 @@ #***************************************************************************** from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass +from sage.misc.latex import latex from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation from sage.rings.polynomial.ore_polynomial_element import OrePolynomial @@ -43,6 +44,14 @@ def __init__(self, parent, ore_pol): self._codomain = parent.codomain() self._ore_polynomial = ore_pol + def _latex_(self): + return f'\\begin{{array}}{{l}}\n' \ + f'\\text{{Drinfeld{{ }}module{{ }}morphism:}}\\\\\n' \ + f'\\text{{{{ }}{{ }}From:{{ }}}}{latex(self._domain)}}}\\\\\n' \ + f'\\text{{{{ }}{{ }}To:{{ }}}}{{ }}{{ }}{latex(self._codomain)}\\\\\n' \ + f'\\text{{{{ }}{{ }}Defn:{{ }}}}{latex(self._ore_polynomial)}\n' \ + f'\\end{{array}}' + def _repr_(self): return f'Drinfeld Module morphism:\n' \ f' From: {self._domain}\n' \ From 6e4595b0a4b0a1343a18a6dcecd38912ec08fc22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 17 Aug 2022 14:30:47 +0200 Subject: [PATCH 074/197] (fix) Remove delta() in FiniteDrinfeldModule --- .../function_field/drinfeld_modules/finite_drinfeld_module.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 4fa9c15eea2..92b93509661 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -24,7 +24,8 @@ def frobenius_norm(self): n = self._base_ring.over(self._Fq).degree_over(self._Fq) d = self.characteristic().degree() m = n // d - norm = self._base_ring.over(self._Fq)(self.delta()).norm() + delta = self._gen[2] + norm = self._base_ring.over(self._Fq)(delta).norm() return ((-1)**n) * (self.characteristic()**m) / norm def frobenius_trace(self): From 6e5cd2d42441cabcb2a65fb7b7afd3c041bbbaca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 17 Aug 2022 14:25:02 +0200 Subject: [PATCH 075/197] Write docstrings for DrinfeldModuleMorphism Docstrings for: - the module; - the class; - the methods. Also, I reordered the methods in the class definition, to comply with the SageMath source. --- .../drinfeld_modules/morphism.py | 247 +++++++++++++++++- 1 file changed, 244 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 3e764661cb1..02de4a781cb 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -1,3 +1,13 @@ +r""" +Drinfeld module morphisms + +This module provides the class +:class:`sage.rings.function_fields.drinfeld_module.morphism.DrinfeldModuleMorphism`. + +AUTHORS: +- Antoine Leudière (2022-04) +""" + #***************************************************************************** # Copyright (C) 2022 Antoine Leudière # @@ -19,6 +29,78 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, metaclass=InheritComparisonClasscallMetaclass): + r""" + Let `\phi,\psi` be two Drinfeld modules defined over the + `\Fq[X]`-field `K`. A *morphism of Drinfeld modules `\phi \to \psi`* + is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a + f` for every `a \in \Fq[X]`. In our case, this is equivalent to + verifying `f \phi_X = \psi_X f`. An *isogeny* is a non-zero + morphism. + + A Drinfeld module morphism is represented by instances of the class + `DrinfeldModuleMorphism`. + + To create a morphism object, do not explicitely use + `DrinfeldModuleMorphism`, but rather call the parent homset with the + defining Ore polynomial:: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: t = phi.ore_variable() + sage: ore_pol = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + sage: psi = phi.velu(ore_pol) + sage: morphism = Hom(phi, psi)(ore_pol) + sage: morphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + To: Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + + We can get basic data on the morphism:: + + sage: morphism.domain() + Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + sage: morphism.domain() is phi + True + + sage: morphism.codomain() + Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + sage: morphism.codomain() is psi + True + + sage: morphism.ore_polynomial() + t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + sage: morphism.ore_polynomial() is ore_pol + True + + We can check various properties:: + + sage: morphism.is_zero() + False + sage: morphism.is_isogeny() + True + sage: morphism.is_endomorphism() + False + sage: morphism.is_isomorphism() + False + + .. NOTE:: + + For the sake of completness, we explain how the user can + directly instanciate the class:: + + sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism + sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + To: Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) is morphism + True + """ @staticmethod def __classcall_private__(cls, parent, x): @@ -59,19 +141,178 @@ def _repr_(self): f' Defn: {self._ore_polynomial}' def codomain(self): + r""" + Return the codomain of the morphism. + + EXAMPLES: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism.codomain() + Drinfeld module defined by X |--> t^2 + (z6^4 + z6^2 + 1)*t + z6 over Finite Field in z6 of size 2^6 + sage: morphism.codomain() is psi + True + """ return self._codomain def domain(self): - return self._domain + r""" + Return the codomain of the morphism. - def ore_polynomial(self): - return self._ore_polynomial + EXAMPLES: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism.domain() + Drinfeld module defined by X |--> t^2 + t + z6 over Finite Field in z6 of size 2^6 + sage: morphism.domain() is phi + True + """ + return self._domain def is_zero(self): + r""" + Return the codomain of the morphism. + + EXAMPLES: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism.is_zero() + False + + sage: zero_morphism = End(phi)(0) + sage: zero_morphism.is_zero() + True + """ return self._ore_polynomial.is_zero() + def is_endomorphism(self): + r""" + Return True if the morphism is an endomorphism; return False + otherwise. + + EXAMPLES: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism.is_endomorphism() + False + + sage: zero_morphism = End(phi)(0) + sage: zero_morphism.is_endomorphism() + True + + sage: identity_morphism = End(phi)(1) + sage: identity_morphism.is_endomorphism() + True + + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism.is_endomorphism() + True + """ + return self._domain is self._codomain + def is_isogeny(self): + r""" + Return True if the morphism is an isogeny; return False + otherwise. + + EXAMPLES: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism.is_isogeny() + True + + sage: zero_morphism = End(phi)(0) + sage: zero_morphism.is_isogeny() + False + + sage: identity_morphism = End(phi)(1) + sage: identity_morphism.is_isogeny() + True + + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism.is_isogeny() + True + """ return not self.is_zero() def is_isomorphism(self): + r""" + Return True if the morphism is an isomorphism; return False + otherwise. + + EXAMPLES: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism.is_isomorphism() + False + + sage: zero_morphism = End(phi)(0) + sage: zero_morphism.is_isomorphism() + False + + sage: identity_morphism = End(phi)(1) + sage: identity_morphism.is_isomorphism() + True + + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism.is_isomorphism() + False + """ return self.is_isogeny() and self._ore_polynomial.degree() == 0 + + def ore_polynomial(self): + r""" + Return the Ore polynomial that defines the morphism. + + EXAMPLES: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: ore_pol = morphism.ore_polynomial() + sage: ore_pol + t + z6^5 + z6^2 + 1 + + sage: ore_pol * phi(X) == psi(X) * ore_pol + True + """ + return self._ore_polynomial From 5decb91d4789727ac795d6c0fafc331fe8fdd8de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 18 Aug 2022 11:47:05 +0200 Subject: [PATCH 076/197] Define __init__ and self._frobenius_trace/norm for FiniteDrinfeldModule I wanted to cache the Frobenius trace and norm, as to compute them only once. For this, I needed to add self._frobenius_norm = None self._frobenius_trace = None in __init__. However, __init__ did not exist (we simply did not need it), so I created it. If there is any better way to set self._frobenius_norm and self._frobenius_trace at init, please tell me. --- .../finite_drinfeld_module.py | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 92b93509661..050b48d1fa3 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -3,6 +3,16 @@ class FiniteDrinfeldModule(DrinfeldModule): + def __init__(self, gen, category): + + # NOTE: There used to be no __init__ here (which was fine). I + # added one to ensure that FiniteDrinfeldModule would always + # have _frobenius_norm and _frobenius_trace attributes. + + super().__init__(gen, category) + self._frobenius_norm = None + self._frobenius_trace = None + def frobenius_endomorphism(self): t = self.ore_variable() L = self._base_ring @@ -21,20 +31,24 @@ def frobenius_charpoly(self, var='T'): def frobenius_norm(self): self._check_rank_two() # Notations from Schost-Musleh: - n = self._base_ring.over(self._Fq).degree_over(self._Fq) - d = self.characteristic().degree() - m = n // d - delta = self._gen[2] - norm = self._base_ring.over(self._Fq)(delta).norm() - return ((-1)**n) * (self.characteristic()**m) / norm + if self._frobenius_norm is None: + n = self._base_ring.over(self._Fq).degree_over(self._Fq) + d = self.characteristic().degree() + m = n // d + delta = self._gen[2] + norm = self._base_ring.over(self._Fq)(delta).norm() + self._frobenius_norm = ((-1)**n) * (self.characteristic()**m) / norm + return self._frobenius_norm def frobenius_trace(self): self._check_rank_two() # Notations from Schost-Musleh: - n = self._base_ring.over(self._Fq).degree_over(self._Fq) - B = self.frobenius_norm() - t = self.ore_polring().gen() - return self.invert(t**n + (self(B) // t**n)) + if self._frobenius_trace is None: + n = self._base_ring.over(self._Fq).degree_over(self._Fq) + B = self.frobenius_norm() + t = self.ore_polring().gen() + self._frobenius_trace = self.invert(t**n + (self(B) // t**n)) + return self._frobenius_trace def is_ordinary(self): self._check_rank_two() From daf9b8a45624b8d7ff0ec9a54a20d8b96eaf0661 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 18 Aug 2022 11:50:54 +0200 Subject: [PATCH 077/197] (minor) Add small enhancements to DrinfeldModule doc --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index cf7ccec125c..254a30f05a6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -39,6 +39,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): r""" + This class handles Drinfeld modules. + Let `q` be the order of a finite field `\Fq`. Let `K` be a field equiped a ring morphism `\gamma: \Fq[X] \to K` --- the field `K` is said to be an *`\Fq[X]`-field*, and the monic polynomial that @@ -872,7 +874,8 @@ def invert(self, ore_pol): ALGORITHM: - See [MS2019]_, 3.2.5. + The algorithm relies on the inversion of a linear algebra + system. See [MS2019]_, 3.2.5 for details. TESTS:: From f16c65afdf4ca6ee7e7a5664f66fc85cc60c2c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 18 Aug 2022 11:52:38 +0200 Subject: [PATCH 078/197] Write docstrings for FiniteDrinfeldModule --- .../finite_drinfeld_module.py | 343 ++++++++++++++++++ 1 file changed, 343 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 050b48d1fa3..68921a0f508 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -1,7 +1,103 @@ +r""" +Finite Drinfeld modules + +This module provides the class +:class:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.FiniteDrinfeldModule`, +which inherits +:class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. + +AUTHORS: + +- Antoine Leudière (2022-04) +""" + +#***************************************************************************** +# Copyright (C) 2022 Antoine Leudière +# +# 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. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule class FiniteDrinfeldModule(DrinfeldModule): + r""" + This class handles finite Drinfeld modules. + + A *finite Drinfeld module* is a Drinfeld module whose base ring is + finite. For general definitions and help on Drinfeld modules, see + class + :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. + In this specific documentation, we only present the specifics of + ``FiniteDrinfeldModle``. + + The user does not ever need to directly call + ``FiniteDrinfeldModule``, as it is the (meta)class + ``DrinfeldModule`` that is reponsible for instanciating + ``DrinfeldModule`` or ``FiniteDrinfeldModule`` depending on its + input:: + + sage: Fq = GF(343) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z6, 0, 5]) + sage: isinstance(phi, DrinfeldModule) + True + sage: from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule + sage: isinstance(phi, FiniteDrinfeldModule) + True + + But, the user should never use ``FiniteDrinfeldModule`` to test if a + Drinfeld module is finite, but rather the ``is_finite`` method:: + + sage: phi.is_finite() + True + + .. RUBRIC:: Complex multiplication of rank two finite Drinfeld modules + + We can handle some aspects of the theory of complex multiplication + of finite Drinfeld modules. Apart from the method + ``frobenius_endomorphism``, we only handle rank two Drinfeld + modules. + + First of all, it is easy to create the Frobenius endomorphism:: + + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> 5*t^2 + z6 over Finite Field in z6 of size 7^6 + To: Drinfeld module defined by X |--> 5*t^2 + z6 over Finite Field in z6 of size 7^6 + Defn: t^2 + + Its characteristic polynomial can be computed:: + + sage: chi = phi.frobenius_charpoly() + sage: chi + T^2 + (X + 2*z3^2 + 2*z3 + 1)*T + 2*X^2 + (z3^2 + z3 + 4)*X + 2*z3 + sage: frob_pol = frobenius_endomorphism.ore_polynomial() + sage: chi(frob_pol, phi(X)) + 0 + + This makes it possible to compute the Frobenius trace and norm:: + + sage: phi.frobenius_trace() + 6*X + 5*z3^2 + 5*z3 + 6 + sage: phi.frobenius_trace() == -chi[1] + True + sage: phi.frobenius_norm() + 2*X^2 + (z3^2 + z3 + 4)*X + 2*z3 + + And to decide if a Drinfeld module is ordinary or supersingular:: + + sage: phi.is_ordinary() + True + sage: phi.is_supersingular() + False + """ def __init__(self, gen, category): @@ -14,6 +110,34 @@ def __init__(self, gen, category): self._frobenius_trace = None def frobenius_endomorphism(self): + r""" + Return the Frobenius endomorphism, as an instance of + ``DrinfeldModuleMorphism``, of the Drinfeld module, if the rank + is two; raise a NotImplementedError otherwise.. + + Let `q` be the order of the base field of the function ring. The + *Frobenius endomorphism* is defined as the endomorphism whose + defining Ore polynomial is `t^q`. + + OUTPUT: + + - a Drinfeld module morphism + + EXAMPLES: + + sage: Fq = GF(343) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi.frobenius_endomorphism() + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> z6*t^2 + 1 over Finite Field in z6 of size 7^6 + To: Drinfeld module defined by X |--> z6*t^2 + 1 over Finite Field in z6 of size 7^6 + Defn: t^2 + sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism + sage: isinstance(phi.frobenius_endomorphism(), DrinfeldModuleMorphism) + True + """ t = self.ore_variable() L = self._base_ring Fq = self._function_ring.base_ring() @@ -21,6 +145,74 @@ def frobenius_endomorphism(self): return self._Hom_(self, category=self.category())(t**deg) def frobenius_charpoly(self, var='T'): + r""" + Return the characteristic polynomial of the Frobenius + endomorphism, if the rank is two; raise a NotImplementedError + otherwise. + + Let `\Fq` be the base field of the function ring. The + *characteristic polynomial `\chi` of the Frobenius endomorphism* is + defined in [Gek1991]_. An important feature of this polynomial + is that it is a monic bivariate polynomial in `T` with + coefficients in `\Fq[X]`. Write `\chi = T^2 - A(X)T + B(X)`, let + `t^n` be the Ore polynomial that defines the Frobenius + endomorphism of `\phi`; by definition, `n` is the degree of the + base ring over `\Fq`. We have `\chi(t^n)(\phi(X)) = t^{2n} - + \phi_A t^n + \phi_B = 0`, with `\deg(A) \leq \frac{n}{2}` and + `\deg(B) = n`. + + Note that the *Frobenius trace* is defined as `A(X)` and the + *Frobenius norm` is defined as `B(X)`. + + INPUT: + + - ``var`` -- (optional) the name of the second variable + + OUTPUT: + + - a polynomial in `\Fq[X][T]` + + EXAMPLES: + + sage: Fq = GF(343) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: chi = phi.frobenius_charpoly() + sage: chi + T^2 + ((3*z3^2 + z3 + 4)*X + 4*z3^2 + 6*z3 + 3)*T + (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + + sage: frob_pol = phi.frobenius_endomorphism().ore_polynomial() + sage: chi(frob_pol, phi(X)) + 0 + + sage: A = phi.frobenius_trace() + sage: A + (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 + sage: B = phi.frobenius_norm() + sage: B + (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + + sage: n = 2 # Degree of the base ring over `\Fq` + sage: A.degree() <= n/2 + True + sage: B.degree() == n + True + + ALGORITHM: + + We compute the Frobenius norm, and with it the Frobenius + trace. This gives the Frobenius characteristic polynomial. + See [SM2019]_, Section 4. + + See docstrings of methods + :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.FiniteDrinfeldModule.frobenius_norm` + and + :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.FiniteDrinfeldModule.frobenius_trace` + for furthere details on the computation of the norm and of + the trace. + """ + self._check_rank_two() A = self._function_ring # Fq[X] S = PolynomialRing(A, name=var) # Fq[X][T] # Does not work when Fq is not a prime field... @@ -29,6 +221,43 @@ def frobenius_charpoly(self, var='T'): return S([self.frobenius_norm(), -self.frobenius_trace(), 1]) def frobenius_norm(self): + r""" + Return Frobenius norm of the Drinfeld module, if the rank is + two; raise a NotImplementedError otherwise. + + Write `\chi = T^2 - A(X)T + B(X) \in \Fq[X][T]` to be the + characteristic polynomial of the Frobenius endomorphism. The + *Frobenius norm* is defined as the polynomial `B(X) \in \Fq[X]`. + + Let `n` be the degree of the base ring over `\Fq`. Then the + Frobenius norm has degree `n`. + + OUTPUT: + + - a polynomial in `\Fq[X]` + + EXAMPLES: + + sage: Fq = GF(343) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: B = phi.frobenius_norm() + sage: B + (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + + sage: n = 2 # Degree of the base ring over `\Fq` + sage: B.degree() == n + True + + sage: B == phi.frobenius_charpoly()[0] + True + + ALGORITHM: + + The Frobenius norm is computed using the formula, by + Gekeler, given in [SM2019]_, Section 4, Proposition 3. + """ self._check_rank_two() # Notations from Schost-Musleh: if self._frobenius_norm is None: @@ -41,6 +270,52 @@ def frobenius_norm(self): return self._frobenius_norm def frobenius_trace(self): + r""" + Return Frobenius norm of the Drinfeld module, if the rank is + two; raise a NotImplementedError otherwise. + + Write `\chi = T^2 - A(X)T + B(X) \in \Fq[X][T]` to be the + characteristic polynomial of the Frobenius endomorphism. The + *Frobenius norm* is defined as the polynomial `B(X) \in \Fq[X]`. + + Let `n` be the degree of the base ring over `\Fq`. Then the + Frobenius trace has degree `\leq \frac{n}{2}`. + + OUTPUT: + + - a polynomial in `\Fq[X]` + + ALGORITHM: + + Let `A(X)` denote the Frobenius trace and `B(X)` denote the + Frobenius norm. We begin by computing `B(X)`, see docstring + of method + :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.FiniteDrinfeldModule.frobenius_norm` + for details. The characteristic polynomial of the Frobenius + yields `t^{2n} - \phi_A t^n + \phi_B = 0`, where `t^n` is + the Frobenius endomorphism. As `\phi_B` is now known, we can + compute `\phi_A = (t^{2n} + \phi_B) / t^n`. We get `A(X)` by + inverting this quantity, using the method + :meth:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule.invert`, + see its docstring for details. + + EXAMPLES: + + sage: Fq = GF(343) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: A = phi.frobenius_trace() + sage: A + (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 + + sage: n = 2 # Degree of the base ring over `\Fq` + sage: A.degree() <= n/2 + True + + sage: A == -phi.frobenius_charpoly()[1] + True + """ self._check_rank_two() # Notations from Schost-Musleh: if self._frobenius_trace is None: @@ -51,9 +326,77 @@ def frobenius_trace(self): return self._frobenius_trace def is_ordinary(self): + r""" + Return True if the Drinfeld module is ordinary, return False + otherwise; raise a NotImplementedError if the rank is not two. + + A rank two finite Drinfeld module is *ordinary* if and only if + the `\Fq[X]-characteristic of the base ring does not devide the + Frobenius trace. A *supersingular* rank two finite Drinfeld + module is a Drinfeld module that is not ordinary. + + OUTPUT: + + - a boolean + + EXAMPLES: + + sage: Fq = GF(343) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi.is_ordinary() + False + sage: phi_p = phi(phi.characteristic()) + sage: phi_p # Purely inseparable + z6*t^2 + + ALGORITHM: + + Compute the Frobenius trace and test if the `\Fq[X]` + characteristic divides it. + + We could also test if the image of the + `\Fq[X]`-characteristic under the Drinfeld module is purely + inseparable; see [Gek1991]_, Proposition 4.1. + """ self._check_rank_two() return not self.is_supersingular() def is_supersingular(self): + r""" + Return True if the Drinfeld module is supersingular, return False + otherwise; raise a NotImplementedError if the rank is not two. + + A rank two finite Drinfeld module is *supersingular* if and only + if the `\Fq[X]-characteristic of the base ring devides the + Frobenius trace. An *ordinary* rank two finite Drinfeld module + is a Drinfeld module that is not supersingular. + + OUTPUT: + + - a boolean + + EXAMPLES: + + sage: Fq = GF(343) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi.is_supersingular() + True + sage: phi_p = phi(phi.characteristic()) + sage: phi_p # Purely inseparable + z6*t^2 + + ALGORITHM: + + Compute the Frobenius trace and test if the `\Fq[X]` + characteristic divides it. + + We could also test if the image of the + `\Fq[X]`-characteristic under the Drinfeld module is purely + inseparable; see [Gek1991]_, Proposition 4.1. + """ self._check_rank_two() return self.characteristic().divides(self.frobenius_trace()) From a1e569d665a9272bf116f5d632a5038015f2cd16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 18 Aug 2022 15:36:48 +0200 Subject: [PATCH 079/197] Refactor exception messages for Drinfeld modules Simplify and shorten messages. --- src/sage/categories/drinfeld_modules.py | 10 +++++----- .../function_field/drinfeld_modules/action.py | 2 +- .../drinfeld_modules/drinfeld_module.py | 17 +++++++++-------- .../function_field/drinfeld_modules/morphism.py | 2 +- 4 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 229afc0a36c..b4cd81bf3ef 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -27,23 +27,23 @@ def __init__(self, morphism, name='t'): gamma = morphism # Check input is a ring Morphism if not isinstance(gamma, RingHomomorphism): - raise TypeError('input must be a Ring morphism') + raise TypeError('category input must be a Ring morphism') self._morphism = morphism self._function_ring = gamma.domain() # Check domain is Fq[X] function_ring = self._function_ring if not isinstance(function_ring, PolynomialRing_general): - raise NotImplementedError('domain must be a polynomial ring') + raise NotImplementedError('function ring must be a polynomial ring') function_ring_base = function_ring.base_ring() if not function_ring_base.is_field() or not function_ring_base.is_finite() : - raise TypeError('the base ring of the domain must be a finite field') + raise TypeError('function ring base must be a finite field') Fq = function_ring_base FqX = function_ring X = FqX.gen() # Check codomain of gamma is field K = gamma.codomain() if not K.is_field(): - raise TypeError('the codomain of the morphism must be a field') + raise TypeError('base must be a field') # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) @@ -105,7 +105,7 @@ def _call_(self, gen): # If gen is not in the codomain, an exception is raised gen = self._ore_polring(gen) if self.characteristic()(gen[0]) != 0: - raise ValueError('constant coefficient is not a root of the characteristic') + raise ValueError('constant coefficient must be a root of the characteristic') return DrinfeldModule(self._function_ring, gen) def _repr_(self): diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 06ed070c7f5..82f1ee0d207 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -7,7 +7,7 @@ class DrinfeldModuleAction(Action): def __init__(self, finite_drinfeld_module): # Verifications if not isinstance(finite_drinfeld_module, DrinfeldModule): - raise TypeError('First argument must be a DrinfeldModule') + raise TypeError('input must be a DrinfeldModule') # Work self.__finite_drinfeld_module = finite_drinfeld_module super().__init__(finite_drinfeld_module.polring(), diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 254a30f05a6..9f63a8a321f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -343,10 +343,10 @@ def __classcall_private__(cls, function_ring, gen, name='t'): # here and in the category constructor, which is not ideal. # Check domain is Fq[X] if not isinstance(function_ring, PolynomialRing_general): - raise NotImplementedError('domain must be a polynomial ring') + raise NotImplementedError('function ring must be a polynomial ring') function_ring_base = function_ring.base_ring() if not function_ring_base.is_field() or not function_ring_base.is_finite() : - raise TypeError('the base ring of the domain must be a finite field') + raise TypeError('function ring base must be a finite field') Fq = function_ring_base FqX = function_ring X = FqX.gen() @@ -362,13 +362,14 @@ def __classcall_private__(cls, function_ring, gen, name='t'): ore_polring = None ore_polring_base = Sequence(gen).universe() else: - raise TypeError('generator must be a list of coefficients '\ - 'or an Ore polynomial') + raise TypeError('generator must be list of coefficients or Ore ' \ + 'polynomial') + # The coefficients are in a base ring that has coercion from Fq: + if not (hasattr(ore_polring_base, 'has_coerce_map_from') and \ + ore_polring_base.has_coerce_map_from(function_ring.base_ring())): + raise ValueError('function ring base must coerce to base ring') # Build the morphism that defines the category - if not ore_polring_base.has_coerce_map_from(function_ring.base_ring()): - raise TypeError('base ring of function ring must coerce to base ' \ - 'ring of Ore polynomial ring') gamma = function_ring.hom([ore_polring_base(gen[0])]) # Other checks in the category definition @@ -1143,4 +1144,4 @@ def _check_rank_two(self): Raise ``NotImplementedError`` if the rank is not two. """ if self.rank() != 2: - raise NotImplementedError('the rank must be 2') + raise NotImplementedError('rank must be 2') diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 02de4a781cb..b7792a5a907 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -117,7 +117,7 @@ def __classcall_private__(cls, parent, x): else: # x is an Ore polynomial ore_pol = domain.ore_polring()(x) if ore_pol * domain.gen() != codomain.gen() * ore_pol: - raise ValueError('the Ore polynomial does not define a morphism') + raise ValueError('Ore polynomial does not define a morphism') return cls.__classcall__(cls, parent, ore_pol) def __init__(self, parent, ore_pol): From 5e6f4c72d724b7364a2b168ef6a1a909fc598b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 18 Aug 2022 15:39:45 +0200 Subject: [PATCH 080/197] Refactor invert method in DrinfeldModule Raise an exception when the input is not in the image of the Drinfeld module, instead of returning None. --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 9f63a8a321f..d6d60540781 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -905,7 +905,7 @@ def invert(self, ore_pol): if ore_pol in self._base_ring: return self._Fq(ore_pol) if deg % r != 0: - return None + raise ValueError('input must be in the image of the Drinfeld module') k = deg // r X = self._function_ring.gen() From 880ab6714dea3070600e48d6b122347efad50b12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 18 Aug 2022 15:40:38 +0200 Subject: [PATCH 081/197] Proofread docstrings in DrinfeldModule No fundamental change, only rephrasing and enhancements. --- .../drinfeld_modules/drinfeld_module.py | 363 +++++++++++------- 1 file changed, 228 insertions(+), 135 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index d6d60540781..f3b69307931 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -41,25 +41,26 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): r""" This class handles Drinfeld modules. - Let `q` be the order of a finite field `\Fq`. Let `K` be a field + Let `\Fq` be a finite field with order `q`. Let `K` be a field equiped a ring morphism `\gamma: \Fq[X] \to K` --- the field `K` is said to be an *`\Fq[X]`-field*, and the monic polynomial that generates `\Ker(\gamma)` is called the *`\Fq[X]`-characteristic of - the `\Fq[X]`-field `K`* (this `\Fq[X]`-characteristic plays the role in - `\Fq[X]` of the standard characteristic, in `\ZZ`, of a finite - field). Let `K\{\tau\}` be the ring of Ore polynomials with - coefficients in `K` and Frobenius variable `\tau: x \mapsto x^q`. A - *Drinfeld `\Fq[X]`-module over the `\Fq[X]`-field `K`* is a ring - morphism `\phi: \Fq[X] \to K\{\tau\}` such that: + the `\Fq[X]`-field `K`* (this characteristic plays the role of the + characteristic of a function field). Let `K\{\tau\}` be the ring of + Ore polynomials with coefficients in `K` and Frobenius + variable `\tau: x \mapsto x^q`. A *Drinfeld `\Fq[X]`-module over the + `\Fq[X]`-field `K`* is a ring morphism `\phi: \Fq[X] \to K\{\tau\}` + such that: - 1. The image of `\phi` has non-constant Ore polynomials. - 2. For every `a \in \Fq[X]`, the constant coefficient of the - Ore polynomial `\phi(a)` is `\gamma(a)`. + 1. The image of `\phi` contains non-constant Ore polynomials. + 2. For every `a \in \Fq[X]`, the constant coefficient `\phi(a)` + is `\gamma(a)`. For `a \in \Fq[X]`, `\phi(a)` is denoted `\phi_a`. - We say that `K` is the *base ring of `\phi`*, `\Fq[X]` is the - *function ring of*, *K\{\tau\}* is the *Ore polynomial ring*, + We say that `\Fq[X]` is the *function ring of `\phi`*; `K` is the + *base ring of `\phi`*, or simply its base or base field; `\Fq[X]` is + the *function ring of*; *K\{\tau\}* is the *Ore polynomial ring*; `t` is the *Ore variable*. The *generator of `\phi`* is `\phi_X`, its *constant coefficient* is the constant coefficient of `\phi_X`. @@ -86,7 +87,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): Ore polynomial - ``name`` (optional) the name of the Ore variable - .. RUBRIC:: Construction + .. RUBRIC:: Construction and input A Drinfeld module object (class :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`) @@ -95,39 +96,142 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: Fq. = GF(3^2) sage: FqX. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [K.gen(), 1, 1]) + sage: phi = DrinfeldModule(FqX, [z, 1, 1]) sage: phi Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - In this example, we used a list of coefficients (``[K.gen(), 1, - 1]``) to represent the Ore polynomial `\phi_X = z + t + t^2`, `K - = \Fq(z)`. We can also use regular Ore polynomials:: + In this example, we used a list of coefficients (``[z, 1, 1]``) to + represent the generator `\phi_X = z + t + t^2`, `K = \Fq(z)`. One can + also use regular Ore polynomials:: sage: ore_polring = phi.ore_polring() - sage: t = phi.ore_variable() # Equals ore_polring.gen() - sage: psi_X = K.gen() + t^3 + sage: t = phi.ore_variable() # Is ore_polring.gen() + sage: psi_X = z + t^3 sage: psi = DrinfeldModule(FqX, psi_X) sage: psi Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 3^12 + sage: psi(X) == psi_X + True + + Naturally, the input is checked, and exceptions are raised when the + input is invalid. + + The generator must have positive degree:: + + sage: DrinfeldModule(FqX, [z]) + Traceback (most recent call last): + ... + ValueError: generator must have positive degree + + The coefficients of the generator must live in some field `K` that + is the codomain of a morphism `\gamma` with domain `\Fq[X]`:: + + sage: DrinfeldModule(FqX, [z, QQ(1)]) + Traceback (most recent call last): + ... + ValueError: function ring base must coerce to base ring + + sage: DrinfeldModule(FqX, [1, QQ(1)]) + Traceback (most recent call last): + ... + ValueError: function ring base must coerce to base ring + + If the coefficients are regular integers, an exception is raised. + One needs to manually cast them to the field of their choice:: + + sage: DrinfeldModule(FqX, [1, 1, 1]) + Traceback (most recent call last): + ... + ValueError: function ring base must coerce to base ring + + sage: DrinfeldModule(FqX, [K(1), 1, 1]) + Drinfeld module defined by X |--> t^2 + t + 1 over Finite Field in z of size 3^12 + + sage: DrinfeldModule(FqX, [Fq(1), 1, 1]) + Drinfeld module defined by X |--> t^2 + t + 1 over Finite Field in z2 of size 3^2 + + The function ring must be an `\Fq[X]`:: + + sage: DrinfeldModule(K, [z, 1, 1]) + Traceback (most recent call last): + ... + NotImplementedError: function ring must be a polynomial ring + + sage: FqXY. = FqX[] + sage: DrinfeldModule(FqXY, [z, 1, 1]) + Traceback (most recent call last): + ... + TypeError: function ring base must be a finite field + + If you already defined a category of Drinfeld modules, you must + ensure that the constant coefficient is a root of the + `\Fq[X]-characteristic of the category base:: + + sage: cat = phi.category() + sage: cat([1, 1, K(1)]) + Traceback (most recent call last): + ... + ValueError: constant coefficient must be a root of the characteristic .. NOTE:: - If the Ore polynomial has coefficients in the integers, the - constructor does not try to guess if the user wants to see the - coefficients as elements of Fq, or an extension like `K`. + The reader may think that it is odd to build a Drinfeld module + without specifying the `\Fq[X]`-characteristic of the base (or, + more generally, its parent category). Indeed, the + `\Fq[X]`-characteristic plays the role of the characteristic of + a function field, and thus preexists Drinfeld modules. The base + field `K` should rather be seen as an `\Fq[X]`-field, i.e. the + field `K` equiped with a morphism `\gamma: \Fq[X] \to K`, + instead of just a field. + + However, as the characteristic may be deduced from the constant + coefficient of the Drinfeld module, we chose to ommit the + characteristic in the input of the class in order to have + concise definitions. + + .. RUBRIC:: Possible base rings + + The morphism `\gamma` does not need be surjective like in the above + examples. This is equivalent to say that the constant coefficient of + the Drinfeld module may be different to the generator of `K` over + `\Fq`. In the following example, `K` is still a degree six extension + of `\Fq`, but `\gamma` is a projection over a degree two extension + with modulus `X^3 + (z_2 + 2)X^2 + (6*z_2 + 1)X + 3z_2 + 5`:: + + sage: p = X^2 + z2 + 2 + sage: p_root = z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z + sage: rho = DrinfeldModule(FqX, [p_root, 1, 1]) + sage: rho + Drinfeld module defined by X |--> t^2 + t + z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z over Finite Field in z of size 3^12 + + Then one can check that the morphisms `\gamma` are not the same for + ``phi`` and ``rho``, and that the `\gamma` associated to `\phi` is + surjective, while the other one is not:: + + sage: rho.category().morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z + sage: phi.category().morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z Note that ``phi`` and ``psi`` are *finite* Drinfeld modules, in the - sense that `K` is finite. This is not mandatory:: + sense that `K` is finite. But `K` can be infinite:: - sage: K_infinite = Frac(FqX) - sage: phi_infinite = DrinfeldModule(FqX, [K_infinite.gen(), 1, 1]) - sage: phi_infinite + sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1, 1]) + sage: sigma Drinfeld module defined by X |--> t^2 + t + X over Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - sage: phi_infinite.is_finite() + sage: sigma.is_finite() False sage: phi.is_finite() True + .. RUBRIC:: The category of Drinfeld modules + Drinfeld modules have their own category (see class :class:`sage.categories.drinfeld_modules.DrinfeldModules`):: @@ -138,7 +242,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): Defn: X |--> z sage: phi.category() is psi.category() True - sage: phi.category() is phi_infinite.category() + sage: phi.category() is sigma.category() False This category holds crucial information, like the @@ -146,42 +250,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: char = phi.category().characteristic() - .. NOTE:: - - As the output of - :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule.category` - suggests, the morphism `\gamma` uniquely determines the category of a Drinfeld - module. - - .. RUBRIC:: More general `K` - - The field `K` does not need be generated by the constant coefficient - (i.e. generated, as an extension of `\Fq`, by the image - `\gamma(X)`). In the following example, `K` is still a - degree six extension of `\Fq`, but `\gamma` is a projection over - `\Fq[X]/p(X)`, with `p(X) = X^3 + (z_2 + 2)X^2 + (6*z_2 + 1)X + 3z_2 - + 5`:: - - sage: p = X^2 + z2 + 2 # Prime polynomial - sage: p_root = z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z # Root of p - sage: phi_inter = DrinfeldModule(FqX, [p_root, 1, 1]) - sage: phi_inter - Drinfeld module defined by X |--> t^2 + t + z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z over Finite Field in z of size 3^12 - - We can check that the morphisms `\gamma` are not the same for - ``phi`` and ``phi_inter``, and that the `\gamma` associated to - `\phi` is surjective, while the other one is not:: - - sage: phi_inter.category().morphism() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z - sage: phi.category().morphism() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z + As the output of + :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule.category` + suggests, the morphism `\gamma` uniquely determines the category of a Drinfeld + module. .. RUBRIC:: Basic methods @@ -194,29 +266,35 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: phi(1) # phi_1 1 - We can retrieve basic properties:: + One can retrieve basic properties:: sage: phi.base_ring() # K Finite Field in z of size 3^12 + sage: phi.ore_polring() # K{t} Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) + sage: phi.ore_variable() # t t + sage: phi.function_ring() # Fq[X] Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + sage: phi.gen() # phi_X t^2 + t + z sage: phi.gen() == phi(X) True + sage: phi.constant_coefficient() # Constant coefficient of phi_X z + sage: phi.morphism() # The Drinfeld module as a morphism Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) Defn: X |--> t^2 + t + z - We can compute the rank and height:: + One can compute the rank and height:: sage: phi.rank() 2 @@ -252,13 +330,13 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): True To create a SageMath object representing the morphism, call the - homset ``hom``:: + homset (``hom`` in the next example):: sage: hom = Hom(phi, phi) - sage: frob = hom(t^6) + sage: frobenius_endomorphism = hom(t^6) sage: identity_morphism = hom(1) sage: zero_morphism = hom(0) - sage: frob + sage: frobenius_endomorphism Drinfeld Module morphism: From: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 @@ -274,23 +352,23 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 Defn: 0 - We can retrieve the underlying Ore polynomial with the method + One can retrieve the underlying Ore polynomial with the method :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule.ore_polynomial`:: - sage: frob.ore_polynomial() + sage: frobenius_endomorphism.ore_polynomial() t^6 - And we can easily check if a morphism defines an isogeny or an + And one can easily check if a morphism defines an isogeny or an isomorphism (i.e. an isogeny whose underlying Ore polynomial has degree `0`):: - sage: frob.is_isogeny() + sage: frobenius_endomorphism.is_isogeny() True sage: identity_morphism.is_isogeny() True sage: zero_morphism.is_isogeny() False - sage: frob.is_isomorphism() + sage: frobenius_endomorphism.is_isomorphism() False sage: identity_morphism.is_isomorphism() True @@ -300,10 +378,9 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: The Vélu formula Let ``ore_pol`` be a non-zero Ore polynomial ``ore_pol``. For - Drinfeld module, it is easy to decide --- and find as the case may - be --- if there exists a Drinfeld module ``psi``, such that - ``ore_pol`` is an isogeny from ``self`` to ``psi``. If this - Drinfeld module exists, it is unique. + Drinfeld module, it is easy to decide if there exists a Drinfeld + module ``psi`` such that ``ore_pol`` is an isogeny from ``self`` to + ``psi``. We can also find ``psi``, which is unique. sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z sage: psi = phi.velu(ore_pol) @@ -314,6 +391,34 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: ore_pol * phi(X) == psi(X) * ore_pol True + .. RUBRIC:: The action of a Drinfeld module + + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + TODO + .. RUBRIC:: Other methods It is possible to change the base ring:: @@ -325,7 +430,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): True Given an Ore polynomial that equals `\phi_a` for some `a \in - \Fq[X]`, we can retrieve `a` (as a morphism, a Drinfeld + \Fq[X]`, one can retrieve `a` (as a morphism, a Drinfeld module is injective, see [Gos1998]_, cor. 4.5.2.):: sage: a = FqX.random_element() @@ -425,12 +530,9 @@ def base_ring(self): r""" Return the base ring of the Drinfeld module. - This is the base field of Ore polynomial ring. In particular, the base + This is the Ore polynomial ring base. In particular, the base ring is always a field. - A Drinfeld module is said to be finite if the base ring is - finite. - OUTPUT: - a field @@ -452,9 +554,8 @@ def base_ring(self): sage: phi.base_ring() is K True - Note that in the above example, the base ring does is not - generated by the constant coefficient (i.e. generated, as an - extension of `\Fq`, by the image `\gamma(X)`). The base + Note that in the above example, the constant coefficient + generates a strict sub-extension of `K/\Fq`. In fact, the base ring may also be the same as ``Fq``:: sage: psi = DrinfeldModule(FqX, [Fq(1), Fq.gen()]) @@ -463,13 +564,13 @@ def base_ring(self): sage: psi.base_ring() is Fq True - In which case the Ore polynomial ring is isomorphic to a regular + In this case the Ore polynomial ring is isomorphic to a regular polynomial ring:: - sage: psi.ore_polring() - Ore Polynomial Ring in t over Finite Field in z2 of size 5^2 twisted by Identity - sage: psi.ore_polring().twisting_morphism() - Identity endomorphism of Finite Field in z2 of size 5^2 + sage: psi.ore_polring() + Ore Polynomial Ring in t over Finite Field in z2 of size 5^2 twisted by Identity + sage: psi.ore_polring().twisting_morphism() + Identity endomorphism of Finite Field in z2 of size 5^2 TESTS:: @@ -486,12 +587,9 @@ def constant_coefficient(self): r""" Return the constant coefficient of the generator (`\phi_X`). - The `A`-characteristic of the base field (see - :meth:`sage.categories.drinfeld_modules.DrinfeldModules.characteristic`) - is the minimal polynomial of this constant term, over the base - ring of the function ring. Equivalently, the constant term is - the image, by the morphism (`\gamma`) that defines the category, - of the generator (`X`) of the polynomial ring. + From the definition of a Drinfeld module, the constant coefficient equals + `\gamma(X)`. Hence, it is a root of the `\Fq[X]`-characteristic + of the base ring. OUTPUT: @@ -507,16 +605,15 @@ def constant_coefficient(self): sage: phi.constant_coefficient() == p_root True - The constant coefficient is the image of ``X`` by the - morphism that defines the category of ``phi``:: + The constant coefficient equals `\gamma(X)`:: sage: cat = phi.category() sage: gamma = cat.morphism() sage: gamma(X) == phi.constant_coefficient() True - Two Drinfeld modules in the same category have the same constant - coefficient:: + Naturally, two Drinfeld modules in the same category have the + same constant coefficient:: sage: t = phi.ore_variable() sage: psi = cat(phi.constant_coefficient() + t^3) @@ -524,15 +621,15 @@ def constant_coefficient(self): Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 Reciprocally, it is impossible to create two Drinfeld modules in - this category if they don't share the same constant + this category if they do not share the same constant coefficient:: sage: rho = cat(phi.constant_coefficient() + 1 + t^3) Traceback (most recent call last): ... - ValueError: constant coefficient is not a root of the characteristic + ValueError: constant coefficient must be a root of the characteristic - One cal also retrieve the constant coefficient using + One can also retrieve the constant coefficient using ``phi(X)[0]`:: sage: phi.constant_coefficient() == phi(X)[0] @@ -542,11 +639,11 @@ def constant_coefficient(self): def gen(self): r""" - Return the generator (`\phi_X`) of the Drinfeld module. + Return the generator of the Drinfeld module, i.e. `\phi_X`. - This method makes sense because, in our case, the function ring is - a polynomial ring; it is generated by one element, whose image - characterizes the morphism that defines the Drinfeld module. + This method makes sense because, in our case, the function ring + `\Fq[X]`, which is `\Fq`-generated by a single element element, + whose image characterizes the Drinfeld module. OUTPUT: @@ -617,9 +714,6 @@ def ore_polring(self): r""" Return the Ore polynomial ring of the Drinfeld module. - If the Drinfeld module is defined by a morphism `A \to - K\{\tau\}`, this is the codomain `K\{\tau\}`. - OUTPUT: - an Ore polynomial ring @@ -661,7 +755,8 @@ def ore_variable(self): r""" Return the Ore variable. - This is generator of the Ore polynomial ring. + The Ore variable is defined as the generator of the Ore + polynomial ring. OUTPUT: @@ -679,7 +774,7 @@ def ore_variable(self): sage: phi.ore_variable() is phi.ore_polring().gen() True - We can use the Ore variable to instanciate new Drinfeld + One can use the Ore variable to instanciate new Drinfeld modules...:: sage: t = phi.ore_variable() @@ -697,10 +792,7 @@ def function_ring(self): r""" Return the function ring of the Drinfeld module. - If the Drinfeld module is defined by a morphism `A \to - K\{\tau\}`, this is the domain `A`. - - In our case, the function ring is a polynomial ring. + In our case, the function ring is an `\Fq[X]`. OUTPUT: @@ -741,8 +833,8 @@ def __call__(self, a): def change_ring(self, new_field, name=None): r""" - Return a Drinfeld module defined like ``self``, but with base - ring ``new_field``. + Return a Drinfeld module defined like the current one, but with + base ring ``new_field``. The new base can either be a field extension of the base ring, or field that has a coercion map from the field of definitions @@ -771,7 +863,7 @@ def change_ring(self, new_field, name=None): sage: phi_2 Drinfeld module defined by X |--> 2*t^2 + (3*z24^23 + 2*z24^22 + 2*z24^20 + z24^19 + 4*z24^18 + 3*z24^17 + 4*z24^15 + 2*z24^13 + 4*z24^12 + 4*z24^11 + 3*z24^10 + 3*z24^9 + 4*z24^8 + 4*z24^6 + 3*z24^5 + 4*z24^4 + 4*z24^3 + 2*z24)*t + 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 over Finite Field in z24 of size 5^24 - And we can check various things:: + And one can check various things:: sage: phi.change_ring(K2).change_ring(K) is phi True @@ -786,7 +878,7 @@ def change_ring(self, new_field, name=None): To: Finite Field in z24 of size 5^24 Defn: X |--> 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 - We can also change the base ring to a subfield, even though some things + One can also change the base ring to a subfield, even though some things do not work as expected:: sage: K0 = Fq.extension(2) @@ -833,12 +925,9 @@ def height(self): def invert(self, ore_pol): r""" - Return the inverse ``ore_pol`` by the morphism that defines the - Drinfeld module; if ``ore_pol`` is not in the image of the - morphism, return ``None``. - - Said otherwise, return `a` if ``ore_pol`` is `phi_a`, otherwise - return ``None``. + Return the pre-image of the input under the Drinfeld module; + raise an exception if the input is not in the image of the + Drinfeld module. INPUT: @@ -864,14 +953,18 @@ def invert(self, ore_pol): sage: phi.invert(phi(Fq.gen())) == Fq.gen() True - When the input is not in the image of the Drinfeld module, the - method returns None:: + When the input is not in the image of the Drinfeld module, an + exception is raised:: sage: t = phi.ore_variable() - sage: phi.invert(t + 1) is None - True - sage: phi.invert(t^3 + t^2 + 1) is None - True + sage: phi.invert(t + 1) + Traceback (most recent call last): + ... + ValueError: input must be in the image of the Drinfeld module + sage: phi.invert(t^3 + t^2 + 1) + Traceback (most recent call last): + ... + ValueError: input must be in the image of the Drinfeld module ALGORITHM: @@ -930,7 +1023,7 @@ def is_finite(self): OUTPUT: - - ``True`` or ``False`` + - a boolean EXAMPLES: @@ -989,7 +1082,7 @@ def j(self): sage: theta.j() Traceback (most recent call last): ... - NotImplementedError: the rank must be 2 + NotImplementedError: rank must be 2 """ self._check_rank_two() g = self._gen[1] From 999a0aefcd1fb0abfb14c799428e71061feb04c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 18 Aug 2022 15:57:09 +0200 Subject: [PATCH 082/197] Fix function_fields imports --- .../drinfeld_modules/drinfeld_module.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index f3b69307931..1f12fb31717 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -2,10 +2,10 @@ Drinfeld modules This module provides the class -:class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. +:class:`sage.rings.function_field.drinfeld_module.drinfeld_module.DrinfeldModule`. For *finite* Drinfeld modules and their theory of complex multiplication, see class -:class:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule`. +:class:`sage.rings.function_field.drinfeld_module.finite_drinfeld_module.DrinfeldModule`. AUTHORS: @@ -90,7 +90,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: Construction and input A Drinfeld module object (class - :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`) + :class:`sage.rings.function_field.drinfeld_module.drinfeld_module.DrinfeldModule`) is constructed as follows:: sage: Fq. = GF(3^2) @@ -251,7 +251,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: char = phi.category().characteristic() As the output of - :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule.category` + :meth:`sage.rings.function_field.drinfeld_module.finite_drinfeld_module.DrinfeldModule.category` suggests, the morphism `\gamma` uniquely determines the category of a Drinfeld module. @@ -353,7 +353,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): Defn: 0 One can retrieve the underlying Ore polynomial with the method - :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.DrinfeldModule.ore_polynomial`:: + :meth:`sage.rings.function_field.drinfeld_module.finite_drinfeld_module.DrinfeldModule.ore_polynomial`:: sage: frobenius_endomorphism.ore_polynomial() t^6 @@ -509,7 +509,7 @@ def __init__(self, gen, category): ########################## def _get_action_(self): - from sage.rings.function_fields.drinfeld_modules.action import DrinfeldModuleAction + from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction return DrinfeldModuleAction(self) def _latex_(self): From 942a9c8d95c0c59115ef55e533cd3f6ac999590c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 18 Aug 2022 17:17:21 +0200 Subject: [PATCH 083/197] Fix DrinfeldModuleAction This class was all broken since it had not been updated since the begining of the project, which itself had evolved a lot in the meantime. --- .../function_field/drinfeld_modules/action.py | 43 ++++++++----------- 1 file changed, 19 insertions(+), 24 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 82f1ee0d207..bc263efd3eb 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -4,34 +4,29 @@ class DrinfeldModuleAction(Action): - def __init__(self, finite_drinfeld_module): - # Verifications - if not isinstance(finite_drinfeld_module, DrinfeldModule): - raise TypeError('input must be a DrinfeldModule') - # Work - self.__finite_drinfeld_module = finite_drinfeld_module - super().__init__(finite_drinfeld_module.polring(), - finite_drinfeld_module.ore_polring().base_ring()) - - ########### - # Methods # - ########### - - def finite_drinfeld_module(self): - return self.__finite_drinfeld_module - ########################## - # Special Sage functions # - ########################## + def __init__(self, drinfeld_module): + if not isinstance(drinfeld_module, DrinfeldModule): + raise TypeError('input must be a DrinfeldModule') + self._drinfeld_module = drinfeld_module + super().__init__(drinfeld_module.function_ring(), + drinfeld_module.base_ring()) - def _act_(self, g, x): - return self.finite_drinfeld_module()(g)(x) + def _act_(self, pol, x): + if pol not in self._drinfeld_module.function_ring(): + raise TypeError('first input must be in the function ring') + if x not in self._drinfeld_module.base_ring(): + raise TypeError('second input must be in the base ring') + return self._drinfeld_module(pol)(x) def _latex_(self): return f'\\text{{Action{{ }}on{{ }}}}' \ - f'{latex(self.extension())}\\text{{{{ }}' \ - f'induced{{ }}by{{ }}}}{self.finite_drinfeld_module()}' + f'{latex(self._drinfeld_module.base_ring())}\\text{{{{ }}' \ + f'induced{{ }}by{{ }}}}{self._drinfeld_module}' def _repr_(self): - return f'Action on {self.domain()} induced by ' \ - f'{self.finite_drinfeld_module()}' + return f'Action on {self._drinfeld_module.base_ring()} induced by ' \ + f'{self._drinfeld_module}' + + def drinfeld_module(self): + return self._drinfeld_module From 721f3072209d05e89fd3587b9bd0f2b4aee9f5c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 19 Aug 2022 15:16:21 +0200 Subject: [PATCH 084/197] Use super().__init__(...) for Drinfeld modules (esp. DrinfeldModuleMorphism) In commit f8e34f2d2ccea453f0cc98ca0fe6d154b352c10d, I made DrinfeldModuleMorphism inherit UniqueRepresentation, which made me refactor the __init__ method. It turns out that I was doing it wrong, and that, `my_morphism.parent()` would be `None`. I fixed that, and I wrote some tests to detect this in the future. I took the opportunity to use `super()` for other Drinfeld module stuff. --- .../drinfeld_modules/drinfeld_module.py | 2 +- .../rings/function_field/drinfeld_modules/homset.py | 2 +- .../rings/function_field/drinfeld_modules/morphism.py | 11 ++++++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 1f12fb31717..ada0618edc4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -496,7 +496,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): return cls.__classcall__(cls, gen, category) def __init__(self, gen, category): - CategoryObject.__init__(self, category=category) + super().__init__(category=category) self._base_ring = category.base() self._function_ring = category.function_ring() self._gen = gen diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 595a8836df2..72f68d4461b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -18,7 +18,7 @@ def __init__(self, X, Y, category=None, check=True): if category != X.category(): raise NotImplementedError('category should be DrinfeldModules') base = category.base() - Homset.__init__(self, X, Y, category=category, base=base, check=check) + super().__init__(X, Y, category=category, base=base, check=check) def _repr_(self): return f'Set of Drinfeld module morphisms:\n' \ diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index b7792a5a907..b6bae6da67c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -87,6 +87,15 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, sage: morphism.is_isomorphism() False + TESTS:: + + sage: morphism.parent() == Hom(phi, psi) + True + sage: phi.frobenius_endomorphism().parent() == End(phi) + True + sage: End(phi)(0).parent() == End(phi) + True + .. NOTE:: For the sake of completness, we explain how the user can @@ -121,7 +130,7 @@ def __classcall_private__(cls, parent, x): return cls.__classcall__(cls, parent, ore_pol) def __init__(self, parent, ore_pol): - Element.__init__(Element(parent), parent) + super().__init__(parent) self._domain = parent.domain() self._codomain = parent.codomain() self._ore_polynomial = ore_pol From 919cf5ed1360e5f65ade295ac4326d4066fb4a94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 19 Aug 2022 16:37:51 +0200 Subject: [PATCH 085/197] Create _latex_ method in DrinfeldModuleHomset --- src/sage/rings/function_field/drinfeld_modules/homset.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 72f68d4461b..e4fd38e7177 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -20,6 +20,11 @@ def __init__(self, X, Y, category=None, check=True): base = category.base() super().__init__(X, Y, category=category, base=base, check=check) + def _latex_(self): + return f'\\text{{Set{{ }}of{{ }}Drinfeld{{ }}module{{ }}morphisms' \ + f'{{ }}from}}{latex(self.domain())}\\text{{{{ }}to{{ }}}}' \ + f'{self.codomain()}' + def _repr_(self): return f'Set of Drinfeld module morphisms:\n' \ f' From: {self.domain()}\n' \ From 70c215aec33c26379d45000b9d65a76645ab8af7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 19 Aug 2022 16:39:39 +0200 Subject: [PATCH 086/197] Write docstrings for DrinfeldModuleHomset --- .../function_field/drinfeld_modules/homset.py | 114 +++++++++++++++++- 1 file changed, 112 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index e4fd38e7177..7229df993a1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -1,9 +1,119 @@ -from sage.structure.parent import Parent +r""" +Set of morphisms between two Drinfeld modules + +This module provides the class +:class:`sage.rings.function_field.drinfeld_module.homset.DrinfeldModuleHomset`. + +AUTHORS: + +- Antoine Leudière (2022-04) +""" + +#***************************************************************************** +# Copyright (C) 2022 Antoine Leudière +# +# 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. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from sage.categories.drinfeld_modules import DrinfeldModules -from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism from sage.categories.homset import Homset, Hom +from sage.misc.latex import latex +from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism +from sage.structure.parent import Parent class DrinfeldModuleHomset(Homset): + r""" + This class represents the set of morphisms between two Drinfeld + modules. + + INPUT: + + - ``X`` -- the domain + - ``Y`` -- the codomain + + EXAMPLES: + + sage: Fq = GF(27) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) + sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: hom = Hom(phi, psi) + sage: hom + Set of Drinfeld module morphisms: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 + + sage: from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset + sage: isinstance(hom, DrinfeldModuleHomset) + True + + There is a simpler syntax for endomorphisms sets:: + + sage: end = End(phi) + sage: end + Set of Drinfeld module morphisms: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + sage: end is Hom(phi, phi) + True + + + One can create morphism objects by calling the homset:: + + sage: t = phi.ore_variable() + sage: identity_morphism = end(1) + sage: identity_morphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + Defn: 1 + + sage: frobenius_endomorphism = end(t^6) + sage: frobenius_endomorphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + Defn: t^6 + + sage: isogeny = hom(t + 1) + sage: isogeny + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 + Defn: t + 1 + + And one can test if an Ore polynomial defines a morphism using the + ``in`` syntax:: + + sage: 1 in hom + False + sage: t^6 in hom + False + sage: t + 1 in hom + True + sage: 1 in end + True + sage: t^6 in end + True + sage: t + 1 in end + False + + This also works if the candidate is a morphism object:: + + sage: isogeny in hom + True + sage: end(0) in end + True + sage: identity_morphism in hom + False + sage: frobenius_endomorphism in hom + False + """ Element = DrinfeldModuleMorphism __contains__ = Parent.__contains__ From c9bfc054383b828e118fb79a4ee5cae60535d7d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 19 Aug 2022 16:37:06 +0200 Subject: [PATCH 087/197] (minor) Delete a blank line in DrinfeldModules --- src/sage/categories/drinfeld_modules.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index b4cd81bf3ef..5898e71ba5f 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -96,7 +96,6 @@ def random_element(self, rank): return self(coeffs) - # Sage methods def _call_(self, gen): From 394d0f5edd4941727f6420b8c76081bdad5269cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 19 Aug 2022 16:40:17 +0200 Subject: [PATCH 088/197] (minor) Change a sentence in DrinfeldModuleMorphism --- src/sage/rings/function_field/drinfeld_modules/morphism.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index b6bae6da67c..0f836451f84 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -99,7 +99,8 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, .. NOTE:: For the sake of completness, we explain how the user can - directly instanciate the class:: + directly instanciate the class, even though this should never be + explicitely done:: sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) From 0f869f2345c2e071023dc5a7832b1776e1ed71be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 19 Aug 2022 17:03:27 +0200 Subject: [PATCH 089/197] (minor) Change first sentences of Drinfeld module classes --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- .../function_field/drinfeld_modules/finite_drinfeld_module.py | 2 +- src/sage/rings/function_field/drinfeld_modules/morphism.py | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index ada0618edc4..82d8e5f8837 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -39,7 +39,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): r""" - This class handles Drinfeld modules. + This class represents a Drinfeld module. Let `\Fq` be a finite field with order `q`. Let `K` be a field equiped a ring morphism `\gamma: \Fq[X] \to K` --- the field `K` is diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 68921a0f508..40563bd83d4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -26,7 +26,7 @@ class FiniteDrinfeldModule(DrinfeldModule): r""" - This class handles finite Drinfeld modules. + This class represnets a finite Drinfeld module. A *finite Drinfeld module* is a Drinfeld module whose base ring is finite. For general definitions and help on Drinfeld modules, see diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 0f836451f84..80625539d7d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -30,6 +30,8 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, metaclass=InheritComparisonClasscallMetaclass): r""" + This class represents a Drinfeld module morphism. + Let `\phi,\psi` be two Drinfeld modules defined over the `\Fq[X]`-field `K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a From 14f98aa4effbbea9a1e1f2e72aee31bc12988085 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 19 Aug 2022 17:04:54 +0200 Subject: [PATCH 090/197] Change _get_action_ to action in DrinfeldModule See https://trac.sagemath.org/ticket/33713 for a discussion on this topic. Representations are very unusual in this context; this implementation seems more appropriate. I added documentation for this method in DrinfeldModule main docstring. NOTE: The commit is not very clean as I did some cleaning in the docstring too. --- .../drinfeld_modules/drinfeld_module.py | 66 ++++++++----------- 1 file changed, 27 insertions(+), 39 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 82d8e5f8837..217718e585f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -393,41 +393,29 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: The action of a Drinfeld module - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - TODO - - .. RUBRIC:: Other methods - - It is possible to change the base ring:: - - sage: L = K.extension(2) - sage: phi_rebased = phi.change_ring(L) - sage: Ltau = phi_rebased.ore_polring() - sage: Ltau(phi(X)) == phi_rebased(X) - True + An `\Fq[X]`-Drinfeld module `\phi` notoriously makes any field + extension `L/K` a left `\Fq[X]`-module with the law defined by + `(P(X), a) \mapsto \phi_P(a)`, where `a \in L`. The method + :meth:`action` returns an ``Action`` object representing the + Drinfeld module action; in this implementation, `K = L`. + + sage: action = phi.action() + sage: action + Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + + The action is computed as follows:: + + sage: P = X + 1 + sage: a = z + sage: action(P, a) + ... + z^9 + 2*z^8 + 2*z^7 + 2*z^6 + 2*z^3 + z^2 + sage: action(0, K.random_element()) + 0 + sage: action(FqX.random_element(), 0) + 0 + + .. RUBRIC:: Inversion of the Drinfeld module Given an Ore polynomial that equals `\phi_a` for some `a \in \Fq[X]`, one can retrieve `a` (as a morphism, a Drinfeld @@ -508,10 +496,6 @@ def __init__(self, gen, category): # Special Sage functions # ########################## - def _get_action_(self): - from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction - return DrinfeldModuleAction(self) - def _latex_(self): return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ f'{latex(self._function_ring.gen())} '\ @@ -526,6 +510,10 @@ def _repr_(self): # Getters # ########### + def action(self): + from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction + return DrinfeldModuleAction(self) + def base_ring(self): r""" Return the base ring of the Drinfeld module. From b014a199f9c25b30234d672cc9ca8e2e9292ba25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 19 Aug 2022 17:19:56 +0200 Subject: [PATCH 091/197] Reorder methods in DrinfeldModule --- .../drinfeld_modules/drinfeld_module.py | 548 +++++++++--------- 1 file changed, 268 insertions(+), 280 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 217718e585f..c1c679d0257 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -492,9 +492,67 @@ def __init__(self, gen, category): self._ore_polring = gen.parent() self._Fq = self._function_ring.base_ring() # Must be last - ########################## - # Special Sage functions # - ########################## + def __call__(self, a): + r""" + Return the image of ``a`` by the morphism that defines the + Drinfeld module, i.e. `\phi_a` if the Drinfeld module is denoted + `phi`. + + INPUT: + + - ``a`` -- an element in the function ring + + OUTPUT: + + - an element of the base ring + """ + + return self._morphism(a) + + def _Hom_(self, other, category): + r""" + Return ``DrinfeldModuleHomset(self, other, category)``. + + Validity of the input is checked at the instantiation of + ``DrinfeldModuleHomset``. ``self`` and ``other`` only need be in + the same category. + + INPUT: + + - ``other`` -- the codomain of the homset + - ``category`` -- the category in which we consider the + morphisms, usually `self.category()` + + OUTPUT: + + - an homset + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: t = phi.ore_variable() + sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + sage: psi = phi.velu(isog) + sage: hom = phi._Hom_(psi, category=phi.category()) + sage: hom is Hom(phi, psi) # known bug + True + sage: from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset + sage: isinstance(hom, DrinfeldModuleHomset) + True + """ + from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset + return DrinfeldModuleHomset(self, other, category) + + def _check_rank_two(self): + r""" + Raise ``NotImplementedError`` if the rank is not two. + """ + if self.rank() != 2: + raise NotImplementedError('rank must be 2') def _latex_(self): return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ @@ -506,10 +564,6 @@ def _repr_(self): return f'Drinfeld module defined by {self._function_ring.gen()} ' \ f'|--> {self._gen} over {self._base_ring}' - ########### - # Getters # - ########### - def action(self): from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction return DrinfeldModuleAction(self) @@ -571,6 +625,76 @@ def base_ring(self): """ return self._base_ring + def change_ring(self, new_field, name=None): + r""" + Return a Drinfeld module defined like the current one, but with + base ring ``new_field``. + + The new base can either be a field extension of the base ring, + or field that has a coercion map from the field of definitions + of the coefficients of the generator. + + INPUT: + + - ``new_field`` -- the field extension of the base ring that + serves as base ring for the new Drinfeld module + + OUTPUT: + + - a Drinfeld module + + EXAMPLES: + + The new ring can be an extension of the base ring:: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, p_root^3, 2]) + sage: K2 = K.extension(2) + sage: phi_2 = phi.change_ring(K2) + sage: phi_2 + Drinfeld module defined by X |--> 2*t^2 + (3*z24^23 + 2*z24^22 + 2*z24^20 + z24^19 + 4*z24^18 + 3*z24^17 + 4*z24^15 + 2*z24^13 + 4*z24^12 + 4*z24^11 + 3*z24^10 + 3*z24^9 + 4*z24^8 + 4*z24^6 + 3*z24^5 + 4*z24^4 + 4*z24^3 + 2*z24)*t + 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 over Finite Field in z24 of size 5^24 + + And one can check various things:: + + sage: phi.change_ring(K2).change_ring(K) is phi + True + sage: phi_2.base_ring() is K2 + True + + Naturally, the category has changed:: + + sage: phi_2.category() + Category of Drinfeld modules defined by Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z24 of size 5^24 + Defn: X |--> 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 + + One can also change the base ring to a subfield, even though some things + do not work as expected:: + + sage: K0 = Fq.extension(2) + sage: phi_0 = phi.change_ring(K0) + sage: phi_0.base_ring() is K0 + True + sage: phi.change_ring(K0).change_ring(K) # known bug + Traceback (most recent call last) + ... + TypeError: no coercion defined + + Furthermore:: + + sage: phi.change_ring(K) is phi + True + """ + coeffs = self._gen.coefficients() + new_coeffs = list(map(new_field, coeffs)) + if name == None: + name = self._ore_polring.variable_name() + return DrinfeldModule(self._function_ring, new_coeffs, name=name) + def constant_coefficient(self): r""" Return the constant coefficient of the generator (`\phi_X`). @@ -625,157 +749,6 @@ def constant_coefficient(self): """ return self.gen()[0] - def gen(self): - r""" - Return the generator of the Drinfeld module, i.e. `\phi_X`. - - This method makes sense because, in our case, the function ring - `\Fq[X]`, which is `\Fq`-generated by a single element element, - whose image characterizes the Drinfeld module. - - OUTPUT: - - - an Ore polynomial - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.gen() == phi(X) - True - """ - return self._gen - - def morphism(self): - r""" - Return the morphism object that defines the Drinfeld module. - - OUTPUT: - - - a ring morphism, from the function ring to the Ore polynomial - ring - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.morphism() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) - Defn: X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: from sage.rings.morphism import RingHomomorphism - sage: isinstance(phi.morphism(), RingHomomorphism) - True - - Actually, the ``DrinfeldModule`` method ``__call__`` simply - class the ``__call__`` method of this morphism:: - - sage: phi.morphism()(X) == phi(X) - True - sage: a = FqX.random_element() - sage: phi.morphism()(a) == phi(a) - True - - And many methods of the Drinfeld module have a counterpart in - the morphism object:: - - sage: m = phi.morphism() - sage: m.domain() is phi.function_ring() - True - sage: m.codomain() is phi.ore_polring() - True - sage: m.im_gens() - [z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12] - sage: phi(X) == m.im_gens()[0] - True - """ - return self._morphism - - def ore_polring(self): - r""" - Return the Ore polynomial ring of the Drinfeld module. - - OUTPUT: - - - an Ore polynomial ring - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: ore_polring = phi.ore_polring() - sage: ore_polring - Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) - - The Ore polynomial ring can also be retrieved from the category - of the Drinfeld module:: - - sage: ore_polring is phi.category().ore_polring() - True - - The generator of the Drinfeld module is in the Ore polynomial - ring:: - - sage: phi(X) in ore_polring - True - - The Ore variable is just the generator of the Ore polynomial - ring:: - - sage: ore_polring.gen() - t - sage: phi.ore_variable() is ore_polring.gen() - True - """ - return self._ore_polring - - def ore_variable(self): - r""" - Return the Ore variable. - - The Ore variable is defined as the generator of the Ore - polynomial ring. - - OUTPUT: - - - an Ore polynomial - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.ore_variable() - t - sage: phi.ore_variable() is phi.ore_polring().gen() - True - - One can use the Ore variable to instanciate new Drinfeld - modules...:: - - sage: t = phi.ore_variable() - sage: psi_X = phi.constant_coefficient() + 3*t + 2*t^4 - sage: psi = DrinfeldModule(FqX, psi_X) - - ...or morphisms and isogenies:: - - sage: t^6 in End(phi) # Frobenius endomorphism - True - """ - return self._ore_polring.gen() - def function_ring(self): r""" Return the function ring of the Drinfeld module. @@ -798,96 +771,29 @@ def function_ring(self): """ return self._function_ring - ########### - # Methods # - ########### - - def __call__(self, a): - r""" - Return the image of ``a`` by the morphism that defines the - Drinfeld module, i.e. `\phi_a` if the Drinfeld module is denoted - `phi`. - - INPUT: - - - ``a`` -- an element in the function ring - - OUTPUT: - - - an element of the base ring - """ - - return self._morphism(a) - - def change_ring(self, new_field, name=None): + def gen(self): r""" - Return a Drinfeld module defined like the current one, but with - base ring ``new_field``. - - The new base can either be a field extension of the base ring, - or field that has a coercion map from the field of definitions - of the coefficients of the generator. - - INPUT: - - - ``new_field`` -- the field extension of the base ring that - serves as base ring for the new Drinfeld module + Return the generator of the Drinfeld module, i.e. `\phi_X`. + This method makes sense because, in our case, the function ring + `\Fq[X]`, which is `\Fq`-generated by a single element element, + whose image characterizes the Drinfeld module. + OUTPUT: - - a Drinfeld module + - an Ore polynomial EXAMPLES: - The new ring can be an extension of the base ring:: - sage: Fq = GF(25) sage: FqX. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, p_root^3, 2]) - sage: K2 = K.extension(2) - sage: phi_2 = phi.change_ring(K2) - sage: phi_2 - Drinfeld module defined by X |--> 2*t^2 + (3*z24^23 + 2*z24^22 + 2*z24^20 + z24^19 + 4*z24^18 + 3*z24^17 + 4*z24^15 + 2*z24^13 + 4*z24^12 + 4*z24^11 + 3*z24^10 + 3*z24^9 + 4*z24^8 + 4*z24^6 + 3*z24^5 + 4*z24^4 + 4*z24^3 + 2*z24)*t + 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 over Finite Field in z24 of size 5^24 - - And one can check various things:: - - sage: phi.change_ring(K2).change_ring(K) is phi - True - sage: phi_2.base_ring() is K2 - True - - Naturally, the category has changed:: - - sage: phi_2.category() - Category of Drinfeld modules defined by Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z24 of size 5^24 - Defn: X |--> 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 - - One can also change the base ring to a subfield, even though some things - do not work as expected:: - - sage: K0 = Fq.extension(2) - sage: phi_0 = phi.change_ring(K0) - sage: phi_0.base_ring() is K0 - True - sage: phi.change_ring(K0).change_ring(K) # known bug - Traceback (most recent call last) - ... - TypeError: no coercion defined - - Furthermore:: - - sage: phi.change_ring(K) is phi + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.gen() == phi(X) True """ - coeffs = self._gen.coefficients() - new_coeffs = list(map(new_field, coeffs)) - if name == None: - name = self._ore_polring.variable_name() - return DrinfeldModule(self._function_ring, new_coeffs, name=name) + return self._gen def height(self): r""" @@ -1078,6 +984,133 @@ def j(self): q = self._Fq.order() return (g**(q+1)) / delta + def morphism(self): + r""" + Return the morphism object that defines the Drinfeld module. + + OUTPUT: + + - a ring morphism, from the function ring to the Ore polynomial + ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + Defn: X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: from sage.rings.morphism import RingHomomorphism + sage: isinstance(phi.morphism(), RingHomomorphism) + True + + Actually, the ``DrinfeldModule`` method ``__call__`` simply + class the ``__call__`` method of this morphism:: + + sage: phi.morphism()(X) == phi(X) + True + sage: a = FqX.random_element() + sage: phi.morphism()(a) == phi(a) + True + + And many methods of the Drinfeld module have a counterpart in + the morphism object:: + + sage: m = phi.morphism() + sage: m.domain() is phi.function_ring() + True + sage: m.codomain() is phi.ore_polring() + True + sage: m.im_gens() + [z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12] + sage: phi(X) == m.im_gens()[0] + True + """ + return self._morphism + + def ore_polring(self): + r""" + Return the Ore polynomial ring of the Drinfeld module. + + OUTPUT: + + - an Ore polynomial ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: ore_polring = phi.ore_polring() + sage: ore_polring + Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + + The Ore polynomial ring can also be retrieved from the category + of the Drinfeld module:: + + sage: ore_polring is phi.category().ore_polring() + True + + The generator of the Drinfeld module is in the Ore polynomial + ring:: + + sage: phi(X) in ore_polring + True + + The Ore variable is just the generator of the Ore polynomial + ring:: + + sage: ore_polring.gen() + t + sage: phi.ore_variable() is ore_polring.gen() + True + """ + return self._ore_polring + + def ore_variable(self): + r""" + Return the Ore variable. + + The Ore variable is defined as the generator of the Ore + polynomial ring. + + OUTPUT: + + - an Ore polynomial + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.ore_variable() + t + sage: phi.ore_variable() is phi.ore_polring().gen() + True + + One can use the Ore variable to instanciate new Drinfeld + modules...:: + + sage: t = phi.ore_variable() + sage: psi_X = phi.constant_coefficient() + 3*t + 2*t^4 + sage: psi = DrinfeldModule(FqX, psi_X) + + ...or morphisms and isogenies:: + + sage: t^6 in End(phi) # Frobenius endomorphism + True + """ + return self._ore_polring.gen() + def rank(self): r""" Return the rank of the Drinfeld module. @@ -1181,48 +1214,3 @@ def velu(self, isog): return None quo, rem = (isog * self.gen()).right_quo_rem(isog) return None if rem != 0 else DrinfeldModule(self._function_ring, quo) - - def _Hom_(self, other, category): - r""" - Return ``DrinfeldModuleHomset(self, other, category)``. - - Validity of the input is checked at the instantiation of - ``DrinfeldModuleHomset``. ``self`` and ``other`` only need be in - the same category. - - INPUT: - - - ``other`` -- the codomain of the homset - - ``category`` -- the category in which we consider the - morphisms, usually `self.category()` - - OUTPUT: - - - an homset - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: t = phi.ore_variable() - sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 - sage: psi = phi.velu(isog) - sage: hom = phi._Hom_(psi, category=phi.category()) - sage: hom is Hom(phi, psi) # known bug - True - sage: from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset - sage: isinstance(hom, DrinfeldModuleHomset) - True - """ - from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset - return DrinfeldModuleHomset(self, other, category) - - def _check_rank_two(self): - r""" - Raise ``NotImplementedError`` if the rank is not two. - """ - if self.rank() != 2: - raise NotImplementedError('rank must be 2') From 138902d263590c46d931ed79db753fd99b6b8beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 11:27:03 +0200 Subject: [PATCH 092/197] Write docstrings for DrinfeldModuleAction --- .../function_field/drinfeld_modules/action.py | 82 +++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index bc263efd3eb..1901a07d371 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -1,9 +1,78 @@ +r""" +The left-module action induced by a Drinfeld module + +This module provides the class +:class:`sage.rings.function_field.drinfeld_module.action.DrinfeldModuleAction`. + +AUTHORS: + +- Antoine Leudière (2022-04) +""" + +#***************************************************************************** +# Copyright (C) 2022 Antoine Leudière +# +# 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. +# http://www.gnu.org/licenses/ +#***************************************************************************** + from sage.categories.action import Action from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule class DrinfeldModuleAction(Action): + r""" + This class represents the left `\Fq[X]`-module action induced by a + Drinfeld `\Fq[X]`-module defined over an `\Fq[X]`-field `K`. + + Let `L/K` be a field extension, let `x \in L`, let `P \in \Fq[X]`; + the action is defined as `(P, a) \mapsto \phi_P(a)`, where + `\phi_P(a)`. In this implementation, `L` is `K`. + + The action is instanciated as follows. Note that the user should + never explicitely instanciate the class `DrinfeldModuleAction`:: + + INPUT: a Drinfeld module + + EXAMPLES: + + sage: Fq. = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: action = phi.action() + sage: action + Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 11^2 + + The action on elements is computed as follows:: + + sage: P = X + 1 + sage: a = z + sage: action(P, a) + ... + 4*z + 2 + sage: action(0, K.random_element()) + 0 + sage: action(FqX.random_element(), 0) + 0 + + To act on a field larger than `K`, one can change the ring of the + Drinfeld module, then create the action:: + + sage: extended_action = phi.change_ring(K.extension(2)).action() + sage: extended_action + Action on Finite Field in z4 of size 11^4 induced by Drinfeld module defined by X |--> t + 10*z4^3 + 4*z4^2 + 5*z4 + 5 over Finite Field in z4 of size 11^4 + + Finally, given a Drinfeld module action, it is easy to recover the + corresponding Drinfeld module:: + + sage: action.drinfeld_module() is phi + True + """ def __init__(self, drinfeld_module): if not isinstance(drinfeld_module, DrinfeldModule): @@ -29,4 +98,17 @@ def _repr_(self): f'{self._drinfeld_module}' def drinfeld_module(self): + r""" + Return the Drinfeld module associated to the action. + + OUTPUT: a Drinfeld module + + sage: Fq. = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: action = phi.action() + sage: action.drinfeld_module() is phi + True + """ return self._drinfeld_module From de5e0380446a4c265026e23699357f2fac55b23e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 13:53:55 +0200 Subject: [PATCH 093/197] Rename random_element method to random_object in DrinfeldModules See https://trac.sagemath.org/ticket/33713#comment:90. --- src/sage/categories/drinfeld_modules.py | 3 +-- .../function_field/drinfeld_modules/drinfeld_module.py | 10 +++++----- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 5898e71ba5f..8ae76b33e8b 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -78,8 +78,7 @@ def morphism(self): def ore_polring(self): return self._ore_polring - def random_element(self, rank): - + def random_object(self, rank): if not isinstance(rank, Integer): raise TypeError('rank must be a positive integer') if rank <= 0: diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index c1c679d0257..c4b0248c4e1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -869,19 +869,19 @@ def invert(self, ore_pol): sage: a = FqX.random_element() sage: cat = phi.category() - sage: phi_r1 = cat.random_element(1) + sage: phi_r1 = cat.random_object(1) sage: phi_r1.invert(phi_r1(a)) == a True - sage: phi_r2 = cat.random_element(2) + sage: phi_r2 = cat.random_object(2) sage: phi_r2.invert(phi_r2(a)) == a True - sage: phi_r3 = cat.random_element(3) + sage: phi_r3 = cat.random_object(3) sage: phi_r3.invert(phi_r3(a)) == a True - sage: phi_r4 = cat.random_element(4) + sage: phi_r4 = cat.random_object(4) sage: phi_r4.invert(phi_r4(a)) == a True - sage: phi_r5 = cat.random_element(5) + sage: phi_r5 = cat.random_object(5) sage: phi_r5.invert(phi_r5(a)) == a True """ From e50a5caab8c6dcfacc1c1a96338f28782c677457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 14:32:14 +0200 Subject: [PATCH 094/197] Handle some zero Fq[X]-characteristic special cases in DrinfeldModules The code is a simple two-liner `if` in `DrinfeldModules.__init__`. --- src/sage/categories/drinfeld_modules.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 8ae76b33e8b..1104c288375 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -57,6 +57,9 @@ def __init__(self, morphism, name='t'): f = gamma * FqX.coerce_map_from(Fq) # Fq -> K E = K.over(f) self._characteristic = FqX(E(gamma(X)).minpoly()) + elif FqX.is_subring(K): + self._characteristic = Integer(0) + def base(self): return self._ore_polring.base_ring() From f5aa48906702e0a42c01c63374072e5a9874861d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 15:22:32 +0200 Subject: [PATCH 095/197] Create _base attribute in DrinfeldModules This is more in line with the rest of the code. --- src/sage/categories/drinfeld_modules.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 1104c288375..b53db6023b1 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -42,6 +42,7 @@ def __init__(self, morphism, name='t'): X = FqX.gen() # Check codomain of gamma is field K = gamma.codomain() + self._base = K if not K.is_field(): raise TypeError('base must be a field') # Build K{t} @@ -62,7 +63,7 @@ def __init__(self, morphism, name='t'): def base(self): - return self._ore_polring.base_ring() + return self._base def characteristic(self): if self._characteristic is None: @@ -87,7 +88,7 @@ def random_object(self, rank): if rank <= 0: raise ValueError('rank must be a positive integer') - K = self.base() + K = self._base coeffs = [self._constant_coefficient] for _ in range(rank-1): coeffs.append(K.random_element()) From 386efec427689989407aae7f788ad4f0d05b6cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 15:23:49 +0200 Subject: [PATCH 096/197] Write docstrings in DrinfeldModules I also did a bit of cleaning and refactoring there and there, but it is only cosmetic changes (order of methods, some comments, etc); nothing important or meaningful. --- src/sage/categories/drinfeld_modules.py | 371 ++++++++++++++++++++++-- 1 file changed, 340 insertions(+), 31 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index b53db6023b1..98ccb24152a 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -1,6 +1,15 @@ r""" -Drinfeld modules +Category of Drinfeld modules + +This module provides the class +:class:`sage.category.drinfeld_modules.DrinfeldModules`. + +AUTHORS: + +- Antoine Leudière (2022-04) +- Xavier Caruso (2022-06) """ + #***************************************************************************** # Copyright (C) 2022 Xavier Caruso # Antoine Leudière @@ -10,18 +19,134 @@ #****************************************************************************** from sage.categories.category import CategoryWithParameters +from sage.categories.homsets import Homsets from sage.misc.functional import log from sage.rings.integer import Integer from sage.rings.morphism import RingHomomorphism -from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing +from sage.rings.polynomial.polynomial_ring import PolynomialRing_general -from sage.categories.homsets import Homsets - -# from sage.misc.cachefunc import cached_method -# from sage.categories.basic import Fields class DrinfeldModules(CategoryWithParameters): + r""" + This class represents the category of Drinfeld modules on a given + `\Fq[X]`-field `K`. + + The `\Fq[X]`-field structure on `K` is given by a ring morphism + `\gamma: \Fq[X] \to K`. + + We say that `\Fq[X]` is the *function ring of the category*; `K` is the + *base of the category*, or simply its base ring or base field; `\Fq[X]` is + the *function ring of the category*; *K\{\tau\}* is the *Ore + polynomial ring of the category*; + `t` is the *Ore variable of the category*. The *constant coefficient + of the category* is `\gamma(X)`. + + INPUT: a ring morphism `\Fq[X] \to K` + + EXAMPLES: + + Generally, Drinfeld modules objects are created before their + category, and the category is retrieved as an attribute of the + Drinfeld module:: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat + Category of Drinfeld modules defined by Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^4 + Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + + The output tells the user that the category is only defined by the + ring morphism `\gamma`. + + .. RUBRIC:: Properties of the category + + The defining morphism is retrieved using the method + :meth:`morphism`:: + + sage: cat.morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^4 + Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + + The so-called *constant coefficient* --- which is the same for all + Drinfeld modules in the category --- is simply the image of `X` by + this morphism: + + sage: cat.constant_coefficient() + z^3 + 7*z^2 + 6*z + 10 + sage: cat.morphism()(X) == cat.constant_coefficient() + True + + Similarly, the *`\Fq[X]`-characteristic* of the category is either + `0` or the unique monic polynomial in `\Fq[X]` that generates + `\mathrm{Ker}(\gamma)`:: + + sage: cat.characteristic() + X^2 + 7*X + 2 + sage: cat.morphism()(cat.characteristic()) + 0 + + Like for its Drinfeld modules, the *base* of the category is the + field `K`:: + + sage: cat.base() + Finite Field in z of size 11^4 + sage: cat.base() is K + True + + And the *function ring* is the polynomial ring `\Fq[X]`:: + + sage: cat.function_ring() + Univariate Polynomial Ring in X over Finite Field of size 11 + sage: cat.function_ring() is FqX + True + + And as expected, the *Ore polynomial ring* is that of + the Drinfeld modules in the category: + + sage: cat.ore_polring() + Ore Polynomial Ring in t over Finite Field in z of size 11^4 twisted by z |--> z^11 + sage: cat.ore_polring() is phi.ore_polring() + True + + .. RUBRIC:: Creating Drinfeld module objects from the category + + Calling the category with an Ore polynomial creates a Drinfeld + module object in the category whose generator is the input:: + + sage: psi = cat([p_root, 1]) + sage: psi + Drinfeld module defined by X |--> t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + sage: psi.category() is cat + True + + Of course, the constant coefficient of the input must be the same as + the category':: + + sage: cat([z, 1]) + Traceback (most recent call last): + ... + ValueError: constant coefficient must be a root of the characteristic + + It is also possible to create a random object in the category, with + a given rank:: + + sage: rho = cat.random_object(2) + sage: rho # random + Drinfeld module defined by X |--> (7*z^3 + 7*z^2 + 10*z + 2)*t^2 + (9*z^3 + 5*z^2 + 2*z + 7)*t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + sage: rho.rank() == 2 + True + sage: rho.category() is cat + True + """ def __init__(self, morphism, name='t'): gamma = morphism @@ -61,28 +186,234 @@ def __init__(self, morphism, name='t'): elif FqX.is_subring(K): self._characteristic = Integer(0) + def _call_(self, gen): + r""" + Return a Drinfeld module object, in the category, whose + generator is the input. + + INPUT: the generator of the Drinfeld module, given as an Ore + polynomial or a list of coefficients + + OUTPUT: a Drinfeld module in the category + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: psi = cat([p_root, 0, 1]) + sage: psi + Drinfeld module defined by X |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + sage: t = phi.ore_variable() + sage: cat(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi + True + """ + from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule + # If gen is not in the Ore polring, an exception is raised + gen = self._ore_polring(gen) + if self.characteristic()(gen[0]) != 0: + raise ValueError('constant coefficient must be a root of the characteristic') + return DrinfeldModule(self._function_ring, gen) + + # Somehow required for the class definition + def _make_named_class_key(self, name): + return self._function_ring.category() + + def _repr_(self): + return f'Category of Drinfeld modules defined by {self._morphism}' + + # Somehow required for the class definition + def Homsets(self): + return Homsets() + + # Somehow required for the class definition + def Endsets(self): + return Homsets() def base(self): + r""" + Return the base of the category. + + OUTPUT: a ring + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.base() + Finite Field in z of size 11^4 + sage: cat.base() is K + True + """ return self._base def characteristic(self): + r""" + Return the `\Fq[X]`-characteristic of the category. + + OUTPUT: `0` or a monic prime polynomial in `\Fq[X]` + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.characteristic() + X^2 + 7*X + 2 + + sage: L = Frac(FqX) + sage: psi = DrinfeldModule(FqX, [L.gen(), 1]) + sage: psi + Drinfeld module defined by X |--> t + X over Fraction Field of Univariate Polynomial Ring in X over Finite Field of size 11 + sage: fox = psi.category() + sage: fox.characteristic() + 0 + """ if self._characteristic is None: raise NotImplementedError return self._characteristic def constant_coefficient(self): + r""" + Return the constant coefficient of the category. + + OUTPUT: an element in the base + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.constant_coefficient() + z^3 + 7*z^2 + 6*z + 10 + sage: cat.constant_coefficient() == cat.morphism()(X) + True + """ return self._constant_coefficient def function_ring(self): + r""" + Return the function ring of the category. + + OUTPUT: the ring `\Fq[X]` + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.function_ring() + Univariate Polynomial Ring in X over Finite Field of size 11 + sage: cat.function_ring() is FqX + True + """ return self._function_ring def morphism(self): + r""" + Return the morphism that defines the category. + + OUTPUT: a ring morphism + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: gamma = cat.morphism() + sage: gamma + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^4 + Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + sage: gamma(X) == cat.constant_coefficient() + True + """ return self._morphism def ore_polring(self): + r""" + Return the Ore polynomial ring of the category. + + OUTPUT: the Ore polynomial ring `K\{\tau\}` + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.ore_polring() + Ore Polynomial Ring in t over Finite Field in z of size 11^4 twisted by z |--> z^11 + sage: cat.ore_polring() is phi.ore_polring() + True + """ return self._ore_polring + def ore_variable(self): + r""" + Return the Ore variable of the category. + + OUTPUT: the generator of the Ore polynomial ring + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.ore_variable() + t + sage: cat.ore_variable() is phi.ore_variable() + True + """ + return self._ore_polring.gen() + def random_object(self, rank): + r""" + Return a random Drinfeld module in the category, whose rank is + the input. + + INPUT: an integer, the rank of the Drinfeld module + + OUTPUT: a Drinfeld module in the category + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: psi = cat.random_object(3) # random + Drinfeld module defined by X |--> (6*z^3 + 4*z^2 + 10*z + 9)*t^3 + (4*z^3 + 8*z^2 + 8*z)*t^2 + (10*z^3 + 3*z^2 + 6*z)*t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + sage: psi.rank() == 3 + True + """ if not isinstance(rank, Integer): raise TypeError('rank must be a positive integer') if rank <= 0: @@ -99,38 +430,16 @@ def random_object(self, rank): return self(coeffs) - # Sage methods - - def _call_(self, gen): - # Avoid circular import - from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule - # If gen is not in the codomain, an exception is raised - gen = self._ore_polring(gen) - if self.characteristic()(gen[0]) != 0: - raise ValueError('constant coefficient must be a root of the characteristic') - return DrinfeldModule(self._function_ring, gen) - - def _repr_(self): - return f'Category of Drinfeld modules defined by {self._morphism}' - - # Sage requires those: - - def _make_named_class_key(self, name): - return self._function_ring.category() - + # Somehow required for the class definition def super_categories(self): return [] - def Homsets(self): - return Homsets() - - def Endsets(self): - return Homsets() - + # Somehow required for the class definition class ParentMethods: def characteristic(self): return self.category().characteristic() + # Somehow required for the class definition class ElementMethods: pass From e80ed4b20d381b60171dc22548a355e7869fc0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 16:08:14 +0200 Subject: [PATCH 097/197] Make OUTPUT: fields oneliners in Drinfeld modules classes See https://trac.sagemath.org/ticket/33713#comment:87. --- .../drinfeld_modules/drinfeld_module.py | 68 +++++-------------- .../finite_drinfeld_module.py | 24 ++----- 2 files changed, 24 insertions(+), 68 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index c4b0248c4e1..f1480b9fd5b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -502,9 +502,7 @@ def __call__(self, a): - ``a`` -- an element in the function ring - OUTPUT: - - - an element of the base ring + OUTPUT: an element of the base ring """ return self._morphism(a) @@ -523,9 +521,7 @@ def _Hom_(self, other, category): - ``category`` -- the category in which we consider the morphisms, usually `self.category()` - OUTPUT: - - - an homset + OUTPUT: an homset EXAMPLES: @@ -575,9 +571,7 @@ def base_ring(self): This is the Ore polynomial ring base. In particular, the base ring is always a field. - OUTPUT: - - - a field + OUTPUT: a field EXAMPLES: @@ -639,9 +633,7 @@ def change_ring(self, new_field, name=None): - ``new_field`` -- the field extension of the base ring that serves as base ring for the new Drinfeld module - OUTPUT: - - - a Drinfeld module + OUTPUT: a Drinfeld module EXAMPLES: @@ -703,9 +695,7 @@ def constant_coefficient(self): `\gamma(X)`. Hence, it is a root of the `\Fq[X]`-characteristic of the base ring. - OUTPUT: - - - an element in the base ring + OUTPUT: an element in the base ring EXAMPLES: @@ -755,9 +745,7 @@ def function_ring(self): In our case, the function ring is an `\Fq[X]`. - OUTPUT: - - - a polynomial ring + OUTPUT: a polynomial ring EXAMPLES: @@ -779,9 +767,7 @@ def gen(self): `\Fq[X]`, which is `\Fq`-generated by a single element element, whose image characterizes the Drinfeld module. - OUTPUT: - - - an Ore polynomial + OUTPUT: an Ore polynomial EXAMPLES: @@ -801,9 +787,7 @@ def height(self): In our case, the height is always 1. - OUTPUT: - - - an integer + OUTPUT: an integer EXAMPLES: @@ -828,9 +812,7 @@ def invert(self, ore_pol): - ``ore_pol`` -- the Ore polynomial whose preimage we want to compute - OUTPUT: - - - a polynomial + OUTPUT: a polynomial EXAMPLES: @@ -915,9 +897,7 @@ def is_finite(self): Return ``True`` if the Drinfeld module is finite; return ``False`` otherwise. - OUTPUT: - - - a boolean + OUTPUT: a boolean EXAMPLES: @@ -949,10 +929,8 @@ def j(self): finite, as we force the function ring to be of the form `\Fq[X]`. - OUTPUT: - - - an element in the base ring if the rank is two; an - exception is raised otherwise + OUTPUT: an element in the base ring if the rank is two; an + exception is raised otherwise EXAMPLES: @@ -988,10 +966,8 @@ def morphism(self): r""" Return the morphism object that defines the Drinfeld module. - OUTPUT: - - - a ring morphism, from the function ring to the Ore polynomial - ring + OUTPUT: a ring morphism, from the function ring to the Ore + polynomial ring EXAMPLES: @@ -1037,9 +1013,7 @@ def ore_polring(self): r""" Return the Ore polynomial ring of the Drinfeld module. - OUTPUT: - - - an Ore polynomial ring + OUTPUT: an Ore polynomial ring EXAMPLES: @@ -1081,9 +1055,7 @@ def ore_variable(self): The Ore variable is defined as the generator of the Ore polynomial ring. - OUTPUT: - - - an Ore polynomial + OUTPUT: an Ore polynomial EXAMPLES: @@ -1118,9 +1090,7 @@ def rank(self): When the function ring is a polynomial ring, the rank is the degree of the generator. - OUTPUT: - - - an integer + OUTPUT: an integer EXAMPLES: @@ -1153,9 +1123,7 @@ def velu(self, isog): - ``isog`` -- the Ore polynomial that defines the isogeny - OUTPUT: - - - a Drinfeld module + OUTPUT: a Drinfeld module ALGORITHM: diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 40563bd83d4..04080461170 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -119,9 +119,7 @@ def frobenius_endomorphism(self): *Frobenius endomorphism* is defined as the endomorphism whose defining Ore polynomial is `t^q`. - OUTPUT: - - - a Drinfeld module morphism + OUTPUT: a Drinfeld module morphism EXAMPLES: @@ -168,9 +166,7 @@ def frobenius_charpoly(self, var='T'): - ``var`` -- (optional) the name of the second variable - OUTPUT: - - - a polynomial in `\Fq[X][T]` + OUTPUT: a polynomial in `\Fq[X][T]` EXAMPLES: @@ -232,9 +228,7 @@ def frobenius_norm(self): Let `n` be the degree of the base ring over `\Fq`. Then the Frobenius norm has degree `n`. - OUTPUT: - - - a polynomial in `\Fq[X]` + OUTPUT: a polynomial in `\Fq[X]` EXAMPLES: @@ -281,9 +275,7 @@ def frobenius_trace(self): Let `n` be the degree of the base ring over `\Fq`. Then the Frobenius trace has degree `\leq \frac{n}{2}`. - OUTPUT: - - - a polynomial in `\Fq[X]` + OUTPUT: a polynomial in `\Fq[X]` ALGORITHM: @@ -335,9 +327,7 @@ def is_ordinary(self): Frobenius trace. A *supersingular* rank two finite Drinfeld module is a Drinfeld module that is not ordinary. - OUTPUT: - - - a boolean + OUTPUT: a boolean EXAMPLES: @@ -373,9 +363,7 @@ def is_supersingular(self): Frobenius trace. An *ordinary* rank two finite Drinfeld module is a Drinfeld module that is not supersingular. - OUTPUT: - - - a boolean + OUTPUT: a boolean EXAMPLES: From 3962e10587ebd857759b8ee18e0d5a81bcccfc30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 16:09:45 +0200 Subject: [PATCH 098/197] Raise an exception if invalid input for velu method in DrinfeldModule When the input Ore polynomial does not define an isogeny, raise ValueError. See https://trac.sagemath.org/ticket/33713#comment:88. --- .../drinfeld_modules/drinfeld_module.py | 47 ++++++++++++++----- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index f1480b9fd5b..11f30b7f02c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -377,10 +377,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: The Vélu formula - Let ``ore_pol`` be a non-zero Ore polynomial ``ore_pol``. For - Drinfeld module, it is easy to decide if there exists a Drinfeld - module ``psi`` such that ``ore_pol`` is an isogeny from ``self`` to - ``psi``. We can also find ``psi``, which is unique. + Let ``ore_pol`` be a non-zero Ore polynomial. For Drinfeld module, + it is easy to decide if there exists a Drinfeld module ``psi`` such + that ``ore_pol`` is an isogeny from ``self`` to ``psi``. If so, we + find ``psi``:: sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z sage: psi = phi.velu(ore_pol) @@ -391,6 +391,17 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: ore_pol * phi(X) == psi(X) * ore_pol True + If the input does not define an isogeny, an exception is raised: + + sage: phi.velu(0) + Traceback (most recent call last): + ... + ValueError: the input does not define an isogeny + sage: phi.velu(t) + Traceback (most recent call last): + ... + ValueError: the input does not define an isogeny + .. RUBRIC:: The action of a Drinfeld module An `\Fq[X]`-Drinfeld module `\phi` notoriously makes any field @@ -1112,12 +1123,9 @@ def rank(self): def velu(self, isog): r""" - Return a new Drinfeld module such that ``isog`` is an + Return a new Drinfeld module such that input is an isogeny to this module with domain ``self``; if no such isogeny - exists, return ``None``. - - If the input is zero, return ``None``, as an isogeny is - required to be non zero. + exists, raise an exception. INPUT: @@ -1171,14 +1179,27 @@ def velu(self, isog): returns None:: sage: phi.velu(0) + Traceback (most recent call last): + ... + ValueError: the input does not define an isogeny sage: phi.velu(t) + Traceback (most recent call last): + ... + ValueError: the input does not define an isogeny sage: phi.velu(t^3 + t + 2) + Traceback (most recent call last): + ... + ValueError: the input does not define an isogeny """ if not isog in self.ore_polring(): raise TypeError('input must be an Ore polynomial') + e = ValueError('the input does not define an isogeny') if isog == 0: - return None - if not self.characteristic().degree().divides(isog.valuation()): - return None + raise e quo, rem = (isog * self.gen()).right_quo_rem(isog) - return None if rem != 0 else DrinfeldModule(self._function_ring, quo) + char_deg = self.characteristic().degree() + if not char_deg.divides(isog.valuation()) \ + or rem != 0: + raise e + else: + return self.category()(quo) From 5f8c0050392027b4a49b90a3aad75306d621e1e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 17:15:01 +0200 Subject: [PATCH 099/197] Add _latex_ and _repr_ docstrings in Drinfeld modules classes See https://trac.sagemath.org/ticket/33713#comment:88. --- src/sage/categories/drinfeld_modules.py | 46 +++++++++++++++++++ .../function_field/drinfeld_modules/action.py | 32 ++++++++++++- .../drinfeld_modules/drinfeld_module.py | 30 ++++++++++++ .../function_field/drinfeld_modules/homset.py | 34 ++++++++++++++ .../drinfeld_modules/morphism.py | 40 ++++++++++++++++ 5 files changed, 181 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 98ccb24152a..e2f7951d669 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -21,6 +21,7 @@ from sage.categories.category import CategoryWithParameters from sage.categories.homsets import Homsets from sage.misc.functional import log +from sage.misc.latex import latex from sage.rings.integer import Integer from sage.rings.morphism import RingHomomorphism from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing @@ -222,7 +223,52 @@ def _call_(self, gen): def _make_named_class_key(self, name): return self._function_ring.category() + def _latex_(self): + r""" + Return a latex representation of the category + + OUTPUT: a string + + EXAMPLE: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: latex(cat) + sage: latex(cat) + \text{Category{ }of{ }Drinfeld{ }modules{ }defined{ }by\begin{array}{l} + \text{\texttt{Ring{ }morphism:}}\\ + \text{\texttt{{ }{ }From:{ }Univariate{ }Polynomial{ }Ring{ }in{ }X{ }over{ }Finite{ }Field{ }of{ }size{ }11}}\\ + \text{\texttt{{ }{ }To:{ }{ }{ }Finite{ }Field{ }in{ }z{ }of{ }size{ }11{\char`\^}4}}\\ + \text{\texttt{{ }{ }Defn:{ }X{ }|{-}{-}>{ }z{\char`\^}3{ }+{ }7*z{\char`\^}2{ }+{ }6*z{ }+{ }10}} + \end{array} + """ + return f'\\text{{Category{{ }}of{{ }}Drinfeld{{ }}modules{{ }}' \ + f'defined{{ }}by{latex(self._morphism)}' + def _repr_(self): + r""" + Return a string representation of the category + + OUTPUT: a string + + EXAMPLE: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat + Category of Drinfeld modules defined by Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^4 + Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + """ return f'Category of Drinfeld modules defined by {self._morphism}' # Somehow required for the class definition diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 1901a07d371..62a81dc3364 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -20,7 +20,7 @@ #***************************************************************************** from sage.categories.action import Action - +from sage.misc.latex import latex from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule @@ -89,11 +89,41 @@ def _act_(self, pol, x): return self._drinfeld_module(pol)(x) def _latex_(self): + r""" + Return a LaTeX representation of the action. + + OUTPUT: a string + + EXAMPLES: + + sage: Fq. = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: action = phi.action() + sage: latex(action) + \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 11^2 + """ return f'\\text{{Action{{ }}on{{ }}}}' \ f'{latex(self._drinfeld_module.base_ring())}\\text{{{{ }}' \ f'induced{{ }}by{{ }}}}{self._drinfeld_module}' def _repr_(self): + r""" + Return a string representation of the action. + + OUTPUT: a string + + EXAMPLES: + + sage: Fq. = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: action = phi.action() + sage: action + Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 11^2 + """ return f'Action on {self._drinfeld_module.base_ring()} induced by ' \ f'{self._drinfeld_module}' diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 11f30b7f02c..438d223f9e1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -562,12 +562,42 @@ def _check_rank_two(self): raise NotImplementedError('rank must be 2') def _latex_(self): + r""" + Return a LaTeX representation of the Drinfeld module. + + OUTPUT: a string + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: latex(phi) + \text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }}\Bold{F}_{5^{12}} + """ return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ f'{latex(self._function_ring.gen())} '\ f'\\mapsto {latex(self._gen)}' \ f'\\text{{{{ }}over{{ }}}}{latex(self._base_ring)}' def _repr_(self): + r""" + Return a string representation of the Drinfeld module. + + OUTPUT: a string + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi + Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + """ return f'Drinfeld module defined by {self._function_ring.gen()} ' \ f'|--> {self._gen} over {self._base_ring}' diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 7229df993a1..a5c2fd67df0 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -131,11 +131,45 @@ def __init__(self, X, Y, category=None, check=True): super().__init__(X, Y, category=category, base=base, check=check) def _latex_(self): + r""" + Return a LaTeX representation of the homset. + + OUTPUT: a string + + EXAMPLES: + + sage: Fq = GF(27) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) + sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: hom = Hom(phi, psi) + sage: latex(hom) + \text{Set{ }of{ }Drinfeld{ }module{ }morphisms{ }from}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto 2 t^{2} + z_{6} t + z_{6}\text{{ }over{ }}\Bold{F}_{3^{6}}\text{{ }to{ }}Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 + """ return f'\\text{{Set{{ }}of{{ }}Drinfeld{{ }}module{{ }}morphisms' \ f'{{ }}from}}{latex(self.domain())}\\text{{{{ }}to{{ }}}}' \ f'{self.codomain()}' def _repr_(self): + r""" + Return a string representation of the homset. + + OUTPUT: a string + + EXAMPLES: + + sage: Fq = GF(27) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) + sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: hom = Hom(phi, psi) + sage: hom + Set of Drinfeld module morphisms: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 + """ return f'Set of Drinfeld module morphisms:\n' \ f' From: {self.domain()}\n' \ f' To: {self.codomain()}' diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 80625539d7d..61ad14914d1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -139,6 +139,27 @@ def __init__(self, parent, ore_pol): self._ore_polynomial = ore_pol def _latex_(self): + r""" + Return a LaTeX representation of the morphism. + + OUTPUT: a string + + EXAMPLES: + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: latex(morphism) + \begin{array}{l} + \text{Drinfeld{ }module{ }morphism:}\\ + \text{{ }{ }From:{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto t^{2} + t + z_{6}\text{{ }over{ }}\Bold{F}_{2^{6}}}\\ + \text{{ }{ }To:{ }}{ }{ }\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto t^{2} + \left(z_{6}^{4} + z_{6}^{2} + 1\right) t + z_{6}\text{{ }over{ }}\Bold{F}_{2^{6}}\\ + \text{{ }{ }Defn:{ }}t + z_{6}^{5} + z_{6}^{2} + 1 + \end{array} + """ return f'\\begin{{array}}{{l}}\n' \ f'\\text{{Drinfeld{{ }}module{{ }}morphism:}}\\\\\n' \ f'\\text{{{{ }}{{ }}From:{{ }}}}{latex(self._domain)}}}\\\\\n' \ @@ -147,6 +168,25 @@ def _latex_(self): f'\\end{{array}}' def _repr_(self): + r""" + Return a string representation of the morphism. + + OUTPUT: a string + + EXAMPLES: + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_variable() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> t^2 + t + z6 over Finite Field in z6 of size 2^6 + To: Drinfeld module defined by X |--> t^2 + (z6^4 + z6^2 + 1)*t + z6 over Finite Field in z6 of size 2^6 + Defn: t + z6^5 + z6^2 + 1 + """ return f'Drinfeld Module morphism:\n' \ f' From: {self._domain}\n' \ f' To: {self._codomain}\n' \ From 83c728ebf5fb752f5e36cc2693ac1f1ff37636f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 17:16:52 +0200 Subject: [PATCH 100/197] Rename j method to j_invariant in DrinfeldModule See https://trac.sagemath.org/ticket/33713#comment:90. --- .../drinfeld_modules/drinfeld_module.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 438d223f9e1..c3fac7893f6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -303,7 +303,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): As well as the j-invariant if the rank is two:: - sage: phi.j() # j-invariant + sage: phi.j_invariant() # j-invariant 1 .. RUBRIC:: Morphisms, isogenies @@ -957,7 +957,7 @@ def is_finite(self): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return isinstance(self, FiniteDrinfeldModule) - def j(self): + def j_invariant(self): r""" Return the j-invariant of the Drinfeld module; only the rank two case has been implemented, a NotImplementedError is raised if @@ -980,19 +980,19 @@ def j(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.j() + sage: phi.j_invariant() z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2 sage: psi = DrinfeldModule(FqX, [p_root, 1, 1]) - sage: psi.j() + sage: psi.j_invariant() 1 sage: rho = DrinfeldModule(FqX, [p_root, 0, 1]) - sage: rho.j() + sage: rho.j_invariant() 0 The rank must be two:: sage: theta = DrinfeldModule(FqX, [p_root, 1, 0]) - sage: theta.j() + sage: theta.j_invariant() Traceback (most recent call last): ... NotImplementedError: rank must be 2 From 53088a9d7f981d6bd5dba9fa60405c975de583a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 17:18:14 +0200 Subject: [PATCH 101/197] Add various docstrings in Drinfeld module classes Some methods were not properly documented; this is solved. --- .../function_field/drinfeld_modules/action.py | 27 ++++++ .../drinfeld_modules/drinfeld_module.py | 28 ++++++ .../function_field/drinfeld_modules/homset.py | 90 +++++++++++++++++++ 3 files changed, 145 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 62a81dc3364..be0b3ecd51c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -82,6 +82,33 @@ def __init__(self, drinfeld_module): drinfeld_module.base_ring()) def _act_(self, pol, x): + r""" + Return ``pol * x``, where ``*`` is the action. + + INPUT: + + - ``pol`` -- a polynomial in the function ring of the Drinfeld + module + - ``x`` -- an element in the base ring of the Drinfeld module + + OUTPUT: an element in the base ring of the Drinfeld module. + + EXAMPLES: + + sage: Fq. = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: action = phi.action() + sage: P = X + 1 + sage: a = z + sage: action(P, a) + 4*z + 2 + sage: action(0, K.random_element()) + 0 + sage: action(FqX.random_element(), 0) + 0 + """ if pol not in self._drinfeld_module.function_ring(): raise TypeError('first input must be in the function ring') if x not in self._drinfeld_module.base_ring(): diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index c3fac7893f6..2b82e8b8edd 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -602,6 +602,34 @@ def _repr_(self): f'|--> {self._gen} over {self._base_ring}' def action(self): + r""" + Return the action object that represents the action on the base that is + induced by the Drinfeld module. + + OUTPUT: a Drinfeld module action object + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: action = phi.action() + sage: action + Action on Finite Field in z12 of size 5^12 induced by Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + + The action on elements is computed as follows:: + + sage: P = X^2 + X + 1 + sage: a = z12 + 1 + sage: action(P, a) + 3*z12^11 + 2*z12^10 + 3*z12^9 + 3*z12^7 + 4*z12^5 + z12^4 + z12^3 + 2*z12 + 1 + sage: action(0, a) + 0 + sage: action(P, 0) + 0 + """ from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction return DrinfeldModuleAction(self) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index a5c2fd67df0..680defe2002 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -175,6 +175,57 @@ def _repr_(self): f' To: {self.codomain()}' def __contains__(self, x): + r""" + Implement the ``in`` operator for the homset; return ``True`` if + the input defines a morphism in the homset, return ``False`` + otherwise. + + INPUT: + + - ``x`` -- an Ore polynomial or a Drinfeld module morphism + + OUTPUT: a boolean + + EXAMPLES: + + In the next examples, the input is an Ore polynomial:: + + sage: Fq = GF(27) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) + sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: hom = Hom(phi, psi) + sage: end = End(phi) + sage: t = phi.ore_variable() + + sage: 1 in hom + False + sage: t^6 in hom + False + sage: t + 1 in hom + True + sage: 1 in end + True + sage: t^6 in end + True + sage: t + 1 in end + False + + Whereas the input is now a Drinfeld module morphism:: + + sage: isogeny = hom(t + 1) + sage: isogeny in hom + True + sage: end(0) in end + True + sage: identity_morphism = end(1) + sage: identity_morphism in hom + False + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism in hom + False + """ try: x = self(x) return True @@ -182,6 +233,45 @@ def __contains__(self, x): return False def _element_constructor_(self, *args, **kwds): + r""" + Return a Drinfeld module morphism defined by the input, which is + an Ore polynomial. + + INPUT: an Ore polynomial + + OUTPUT: a Drinfeld module morphism + + EXAMPLES: + + sage: Fq = GF(27) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) + sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: hom = Hom(phi, psi) + sage: end = End(phi) + sage: t = phi.ore_variable() + sage: identity_morphism = end(1) + sage: identity_morphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + Defn: 1 + + sage: frobenius_endomorphism = end(t^6) + sage: frobenius_endomorphism + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + Defn: t^6 + + sage: isogeny = hom(t + 1) + sage: isogeny + Drinfeld Module morphism: + From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 + Defn: t + 1 + """ # NOTE: This used to be self.element_class(self, ...), but this # would call __init__ instead of __classcall_private__. This # seems to work, but I don't know what I'm doing. From 261ab62cdd20badc99fbcb1ddf59bfcf56073ed0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 17:19:09 +0200 Subject: [PATCH 102/197] Include final comments (87-90) from Ticket I added (almost all) suggestions from comments 87-91 from ticket https://trac.sagemath.org/ticket/33713. Some of the suggestions were already introduced in previous commits. --- .../function_field/drinfeld_modules/action.py | 2 + .../drinfeld_modules/drinfeld_module.py | 63 +++++++++++-------- .../function_field/drinfeld_modules/homset.py | 1 - 3 files changed, 39 insertions(+), 27 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index be0b3ecd51c..05999b7ea96 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -160,6 +160,8 @@ def drinfeld_module(self): OUTPUT: a Drinfeld module + EXAMPLES: + sage: Fq. = GF(11) sage: FqX. = Fq[] sage: K. = Fq.extension(2) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 2b82e8b8edd..5e2232398e1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -60,9 +60,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): We say that `\Fq[X]` is the *function ring of `\phi`*; `K` is the *base ring of `\phi`*, or simply its base or base field; `\Fq[X]` is - the *function ring of*; *K\{\tau\}* is the *Ore polynomial ring*; - `t` is the *Ore variable*. The *generator of `\phi`* is `\phi_X`, - its *constant coefficient* is the constant coefficient of `\phi_X`. + the *function ring of `\phi`*; *K\{\tau\}* is the *Ore polynomial + ring of `\phi`*; `t` is the *Ore variable of `\phi`*. The *generator of `\phi`* is + `\phi_X`, its *constant coefficient* is the constant coefficient of + `\phi_X`. The Drinfeld module `\phi` is uniquely determined by the image `\phi_X` of `X`. This Ore polynomial is an input of the class @@ -89,9 +90,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: Construction and input - A Drinfeld module object (class - :class:`sage.rings.function_field.drinfeld_module.drinfeld_module.DrinfeldModule`) - is constructed as follows:: + A Drinfeld module object is constructed as follows:: sage: Fq. = GF(3^2) sage: FqX. = Fq[] @@ -105,7 +104,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): also use regular Ore polynomials:: sage: ore_polring = phi.ore_polring() - sage: t = phi.ore_variable() # Is ore_polring.gen() + sage: t = phi.ore_variable() # same as ore_polring.gen() sage: psi_X = z + t^3 sage: psi = DrinfeldModule(FqX, psi_X) sage: psi @@ -113,9 +112,6 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: psi(X) == psi_X True - Naturally, the input is checked, and exceptions are raised when the - input is invalid. - The generator must have positive degree:: sage: DrinfeldModule(FqX, [z]) @@ -230,6 +226,14 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: phi.is_finite() True + It is possible to change the base ring:: + + sage: L = K.extension(2) + sage: phi_rebased = phi.change_ring(L) + sage: Ltau = phi_rebased.ore_polring() + sage: Ltau(phi(X)) == phi_rebased(X) + True + .. RUBRIC:: The category of Drinfeld modules Drinfeld modules have their own category (see class @@ -313,8 +317,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): every `a \in \Fq[X]`. In our case, this is equivalent to verifying `f \phi_X = \psi_X f`. An *isogeny* is a non-zero morphism. - Use the ``in`` syntax to test if an Ore polynomial defines an - isogeny:: + Use the ``in`` syntax to test if an Ore polynomial defines a + morphism:: sage: phi(X) in Hom(phi, phi) True @@ -405,16 +409,17 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: The action of a Drinfeld module An `\Fq[X]`-Drinfeld module `\phi` notoriously makes any field - extension `L/K` a left `\Fq[X]`-module with the law defined by - `(P(X), a) \mapsto \phi_P(a)`, where `a \in L`. The method - :meth:`action` returns an ``Action`` object representing the - Drinfeld module action; in this implementation, `K = L`. + extension `L/K` a left `\Fq[X]`-module. Let `x \in L`, let `P \in + \Fq[X]`; the action is defined as `(P, a) \mapsto \phi_P(a)`, where + `\phi_P(a)`. The method :meth:`action` returns an ``Action`` object + representing the Drinfeld module action; in this implementation, `K + = L`. sage: action = phi.action() sage: action Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - The action is computed as follows:: + The action on elements is computed as follows:: sage: P = X + 1 sage: a = z @@ -426,6 +431,13 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: action(FqX.random_element(), 0) 0 + To act on a field larger than `K`, one can change the ring of the + Drinfeld module, then create the action:: + + sage: extended_action = phi.change_ring(K.extension(5)).action() + sage: extended_action + Action on Finite Field in z60 of size 3^60 induced by Drinfeld module defined by X |--> t^2 + t + 2*z60^59 + z60^56 + 2*z60^55 + 2*z60^54 + 2*z60^53 + z60^49 + z60^48 + z60^47 + 2*z60^45 + z60^44 + 2*z60^41 + 2*z60^40 + 2*z60^39 + 2*z60^37 + 2*z60^36 + z60^34 + z60^33 + z60^32 + 2*z60^31 + 2*z60^30 + 2*z60^27 + 2*z60^25 + z60^23 + z60^22 + z60^21 + 2*z60^20 + z60^19 + z60^18 + z60^17 + z60^16 + z60^15 + 2*z60^14 + z60^12 + 2*z60^11 + 2*z60^10 + z60^8 + z60^6 + 2*z60^5 + z60^4 + z60^3 + z60 + 1 over Finite Field in z60 of size 3^60 + .. RUBRIC:: Inversion of the Drinfeld module Given an Ore polynomial that equals `\phi_a` for some `a \in @@ -480,7 +492,8 @@ def __classcall_private__(cls, function_ring, gen, name='t'): category = DrinfeldModules(gamma, name=name) # Check gen as Ore polynomial - if ore_polring not in (None, category.ore_polring()): + if ore_polring is not None and \ + ore_polring != category.ore_polring(): raise ValueError(f'generator must lie in {category.ore_polring()}') ore_polring = category.ore_polring() # Sanity cast gen = ore_polring(gen) @@ -491,8 +504,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): if ore_polring_base.is_finite(): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return FiniteDrinfeldModule(gen, category) - else: - return cls.__classcall__(cls, gen, category) + return cls.__classcall__(cls, gen, category) def __init__(self, gen, category): super().__init__(category=category) @@ -833,8 +845,8 @@ def gen(self): Return the generator of the Drinfeld module, i.e. `\phi_X`. This method makes sense because, in our case, the function ring - `\Fq[X]`, which is `\Fq`-generated by a single element element, - whose image characterizes the Drinfeld module. + `\Fq[X]`, which is `\Fq`-generated by a single element, whose + image characterizes the Drinfeld module. OUTPUT: an Ore polynomial @@ -872,7 +884,7 @@ def height(self): def invert(self, ore_pol): r""" - Return the pre-image of the input under the Drinfeld module; + Return the preimage of the input under the Drinfeld module; raise an exception if the input is not in the image of the Drinfeld module. @@ -963,8 +975,7 @@ def invert(self, ore_pol): def is_finite(self): r""" - Return ``True`` if the Drinfeld module is finite; return - ``False`` otherwise. + Return ``True`` whether the Drinfeld module is finite. OUTPUT: a boolean @@ -1194,7 +1205,7 @@ def velu(self, isog): ALGORITHM: The input defines an isogeny if only if: - 1. The degree of the characteristic devides the height + 1. The degree of the characteristic divides the height of the input. (The height of an Ore polynomial `P(t)` is the maximum `n` such that `t^n` right-divides `P(t)`.) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 680defe2002..8ec98e7562f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -62,7 +62,6 @@ class DrinfeldModuleHomset(Homset): sage: end is Hom(phi, phi) True - One can create morphism objects by calling the homset:: sage: t = phi.ore_variable() From c6aaebdedd3a65cf0ba352bba0c713711ca3458d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 22 Aug 2022 19:38:39 +0200 Subject: [PATCH 103/197] Add coefficients methods in DrinfeldModule I created the DrinfeldModule methods: - coefficients, - coefficient, - __getitem__. The code is tested and I added documentation in the main docstring. See https://trac.sagemath.org/ticket/33713#comment:89. --- .../drinfeld_modules/drinfeld_module.py | 136 +++++++++++++++--- 1 file changed, 120 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 5e2232398e1..0822d03fe44 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -259,16 +259,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): suggests, the morphism `\gamma` uniquely determines the category of a Drinfeld module. - .. RUBRIC:: Basic methods - - For a polynomial `a \in \Fq[X]`, compute `\phi_a` by calling `phi`:: - - sage: phi(X) # phi_X - t^2 + t + z - sage: phi(X^3 + X + 1) # phi_X^3 +X + 1 - t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 - sage: phi(1) # phi_1 - 1 + .. RUBRIC:: Getters and basic properties One can retrieve basic properties:: @@ -298,6 +289,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) Defn: X |--> t^2 + t + z + .. RUBRIC:: Height, rank and j-invariant + One can compute the rank and height:: sage: phi.rank() @@ -310,6 +303,36 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: phi.j_invariant() # j-invariant 1 + .. RUBRIC:: The generator of a Drinfeld module + + For a polynomial `a \in \Fq[X]`, compute `\phi_a` by calling `phi`:: + + sage: phi(X) # phi_X + t^2 + t + z + sage: phi(X^3 + X + 1) # phi_X^3 +X + 1 + t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 + sage: phi(1) # phi_1 + 1 + + This is especially useful to quickly retrive `\phi_X`, the + generator. Furthermore, a Drinfeld `\Fq[X]`-module can be seen as an + Ore polynomial with positive degree and constant coefficient + `\gamma(X)`. This analogy is the motivation for the following + methods:: + + sage: phi.coefficients() + [z, 1, 1] + + sage: phi.coefficient(1) + 1 + sage: phi.coefficient(1) + 1 + + The ``[...]`` was introduced as a shortcut to ``phi(X)[...]``:: + + sage: phi[1] + 1 + .. RUBRIC:: Morphisms, isogenies A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore @@ -530,6 +553,12 @@ def __call__(self, a): return self._morphism(a) + def __getitem__(self, n): + r""" + See method :meth:`coefficient`. + """ + return self.coefficient(n) + def _Hom_(self, other, category): r""" Return ``DrinfeldModuleHomset(self, other, category)``. @@ -812,13 +841,88 @@ def constant_coefficient(self): ... ValueError: constant coefficient must be a root of the characteristic - One can also retrieve the constant coefficient using - ``phi(X)[0]`:: + One can also retrieve the constant coefficient using ``phi[0]`:: + + sage: phi.constant_coefficient() == phi[0] + True + """ + return self[0] + + def coefficient(self, n): + r""" + Return the n-th coefficient of the generator. + + INPUT: + + - ``n`` -- a non-negative integer + + OUTPUT: an element in the base ring + + EXAMPLES: - sage: phi.constant_coefficient() == phi(X)[0] + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.coefficient(0) + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi.coefficient(0) == p_root True + sage: phi.coefficient(1) + z12^3 + sage: phi.coefficient(2) + z12^5 + sage: phi.coefficient(5) + Traceback (most recent call last): + ... + ValueError: input must be >= 0 and <= rank + """ + if not isinstance(n, Integer) and not isinstance(n, int): + raise TypeError('input must be an integer') + if not 0 <= n <= self.rank(): + raise ValueError('input must be >= 0 and <= rank') + return self.coefficients(sparse=False)[n] + + def coefficients(self, sparse=True): + r""" + Return the coefficients of the generator, as a list. + + If the the flag ``sparse`` is ``True`` (default), only return the + non-zero coefficients; otherwise, return all of them. + + INPUT: + - ``sparse`` -- a boolean + + OUTPUT: a list of elements in the base ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.coefficients() + [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, + z12^3, + z12^5] + + Careful, the method only returns the non-zero coefficients, + unless otherwise specified:: + + sage: rho = DrinfeldModule(FqX, [p_root, 0, 0, 0, 1]) + sage: rho.coefficients() + [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, + 1] + sage: rho.coefficients(sparse=False) + [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, + 0, + 0, + 0, + 1] """ - return self.gen()[0] + return self._gen.coefficients(sparse=sparse) def function_ring(self): r""" @@ -1037,8 +1141,8 @@ def j_invariant(self): NotImplementedError: rank must be 2 """ self._check_rank_two() - g = self._gen[1] - delta = self._gen[2] + g = self[1] + delta = self[2] q = self._Fq.order() return (g**(q+1)) / delta From 529f5ec6b7757a7451d0dfeeaf51a910a9650dc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 23 Aug 2022 14:25:28 +0200 Subject: [PATCH 104/197] Use Fq[X] notation as little as possible in Drinfeld modules classes (comment 96) See https://trac.sagemath.org/ticket/33713#comment:96. The suggestion is to use as little notations like `\Fq[X]` as possible, as different people use different variables. What I did is that I kept the same notations in the main docstring of each class, as I consider notations to be global notations in these giant docstrings. However, I removed them in the method docstrings. I may have forgot some modifications. Also feel free to suggest enhancements on those changes, as they were quickly made. --- src/sage/categories/drinfeld_modules.py | 23 ++-- .../function_field/drinfeld_modules/action.py | 16 ++- .../drinfeld_modules/drinfeld_module.py | 122 ++++++++++-------- .../finite_drinfeld_module.py | 79 +++++++----- .../drinfeld_modules/morphism.py | 16 +-- 5 files changed, 142 insertions(+), 114 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index e2f7951d669..4a7c4e6b155 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -31,10 +31,11 @@ class DrinfeldModules(CategoryWithParameters): r""" This class represents the category of Drinfeld modules on a given - `\Fq[X]`-field `K`. + base. - The `\Fq[X]`-field structure on `K` is given by a ring morphism - `\gamma: \Fq[X] \to K`. + Let `\phi` be a Drinfeld module in the present category. We denote + its function ring `\Fq[X]` and its base ring `K`. The `\Fq[X]`-field + structure on `K` is given by a morphism `\gamma` We say that `\Fq[X]` is the *function ring of the category*; `K` is the *base of the category*, or simply its base ring or base field; `\Fq[X]` is @@ -43,7 +44,11 @@ class DrinfeldModules(CategoryWithParameters): `t` is the *Ore variable of the category*. The *constant coefficient of the category* is `\gamma(X)`. - INPUT: a ring morphism `\Fq[X] \to K` + .. NOTE:: + + These notations will be used throughout this docstring. + + INPUT: a ring morphism from the function ring to the base ring EXAMPLES: @@ -86,7 +91,7 @@ class DrinfeldModules(CategoryWithParameters): sage: cat.morphism()(X) == cat.constant_coefficient() True - Similarly, the *`\Fq[X]`-characteristic* of the category is either + Similarly, the *function ring-characteristic* of the category is either `0` or the unique monic polynomial in `\Fq[X]` that generates `\mathrm{Ker}(\gamma)`:: @@ -302,9 +307,9 @@ def base(self): def characteristic(self): r""" - Return the `\Fq[X]`-characteristic of the category. + Return the function ring-characteristic of the category. - OUTPUT: `0` or a monic prime polynomial in `\Fq[X]` + OUTPUT: `0` or a monic prime polynomial in the function ring EXAMPLES: @@ -400,7 +405,7 @@ def ore_polring(self): r""" Return the Ore polynomial ring of the category. - OUTPUT: the Ore polynomial ring `K\{\tau\}` + OUTPUT: an Ore polynomial ring EXAMPLES: @@ -421,7 +426,7 @@ def ore_variable(self): r""" Return the Ore variable of the category. - OUTPUT: the generator of the Ore polynomial ring + OUTPUT: an Ore polynomial EXAMPLES: diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 05999b7ea96..535474c3f57 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -26,12 +26,15 @@ class DrinfeldModuleAction(Action): r""" - This class represents the left `\Fq[X]`-module action induced by a - Drinfeld `\Fq[X]`-module defined over an `\Fq[X]`-field `K`. - Let `L/K` be a field extension, let `x \in L`, let `P \in \Fq[X]`; - the action is defined as `(P, a) \mapsto \phi_P(a)`, where - `\phi_P(a)`. In this implementation, `L` is `K`. + This class represents the module action induced by a Drinfeld + module. + + Let `\phi` be a Drinfeld module with function ring `\Fq[X]` and base + ring `K`, whose `\Fq[X]`-field structure is given by a morphism + `\gamma`. Let `L/K` be a field extension, let `x \in L`, let `a` be + a function ring element; the action is defined as `(a, x) \mapsto + \phi_a(x)`. In this implementation, `L` is `K`. The action is instanciated as follows. Note that the user should never explicitely instanciate the class `DrinfeldModuleAction`:: @@ -87,8 +90,7 @@ def _act_(self, pol, x): INPUT: - - ``pol`` -- a polynomial in the function ring of the Drinfeld - module + - ``pol`` -- a function ring element - ``x`` -- an element in the base ring of the Drinfeld module OUTPUT: an element in the base ring of the Drinfeld module. diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 0822d03fe44..08b1309de86 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -45,25 +45,31 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): equiped a ring morphism `\gamma: \Fq[X] \to K` --- the field `K` is said to be an *`\Fq[X]`-field*, and the monic polynomial that generates `\Ker(\gamma)` is called the *`\Fq[X]`-characteristic of - the `\Fq[X]`-field `K`* (this characteristic plays the role of the - characteristic of a function field). Let `K\{\tau\}` be the ring of - Ore polynomials with coefficients in `K` and Frobenius - variable `\tau: x \mapsto x^q`. A *Drinfeld `\Fq[X]`-module over the - `\Fq[X]`-field `K`* is a ring morphism `\phi: \Fq[X] \to K\{\tau\}` - such that: + the `\Fq[X]`-field `K`*. This characteristic plays the role of the + characteristic of a function field. + + Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in + `K` and Frobenius variable `\tau: x \mapsto x^q`. A *Drinfeld + `\Fq[X]`-module over the `\Fq[X]`-field `K`* is a ring morphism + `\phi: \Fq[X] \to K\{\tau\}` such that: 1. The image of `\phi` contains non-constant Ore polynomials. - 2. For every `a \in \Fq[X]`, the constant coefficient `\phi(a)` - is `\gamma(a)`. + 2. For every element `a` in the function ring, the constant + coefficient `\phi(a)` is `\gamma(a)`. + + For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. + + .. NOTE:: - For `a \in \Fq[X]`, `\phi(a)` is denoted `\phi_a`. + These notations will be used throughout this docstring. We say that `\Fq[X]` is the *function ring of `\phi`*; `K` is the - *base ring of `\phi`*, or simply its base or base field; `\Fq[X]` is - the *function ring of `\phi`*; *K\{\tau\}* is the *Ore polynomial - ring of `\phi`*; `t` is the *Ore variable of `\phi`*. The *generator of `\phi`* is - `\phi_X`, its *constant coefficient* is the constant coefficient of - `\phi_X`. + *base ring of `\phi`*, or simply its base or base field; *K\{\tau\}* + is the *Ore polynomial ring of `\phi`*; `t` is the *Ore variable of + `\phi`*. The *generator of `\phi`* is `\phi_X`, its *constant + coefficient* is the constant coefficient of `\phi_X`. The + `\Fq[X]`-characteristic of the base ring `K` can also be referred to + as its *function ring-characteristic*. The Drinfeld module `\phi` is uniquely determined by the image `\phi_X` of `X`. This Ore polynomial is an input of the class @@ -74,16 +80,17 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. NOTE:: - Drinfeld modules are defined in a larger setting, in which - `\Fq[X]` is replaced by a more general ring: the ring of - functions in `k` that are regular outside `\infty`, where `k` is - a function field over `\Fq` with transcendance degree `1` and - `\infty` is a fixed place of `k`. This is out of the scope of - this implementation. + Drinfeld modules are defined in a larger setting, in which the + polynomial ring `\Fq[X]` is replaced by a more general function + ring: the ring of functions in `k` that are regular outside + `\infty`, where `k` is a function field over `\Fq` with + transcendance degree `1` and `\infty` is a fixed place of `k`. + This is out of the scope of this implementation. INPUT: - - ``function_ring`` -- the polynomial ring `\Fq[X]` + - ``function_ring`` -- the polynomial ring with coefficients in the finite + field `\Fq` - ``gen`` -- the generator `\phi_X`, as a list of coefficients or an Ore polynomial - ``name`` (optional) the name of the Ore variable @@ -119,8 +126,9 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): ... ValueError: generator must have positive degree - The coefficients of the generator must live in some field `K` that - is the codomain of a morphism `\gamma` with domain `\Fq[X]`:: + The coefficients of the generator must live in some base field `K` + that is the codomain of a morphism `\gamma: \Fq[X] \to K`, where + `\Fq[X]` is the function ring:: sage: DrinfeldModule(FqX, [z, QQ(1)]) Traceback (most recent call last): @@ -146,7 +154,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: DrinfeldModule(FqX, [Fq(1), 1, 1]) Drinfeld module defined by X |--> t^2 + t + 1 over Finite Field in z2 of size 3^2 - The function ring must be an `\Fq[X]`:: + The function ring must be an univariate polynomial ring whose + coefficients lie in a finite field:: sage: DrinfeldModule(K, [z, 1, 1]) Traceback (most recent call last): @@ -161,7 +170,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): If you already defined a category of Drinfeld modules, you must ensure that the constant coefficient is a root of the - `\Fq[X]-characteristic of the category base:: + function ring-characteristic of the category base:: sage: cat = phi.category() sage: cat([1, 1, K(1)]) @@ -250,7 +259,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): False This category holds crucial information, like the - `\Fq[X]`-characteristic of `K`:: + function ring-characteristic of the base:: sage: char = phi.category().characteristic() @@ -305,7 +314,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: The generator of a Drinfeld module - For a polynomial `a \in \Fq[X]`, compute `\phi_a` by calling `phi`:: + For a `a` in the function ring, `\phi_a` is computed by calling + `phi`:: sage: phi(X) # phi_X t^2 + t + z @@ -314,11 +324,11 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: phi(1) # phi_1 1 - This is especially useful to quickly retrive `\phi_X`, the - generator. Furthermore, a Drinfeld `\Fq[X]`-module can be seen as an - Ore polynomial with positive degree and constant coefficient - `\gamma(X)`. This analogy is the motivation for the following - methods:: + This is especially useful to quickly retrive `\phi_X`, the generator + of the Drinfeld module. Furthermore, a Drinfeld `\Fq[X]`-module can + be seen as an Ore polynomial with positive degree and constant + coefficient `\gamma(X)`. This analogy is the motivation for the + following methods:: sage: phi.coefficients() [z, 1, 1] @@ -337,8 +347,9 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for - every `a \in \Fq[X]`. In our case, this is equivalent to verifying - `f \phi_X = \psi_X f`. An *isogeny* is a non-zero morphism. + every `a` in the function ring. In our specific case, this is + equivalent to verifying `f \phi_X = \psi_X f`. An *isogeny* is a + non-zero morphism. Use the ``in`` syntax to test if an Ore polynomial defines a morphism:: @@ -432,9 +443,9 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: The action of a Drinfeld module An `\Fq[X]`-Drinfeld module `\phi` notoriously makes any field - extension `L/K` a left `\Fq[X]`-module. Let `x \in L`, let `P \in - \Fq[X]`; the action is defined as `(P, a) \mapsto \phi_P(a)`, where - `\phi_P(a)`. The method :meth:`action` returns an ``Action`` object + extension `L/K` a left `\Fq[X]`-module. Let `x \in L`, let `a` be in + the function ring; the action is defined as `(a, x) \mapsto + \phi_a(x)`. The method :meth:`action` returns an ``Action`` object representing the Drinfeld module action; in this implementation, `K = L`. @@ -463,8 +474,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: Inversion of the Drinfeld module - Given an Ore polynomial that equals `\phi_a` for some `a \in - \Fq[X]`, one can retrieve `a` (as a morphism, a Drinfeld + Given an Ore polynomial that equals `\phi_a` for some function ring + elelement `a`, one can retrieve `a` (as a morphism, a Drinfeld module is injective, see [Gos1998]_, cor. 4.5.2.):: sage: a = FqX.random_element() @@ -799,12 +810,8 @@ def change_ring(self, new_field, name=None): def constant_coefficient(self): r""" - Return the constant coefficient of the generator (`\phi_X`). + Return the constant coefficient of the generator. - From the definition of a Drinfeld module, the constant coefficient equals - `\gamma(X)`. Hence, it is a root of the `\Fq[X]`-characteristic - of the base ring. - OUTPUT: an element in the base ring EXAMPLES: @@ -817,7 +824,9 @@ def constant_coefficient(self): sage: phi.constant_coefficient() == p_root True - The constant coefficient equals `\gamma(X)`:: + Let `\Fq[X]` be the function ring, and let `\gamma` the morphism + defining the `\Fq[X]`-field structure of the base ring. The + constant coefficient equals `\gamma(X)`:: sage: cat = phi.category() sage: gamma = cat.morphism() @@ -928,7 +937,8 @@ def function_ring(self): r""" Return the function ring of the Drinfeld module. - In our case, the function ring is an `\Fq[X]`. + In our case, the function ring is an univariate polynomial ring + whose coefficients lie in a finite field `\Fq`. OUTPUT: a polynomial ring @@ -946,11 +956,13 @@ def function_ring(self): def gen(self): r""" - Return the generator of the Drinfeld module, i.e. `\phi_X`. + Return the generator of the Drinfeld module. + + Let `\phi` denote the Drinfeld module, and `\Fq[X]` be the + function ring. This method returns `\phi_X`. - This method makes sense because, in our case, the function ring - `\Fq[X]`, which is `\Fq`-generated by a single element, whose - image characterizes the Drinfeld module. + This method makes sense the function ring is `\Fq`-generated by + a single element, whose image characterizes the Drinfeld module. OUTPUT: an Ore polynomial @@ -997,7 +1009,7 @@ def invert(self, ore_pol): - ``ore_pol`` -- the Ore polynomial whose preimage we want to compute - OUTPUT: a polynomial + OUTPUT: an element in the function ring EXAMPLES: @@ -1109,9 +1121,8 @@ def j_invariant(self): Assume the rank is two. Write the generator `\phi_X = \gamma(X) + g\tau + \Delta\tau^2`. The j-invariant is defined by `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base field - of the polynomial ring. In our case, this base field is always - finite, as we force the function ring to be of the form - `\Fq[X]`. + of the function ring. In our case, this base field is always + finite. OUTPUT: an element in the base ring if the rank is two; an exception is raised otherwise @@ -1271,8 +1282,7 @@ def rank(self): r""" Return the rank of the Drinfeld module. - When the function ring is a polynomial ring, the rank is the - degree of the generator. + In our case, the rank is the degree of the generator. OUTPUT: an integer diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 04080461170..c7cd5c6d2f5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -35,6 +35,8 @@ class FiniteDrinfeldModule(DrinfeldModule): In this specific documentation, we only present the specifics of ``FiniteDrinfeldModle``. + .. RUBRIC:: Construction: + The user does not ever need to directly call ``FiniteDrinfeldModule``, as it is the (meta)class ``DrinfeldModule`` that is reponsible for instanciating @@ -51,7 +53,7 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: isinstance(phi, FiniteDrinfeldModule) True - But, the user should never use ``FiniteDrinfeldModule`` to test if a + The user should never use ``FiniteDrinfeldModule`` to test if a Drinfeld module is finite, but rather the ``is_finite`` method:: sage: phi.is_finite() @@ -149,15 +151,20 @@ def frobenius_charpoly(self, var='T'): otherwise. Let `\Fq` be the base field of the function ring. The - *characteristic polynomial `\chi` of the Frobenius endomorphism* is - defined in [Gek1991]_. An important feature of this polynomial - is that it is a monic bivariate polynomial in `T` with - coefficients in `\Fq[X]`. Write `\chi = T^2 - A(X)T + B(X)`, let - `t^n` be the Ore polynomial that defines the Frobenius - endomorphism of `\phi`; by definition, `n` is the degree of the - base ring over `\Fq`. We have `\chi(t^n)(\phi(X)) = t^{2n} - - \phi_A t^n + \phi_B = 0`, with `\deg(A) \leq \frac{n}{2}` and - `\deg(B) = n`. + *characteristic polynomial `\chi` of the Frobenius endomorphism* + is defined in [Gek1991]_. An important feature of this + polynomial is that it is a monic univariate polynomial with + coefficients in the function ring. As in our case the function + ring is a univariate polynomial ring, it is customary to see the + characteristic polynomial of the Frobenius endomorphism as a + bivariate polynomial. + + Let `\chi = T^2 - A(X)T + B(X)` be the characteristic polynomial + of the Frobenius endomorphism, let `t^n` be the Ore polynomial + that defines the Frobenius endomorphism of `\phi`; by + definition, `n` is the degree of the base ring over `\Fq`. We + have `\chi(t^n)(\phi(X)) = t^{2n} - \phi_A t^n + \phi_B = 0`, + with `\deg(A) \leq \frac{n}{2}` and `\deg(B) = n`. Note that the *Frobenius trace* is defined as `A(X)` and the *Frobenius norm` is defined as `B(X)`. @@ -166,7 +173,8 @@ def frobenius_charpoly(self, var='T'): - ``var`` -- (optional) the name of the second variable - OUTPUT: a polynomial in `\Fq[X][T]` + OUTPUT: an univariate polynomial with coefficients in the + function ring EXAMPLES: @@ -189,7 +197,7 @@ def frobenius_charpoly(self, var='T'): sage: B (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 - sage: n = 2 # Degree of the base ring over `\Fq` + sage: n = 2 # Degree of the base ring over Fq sage: A.degree() <= n/2 True sage: B.degree() == n @@ -221,14 +229,15 @@ def frobenius_norm(self): Return Frobenius norm of the Drinfeld module, if the rank is two; raise a NotImplementedError otherwise. - Write `\chi = T^2 - A(X)T + B(X) \in \Fq[X][T]` to be the - characteristic polynomial of the Frobenius endomorphism. The - *Frobenius norm* is defined as the polynomial `B(X) \in \Fq[X]`. + Let `\Fq[X]` be the function ring, write `\chi = T^2 - A(X)T + + B(X) \in \Fq[X][T]` for the characteristic polynomial of the + Frobenius endomorphism. The *Frobenius norm* is defined as the + polynomial `B(X) \in \Fq[X]`. Let `n` be the degree of the base ring over `\Fq`. Then the Frobenius norm has degree `n`. - OUTPUT: a polynomial in `\Fq[X]` + OUTPUT: an element in the function ring EXAMPLES: @@ -240,7 +249,7 @@ def frobenius_norm(self): sage: B (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 - sage: n = 2 # Degree of the base ring over `\Fq` + sage: n = 2 # Degree of the base ring over Fq sage: B.degree() == n True @@ -268,14 +277,15 @@ def frobenius_trace(self): Return Frobenius norm of the Drinfeld module, if the rank is two; raise a NotImplementedError otherwise. - Write `\chi = T^2 - A(X)T + B(X) \in \Fq[X][T]` to be the - characteristic polynomial of the Frobenius endomorphism. The - *Frobenius norm* is defined as the polynomial `B(X) \in \Fq[X]`. + Let `\Fq[X]` be the function ring, write `\chi = T^2 - A(X)T + + B(X) \in \Fq[X][T]` for the characteristic polynomial of the + Frobenius endomorphism. The *Frobenius norm* is defined as the + polynomial `B(X) \in \Fq[X]`. Let `n` be the degree of the base ring over `\Fq`. Then the Frobenius trace has degree `\leq \frac{n}{2}`. - OUTPUT: a polynomial in `\Fq[X]` + OUTPUT: an element in the function ring ALGORITHM: @@ -301,7 +311,7 @@ def frobenius_trace(self): sage: A (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 - sage: n = 2 # Degree of the base ring over `\Fq` + sage: n = 2 # Degree of the base ring over Fq sage: A.degree() <= n/2 True @@ -319,14 +329,17 @@ def frobenius_trace(self): def is_ordinary(self): r""" - Return True if the Drinfeld module is ordinary, return False - otherwise; raise a NotImplementedError if the rank is not two. + Return ``True`` whether the Drinfeld module is ordinary; raise a + NotImplementedError if the rank is not two. A rank two finite Drinfeld module is *ordinary* if and only if the `\Fq[X]-characteristic of the base ring does not devide the Frobenius trace. A *supersingular* rank two finite Drinfeld module is a Drinfeld module that is not ordinary. + A rnak two Drinfeld module is *ordinary* if and only if it is + note supersingular; see :meth:`is_supersingular`. + OUTPUT: a boolean EXAMPLES: @@ -355,13 +368,13 @@ def is_ordinary(self): def is_supersingular(self): r""" - Return True if the Drinfeld module is supersingular, return False - otherwise; raise a NotImplementedError if the rank is not two. + Return ``True`` whether the Drinfeld module is supersingular; raise a + NotImplementedError if the rank is not two. A rank two finite Drinfeld module is *supersingular* if and only - if the `\Fq[X]-characteristic of the base ring devides the - Frobenius trace. An *ordinary* rank two finite Drinfeld module - is a Drinfeld module that is not supersingular. + if the function field-characteristic of the base ring devides + the Frobenius trace. An *ordinary* rank two finite Drinfeld + module is a Drinfeld module that is not supersingular. OUTPUT: a boolean @@ -379,11 +392,11 @@ def is_supersingular(self): ALGORITHM: - Compute the Frobenius trace and test if the `\Fq[X]` - characteristic divides it. + Compute the Frobenius trace and test if the function + ring-characteristic divides it. - We could also test if the image of the - `\Fq[X]`-characteristic under the Drinfeld module is purely + We could also test if the image of the function + ring-characteristic under the Drinfeld module is purely inseparable; see [Gek1991]_, Proposition 4.1. """ self._check_rank_two() diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 61ad14914d1..67f7d5a1aba 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -32,8 +32,9 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, r""" This class represents a Drinfeld module morphism. - Let `\phi,\psi` be two Drinfeld modules defined over the - `\Fq[X]`-field `K`. A *morphism of Drinfeld modules `\phi \to \psi`* + Let `\phi, \psi` be two Drinfeld modules with function ring `\Fq[X]` + and base ring `K`, whose `\Fq[X]`-field structure is given by a + morphism `\gamma`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a \in \Fq[X]`. In our case, this is equivalent to verifying `f \phi_X = \psi_X f`. An *isogeny* is a non-zero @@ -234,7 +235,7 @@ def domain(self): def is_zero(self): r""" - Return the codomain of the morphism. + Return ``True`` whethere the morphism is the zero morphism. EXAMPLES: @@ -256,8 +257,7 @@ def is_zero(self): def is_endomorphism(self): r""" - Return True if the morphism is an endomorphism; return False - otherwise. + Return ``True`` whether the morphism is an endomorphism. EXAMPLES: @@ -287,8 +287,7 @@ def is_endomorphism(self): def is_isogeny(self): r""" - Return True if the morphism is an isogeny; return False - otherwise. + Return ``True`` whether the morphism is an isogeny. EXAMPLES: @@ -318,8 +317,7 @@ def is_isogeny(self): def is_isomorphism(self): r""" - Return True if the morphism is an isomorphism; return False - otherwise. + Return ``True`` whether the morphism is an isomorphism. EXAMPLES: From 8b890f4639cdf841a9e54ecea43be942b381c6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 23 Aug 2022 15:50:12 +0200 Subject: [PATCH 105/197] Change an exception text in DrinfeldModules --- .../function_field/drinfeld_modules/drinfeld_module.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 08b1309de86..e2641335987 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -133,12 +133,12 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: DrinfeldModule(FqX, [z, QQ(1)]) Traceback (most recent call last): ... - ValueError: function ring base must coerce to base ring + ValueError: function ring base must coerce into base ring sage: DrinfeldModule(FqX, [1, QQ(1)]) Traceback (most recent call last): ... - ValueError: function ring base must coerce to base ring + ValueError: function ring base must coerce into base ring If the coefficients are regular integers, an exception is raised. One needs to manually cast them to the field of their choice:: @@ -146,7 +146,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: DrinfeldModule(FqX, [1, 1, 1]) Traceback (most recent call last): ... - ValueError: function ring base must coerce to base ring + ValueError: function ring base must coerce into base ring sage: DrinfeldModule(FqX, [K(1), 1, 1]) Drinfeld module defined by X |--> t^2 + t + 1 over Finite Field in z of size 3^12 @@ -517,7 +517,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): # The coefficients are in a base ring that has coercion from Fq: if not (hasattr(ore_polring_base, 'has_coerce_map_from') and \ ore_polring_base.has_coerce_map_from(function_ring.base_ring())): - raise ValueError('function ring base must coerce to base ring') + raise ValueError('function ring base must coerce into base ring') # Build the morphism that defines the category gamma = function_ring.hom([ore_polring_base(gen[0])]) From 3f542d8d72e398e7e6110402a656243ee04dda9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 23 Aug 2022 15:51:05 +0200 Subject: [PATCH 106/197] Check morphism is non zero in DrinfeldModules This is equivalent to asking the constant coefficient to be non zero. Goss asks the function-ring characteristic to be either 0 or prime, hence this change. --- src/sage/categories/drinfeld_modules.py | 3 +++ .../function_field/drinfeld_modules/drinfeld_module.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 4a7c4e6b155..7117c3c2835 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -176,6 +176,9 @@ def __init__(self, morphism, name='t'): self._base = K if not K.is_field(): raise TypeError('base must be a field') + # Check morphism is non zero + if gamma(X).is_zero(): + raise ValueError('the morphism must be non zero') # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index e2641335987..ac6011e9006 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -126,6 +126,13 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): ... ValueError: generator must have positive degree + The constant coefficient mudt be non zero:: + + sage: DrinfeldModule(FqX, [K(0), K(1)]) + Traceback (most recent call last): + ... + ValueError: the morphism must be non zero + The coefficients of the generator must live in some base field `K` that is the codomain of a morphism `\gamma: \Fq[X] \to K`, where `\Fq[X]` is the function ring:: From fd9126ca9b005b7f5aaec033cd767769d57b6e2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 23 Aug 2022 16:32:19 +0200 Subject: [PATCH 107/197] Add various TESTS: in Drinfeld modules classes --- src/sage/categories/drinfeld_modules.py | 38 +++++++++- .../drinfeld_modules/drinfeld_module.py | 69 ++++++++++++++++++- 2 files changed, 103 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 7117c3c2835..ca4336b58d0 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -50,7 +50,7 @@ class DrinfeldModules(CategoryWithParameters): INPUT: a ring morphism from the function ring to the base ring - EXAMPLES: + .. RUBRIC:: Construction Generally, Drinfeld modules objects are created before their category, and the category is retrieved as an attribute of the @@ -152,13 +152,46 @@ class DrinfeldModules(CategoryWithParameters): True sage: rho.category() is cat True + + TESTS: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: from sage.categories.drinfeld_modules import DrinfeldModules + sage: gamma = Hom(FqX, K)(0) + sage: cat = DrinfeldModules(gamma) + Traceback (most recent call last): + ... + ValueError: the morphism must be non zero + + sage: gamma = Hom(FqX, FqX)(1) + sage: cat = DrinfeldModules(gamma) + Traceback (most recent call last): + ... + TypeError: base must be a field + + sage: gamma = 'I hate Rostropovitch' + sage: cat = DrinfeldModules(gamma) # known bug (blankline) + + Traceback (most recent call last): + ... + TypeError: input must be a ring morphism + + sage: ZZT. = ZZ[] + sage: gamma = Hom(ZZT, K)(1) + sage: cat = DrinfeldModules(gamma) # known bug (blankline) + + Traceback (most recent call last): + ... + TypeError: function ring base must be a finite field """ def __init__(self, morphism, name='t'): gamma = morphism # Check input is a ring Morphism if not isinstance(gamma, RingHomomorphism): - raise TypeError('category input must be a Ring morphism') + raise TypeError('input must be a Ring morphism') self._morphism = morphism self._function_ring = gamma.domain() # Check domain is Fq[X] @@ -246,7 +279,6 @@ def _latex_(self): sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: latex(cat) - sage: latex(cat) \text{Category{ }of{ }Drinfeld{ }modules{ }defined{ }by\begin{array}{l} \text{\texttt{Ring{ }morphism:}}\\ \text{\texttt{{ }{ }From:{ }Univariate{ }Polynomial{ }Ring{ }in{ }X{ }over{ }Finite{ }Field{ }of{ }size{ }11}}\\ diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index ac6011e9006..b152eaf5f16 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -488,6 +488,21 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: a = FqX.random_element() sage: phi.invert(phi(a)) == a True + + TESTS: + + sage: Fq = K = GF(2) + sage: FqX. = Fq[] + sage: phi = DrinfeldModule(FqX, [1, 1]) + Traceback (most recent call last): + ... + ValueError: function ring base must coerce into base ring + + sage: Fq = K = GF(2) + sage: FqX. = Fq[] + sage: phi = DrinfeldModule(FqX, [K(1), 1]) + sage: isinstance(phi.ore_polring(), OrePolynomialRing) + True """ @staticmethod @@ -567,6 +582,31 @@ def __call__(self, a): - ``a`` -- an element in the function ring OUTPUT: an element of the base ring + + TESTS: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + + sage: a = X^3 + 4*X + 2 + sage: phi(a) == phi(X)^3 + 4*phi(X) + 2 + True + sage: phi(a)[0] == p_root^3 + 4*p_root + 2 + True + + sage: phi(0) + 0 + sage: phi(1) + 1 + sage: phi(X) == phi._gen + True + + sage: a = FqX.random_element(5) + sage: phi(a)[0] == phi.category().morphism()(a) + True """ return self._morphism(a) @@ -574,6 +614,33 @@ def __call__(self, a): def __getitem__(self, n): r""" See method :meth:`coefficient`. + + TESTS: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi[0] == p_root + True + sage: phi[1] == z12^3 + True + sage: phi[2] == z12^5 + True + sage: phi[3] + Traceback (most recent call last): + ... + ValueError: input must be >= 0 and <= rank + sage: phi[-1] + Traceback (most recent call last): + ... + ValueError: input must be >= 0 and <= rank + sage: phi['I hate Dream Theater'] # known bug (blankline) + + Traceback (most recent call last): + ... + TypeErro: input must be an integer """ return self.coefficient(n) @@ -800,7 +867,7 @@ def change_ring(self, new_field, name=None): sage: phi_0.base_ring() is K0 True sage: phi.change_ring(K0).change_ring(K) # known bug - Traceback (most recent call last) + Traceback (most recent call last): ... TypeError: no coercion defined From a9152b7874836e39df5fe0f70b5e0c7009afc3cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 29 Aug 2022 13:58:43 +0200 Subject: [PATCH 108/197] Change validity test in DrinfeldModules method __call__ Before, the constant coefficient of the input was tested to be a root of the characteristic. I changed this test to the following: we check that the constant coefficient is the image (gamma(X)) of the morphism (gamma) that defines the category. --- src/sage/categories/drinfeld_modules.py | 9 ++++++--- .../function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index ca4336b58d0..bb54c1d8c14 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -140,7 +140,7 @@ class DrinfeldModules(CategoryWithParameters): sage: cat([z, 1]) Traceback (most recent call last): ... - ValueError: constant coefficient must be a root of the characteristic + ValueError: constant coefficient must be the generator of the morphism that defines the category It is also possible to create a random object in the category, with a given rank:: @@ -256,8 +256,11 @@ def _call_(self, gen): from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule # If gen is not in the Ore polring, an exception is raised gen = self._ore_polring(gen) - if self.characteristic()(gen[0]) != 0: - raise ValueError('constant coefficient must be a root of the characteristic') + X = self._function_ring.gen() + gamma = self._morphism + if gen[0] != gamma(X): + raise ValueError('constant coefficient must be the generator ' \ + 'of the morphism that defines the category') return DrinfeldModule(self._function_ring, gen) # Somehow required for the class definition diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index b152eaf5f16..ff54ef4b656 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -183,7 +183,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: cat([1, 1, K(1)]) Traceback (most recent call last): ... - ValueError: constant coefficient must be a root of the characteristic + ValueError: constant coefficient must be the generator of the morphism that defines the category .. NOTE:: @@ -922,7 +922,7 @@ def constant_coefficient(self): sage: rho = cat(phi.constant_coefficient() + 1 + t^3) Traceback (most recent call last): ... - ValueError: constant coefficient must be a root of the characteristic + ValueError: constant coefficient must be the generator of the morphism that defines the category One can also retrieve the constant coefficient using ``phi[0]`:: From 52cb56719bdf0fd54e50424e394cee48ed19f316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 29 Aug 2022 14:12:39 +0200 Subject: [PATCH 109/197] Remove method __getitem__ in DrinfeldModule After introducing this method a few days ago, I realize that this syntax is a conflict with the more traditional syntax phi[a] for the a-torsion of phi. --- .../drinfeld_modules/drinfeld_module.py | 49 ++----------------- 1 file changed, 3 insertions(+), 46 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index ff54ef4b656..28d88faa6a4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -345,11 +345,6 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: phi.coefficient(1) 1 - The ``[...]`` was introduced as a shortcut to ``phi(X)[...]``:: - - sage: phi[1] - 1 - .. RUBRIC:: Morphisms, isogenies A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore @@ -611,39 +606,6 @@ def __call__(self, a): return self._morphism(a) - def __getitem__(self, n): - r""" - See method :meth:`coefficient`. - - TESTS: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi[0] == p_root - True - sage: phi[1] == z12^3 - True - sage: phi[2] == z12^5 - True - sage: phi[3] - Traceback (most recent call last): - ... - ValueError: input must be >= 0 and <= rank - sage: phi[-1] - Traceback (most recent call last): - ... - ValueError: input must be >= 0 and <= rank - sage: phi['I hate Dream Theater'] # known bug (blankline) - - Traceback (most recent call last): - ... - TypeErro: input must be an integer - """ - return self.coefficient(n) - def _Hom_(self, other, category): r""" Return ``DrinfeldModuleHomset(self, other, category)``. @@ -923,13 +885,8 @@ def constant_coefficient(self): Traceback (most recent call last): ... ValueError: constant coefficient must be the generator of the morphism that defines the category - - One can also retrieve the constant coefficient using ``phi[0]`:: - - sage: phi.constant_coefficient() == phi[0] - True """ - return self[0] + return self.coefficient(0) def coefficient(self, n): r""" @@ -1226,8 +1183,8 @@ def j_invariant(self): NotImplementedError: rank must be 2 """ self._check_rank_two() - g = self[1] - delta = self[2] + g = self.coefficient(1) + delta = self.coefficient(2) q = self._Fq.order() return (g**(q+1)) / delta From 0f3d19ce271008ba521496bdf3db633831ca093a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 29 Aug 2022 15:00:57 +0200 Subject: [PATCH 110/197] Proofread docs in Drinfeld module classes --- src/sage/categories/drinfeld_modules.py | 36 ++- .../function_field/drinfeld_modules/action.py | 19 +- .../drinfeld_modules/drinfeld_module.py | 281 ++++++++---------- .../finite_drinfeld_module.py | 45 ++- .../function_field/drinfeld_modules/homset.py | 7 +- .../drinfeld_modules/morphism.py | 22 +- 6 files changed, 191 insertions(+), 219 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index bb54c1d8c14..f9d854de1d4 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -37,16 +37,22 @@ class DrinfeldModules(CategoryWithParameters): its function ring `\Fq[X]` and its base ring `K`. The `\Fq[X]`-field structure on `K` is given by a morphism `\gamma` - We say that `\Fq[X]` is the *function ring of the category*; `K` is the - *base of the category*, or simply its base ring or base field; `\Fq[X]` is - the *function ring of the category*; *K\{\tau\}* is the *Ore - polynomial ring of the category*; - `t` is the *Ore variable of the category*. The *constant coefficient - of the category* is `\gamma(X)`. + .. NOTE:: + + These notations will be used throughout this documentation. + + We say that `\Fq[X]` is the *function ring of the category*; `K` is + the *base of the category* (or simply its *base ring* or *base + field*); `\Fq[X]` is the *function ring of the category*; + *K\{\tau\}* is the *Ore polynomial ring of the category*; `t` is the + *Ore variable of the category*. The *constant coefficient of the + category* is `\gamma(X)`. The `\Fq[X]`-characteristic of the base + ring `K` can also be referred to as its *function + ring-characteristic*. .. NOTE:: - These notations will be used throughout this docstring. + The base is always a field. INPUT: a ring morphism from the function ring to the base ring @@ -107,6 +113,8 @@ class DrinfeldModules(CategoryWithParameters): Finite Field in z of size 11^4 sage: cat.base() is K True + sage: cat.base() is phi.base_ring() + True And the *function ring* is the polynomial ring `\Fq[X]`:: @@ -114,6 +122,8 @@ class DrinfeldModules(CategoryWithParameters): Univariate Polynomial Ring in X over Finite Field of size 11 sage: cat.function_ring() is FqX True + sage: cat.function_ring() is phi.function_ring() + True And as expected, the *Ore polynomial ring* is that of the Drinfeld modules in the category: @@ -142,8 +152,8 @@ class DrinfeldModules(CategoryWithParameters): ... ValueError: constant coefficient must be the generator of the morphism that defines the category - It is also possible to create a random object in the category, with - a given rank:: + It is also possible to create a random object in the category. The + input is the desired rank:: sage: rho = cat.random_object(2) sage: rho # random @@ -230,8 +240,8 @@ def __init__(self, morphism, name='t'): def _call_(self, gen): r""" - Return a Drinfeld module object, in the category, whose - generator is the input. + Return a Drinfeld module object in the category whose generator + is the input. INPUT: the generator of the Drinfeld module, given as an Ore polynomial or a list of coefficients @@ -376,7 +386,7 @@ def constant_coefficient(self): r""" Return the constant coefficient of the category. - OUTPUT: an element in the base + OUTPUT: a base element EXAMPLES: @@ -397,7 +407,7 @@ def function_ring(self): r""" Return the function ring of the category. - OUTPUT: the ring `\Fq[X]` + OUTPUT: a univariate polynomial ring EXAMPLES: diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 535474c3f57..0e5edfb74e6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -26,7 +26,6 @@ class DrinfeldModuleAction(Action): r""" - This class represents the module action induced by a Drinfeld module. @@ -34,10 +33,14 @@ class DrinfeldModuleAction(Action): ring `K`, whose `\Fq[X]`-field structure is given by a morphism `\gamma`. Let `L/K` be a field extension, let `x \in L`, let `a` be a function ring element; the action is defined as `(a, x) \mapsto - \phi_a(x)`. In this implementation, `L` is `K`. + \phi_a(x)`. + + .. NOTE:: + + In this implementation, `L` is `K`. - The action is instanciated as follows. Note that the user should - never explicitely instanciate the class `DrinfeldModuleAction`:: + The action is instantiated as follows. Note that the user should + never explicitly instantiate the class `DrinfeldModuleAction`:: INPUT: a Drinfeld module @@ -86,14 +89,14 @@ def __init__(self, drinfeld_module): def _act_(self, pol, x): r""" - Return ``pol * x``, where ``*`` is the action. + Return the action of ``pol`` on ``x``. INPUT: - ``pol`` -- a function ring element - - ``x`` -- an element in the base ring of the Drinfeld module + - ``x`` -- a base ring element - OUTPUT: an element in the base ring of the Drinfeld module. + OUTPUT: an element in the base ring of the Drinfeld module EXAMPLES: @@ -158,7 +161,7 @@ def _repr_(self): def drinfeld_module(self): r""" - Return the Drinfeld module associated to the action. + Return the Drinfeld module defining to the action. OUTPUT: a Drinfeld module diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 28d88faa6a4..8e9df953a43 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -3,6 +3,7 @@ This module provides the class :class:`sage.rings.function_field.drinfeld_module.drinfeld_module.DrinfeldModule`. + For *finite* Drinfeld modules and their theory of complex multiplication, see class :class:`sage.rings.function_field.drinfeld_module.finite_drinfeld_module.DrinfeldModule`. @@ -23,35 +24,33 @@ # http://www.gnu.org/licenses/ #***************************************************************************** -from sage.rings.integer import Integer -from sage.structure.unique_representation import UniqueRepresentation -from sage.structure.category_object import CategoryObject - from sage.categories.drinfeld_modules import DrinfeldModules +from sage.matrix.constructor import Matrix +from sage.misc.latex import latex +from sage.modules.free_module_element import vector +from sage.rings.integer import Integer from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing from sage.rings.polynomial.polynomial_ring import PolynomialRing_general - -from sage.misc.latex import latex +from sage.structure.category_object import CategoryObject from sage.structure.sequence import Sequence -from sage.matrix.constructor import Matrix -from sage.modules.free_module_element import vector +from sage.structure.unique_representation import UniqueRepresentation + class DrinfeldModule(UniqueRepresentation, CategoryObject): r""" This class represents a Drinfeld module. - Let `\Fq` be a finite field with order `q`. Let `K` be a field - equiped a ring morphism `\gamma: \Fq[X] \to K` --- the field `K` is - said to be an *`\Fq[X]`-field*, and the monic polynomial that - generates `\Ker(\gamma)` is called the *`\Fq[X]`-characteristic of - the `\Fq[X]`-field `K`*. This characteristic plays the role of the - characteristic of a function field. + Let `\Fq` be a finite field with order `q` and let `K` be a field + equipped a ring morphism `\gamma: \Fq[X] \to K`. The field `K` is + called an *`\Fq[X]`-field*, and the monic generator of + `\Ker(\gamma)` is called the *`\Fq[X]`-characteristic of the + `\Fq[X]`-field `K`*. Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in `K` and Frobenius variable `\tau: x \mapsto x^q`. A *Drinfeld - `\Fq[X]`-module over the `\Fq[X]`-field `K`* is a ring morphism - `\phi: \Fq[X] \to K\{\tau\}` such that: + `\Fq[X]`-module over the `\Fq[X]`-field `K`* is an `\Fq`-algebra + morphism `\phi: \Fq[X] \to K\{\tau\}` such that: 1. The image of `\phi` contains non-constant Ore polynomials. 2. For every element `a` in the function ring, the constant @@ -61,19 +60,22 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. NOTE:: - These notations will be used throughout this docstring. + These notations will be used throughout the documentation. We say that `\Fq[X]` is the *function ring of `\phi`*; `K` is the - *base ring of `\phi`*, or simply its base or base field; *K\{\tau\}* - is the *Ore polynomial ring of `\phi`*; `t` is the *Ore variable of - `\phi`*. The *generator of `\phi`* is `\phi_X`, its *constant - coefficient* is the constant coefficient of `\phi_X`. The - `\Fq[X]`-characteristic of the base ring `K` can also be referred to - as its *function ring-characteristic*. + *base ring of `\phi`* (or simply its *base* or *base field*); + *K\{\tau\}* is the *Ore polynomial ring of `\phi`*; `t` is the *Ore + variable of `\phi`*. Further, the *generator of `\phi`* is `\phi_X` + and its *constant coefficient* is the constant coefficient of + `\phi_X`. The `\Fq[X]`-characteristic of the base ring `K` can also + be referred to as its *function ring-characteristic*. + + .. NOTE:: + + The base ring is always a field. The Drinfeld module `\phi` is uniquely determined by the image - `\phi_X` of `X`. This Ore polynomial is an input of the class - constructor. + `\phi_X` of `X`, which is an input of the class. Classical references on Drinfeld modules include [Gos1998]_, [Rosen2002]_, [VS06]_ and [Gek1998]_. @@ -84,18 +86,18 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): polynomial ring `\Fq[X]` is replaced by a more general function ring: the ring of functions in `k` that are regular outside `\infty`, where `k` is a function field over `\Fq` with - transcendance degree `1` and `\infty` is a fixed place of `k`. + transcendence degree `1` and `\infty` is a fixed place of `k`. This is out of the scope of this implementation. INPUT: - - ``function_ring`` -- the polynomial ring with coefficients in the finite - field `\Fq` - - ``gen`` -- the generator `\phi_X`, as a list of coefficients or an - Ore polynomial - - ``name`` (optional) the name of the Ore variable + - ``function_ring`` -- a univariate polynomial ring whose base is a + finite field + - ``gen`` -- the generator of the Drinfeld module; as a list of + coefficients or an Ore polynomial + - ``name`` (optional) -- the name of the Ore variable - .. RUBRIC:: Construction and input + .. RUBRIC:: Construction A Drinfeld module object is constructed as follows:: @@ -126,16 +128,15 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): ... ValueError: generator must have positive degree - The constant coefficient mudt be non zero:: + The constant coefficient must be non zero:: sage: DrinfeldModule(FqX, [K(0), K(1)]) Traceback (most recent call last): ... ValueError: the morphism must be non zero - The coefficients of the generator must live in some base field `K` - that is the codomain of a morphism `\gamma: \Fq[X] \to K`, where - `\Fq[X]` is the function ring:: + The coefficients of the generator must lie in an `\Fq[X]`-field, + where `\Fq[X]` is the function ring of the Drinfeld module:: sage: DrinfeldModule(FqX, [z, QQ(1)]) Traceback (most recent call last): @@ -147,22 +148,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): ... ValueError: function ring base must coerce into base ring - If the coefficients are regular integers, an exception is raised. - One needs to manually cast them to the field of their choice:: - - sage: DrinfeldModule(FqX, [1, 1, 1]) - Traceback (most recent call last): - ... - ValueError: function ring base must coerce into base ring - - sage: DrinfeldModule(FqX, [K(1), 1, 1]) - Drinfeld module defined by X |--> t^2 + t + 1 over Finite Field in z of size 3^12 - - sage: DrinfeldModule(FqX, [Fq(1), 1, 1]) - Drinfeld module defined by X |--> t^2 + t + 1 over Finite Field in z2 of size 3^2 - The function ring must be an univariate polynomial ring whose - coefficients lie in a finite field:: + base is a finite field:: sage: DrinfeldModule(K, [z, 1, 1]) Traceback (most recent call last): @@ -175,9 +162,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): ... TypeError: function ring base must be a finite field - If you already defined a category of Drinfeld modules, you must - ensure that the constant coefficient is a root of the - function ring-characteristic of the category base:: + If you already defined a category of Drinfeld modules, and you + create a Drinfeld module through this category, you must + ensure that the constant coefficient is the generator of the algebra + morphism that defines the category:: sage: cat = phi.category() sage: cat([1, 1, K(1)]) @@ -193,13 +181,13 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): `\Fq[X]`-characteristic plays the role of the characteristic of a function field, and thus preexists Drinfeld modules. The base field `K` should rather be seen as an `\Fq[X]`-field, i.e. the - field `K` equiped with a morphism `\gamma: \Fq[X] \to K`, + field `K` equipped with a morphism `\gamma: \Fq[X] \to K`, instead of just a field. However, as the characteristic may be deduced from the constant - coefficient of the Drinfeld module, we chose to ommit the - characteristic in the input of the class in order to have - concise definitions. + coefficient of the Drinfeld module (it is its minimal polynomial + over the function ring), we chose to ommit the characteristic + in the input of the class in order to have concise definitions. .. RUBRIC:: Possible base rings @@ -216,9 +204,9 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: rho Drinfeld module defined by X |--> t^2 + t + z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z over Finite Field in z of size 3^12 - Then one can check that the morphisms `\gamma` are not the same for - ``phi`` and ``rho``, and that the `\gamma` associated to `\phi` is - surjective, while the other one is not:: + The morphisms `\gamma` are not the same for ``phi`` and ``rho``, and + that the `\gamma` associated to `\phi` is surjective, while the + other one is not:: sage: rho.category().morphism() Ring morphism: @@ -270,12 +258,33 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: char = phi.category().characteristic() - As the output of - :meth:`sage.rings.function_field.drinfeld_module.finite_drinfeld_module.DrinfeldModule.category` - suggests, the morphism `\gamma` uniquely determines the category of a Drinfeld - module. + As the output of :meth:`category` suggests, the morphism `\gamma` + uniquely determines the category. + + .. RUBRIC:: Basics - .. RUBRIC:: Getters and basic properties + Images under the Drinfeld module are computed by calling the object:: + + sage: phi(X) # phi_X + t^2 + t + z + sage: phi(X^3 + X + 1) # phi_X^3 +X + 1 + t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 + sage: phi(1) # phi_1 + 1 + + This is useful to quickly retrieve the generator of the Drinfeld + module. Furthermore, a Drinfeld `\Fq[X]`-module can be seen as an + Ore polynomial with positive degree and constant coefficient + `\gamma(X)`. This analogy is the motivation for the following + methods:: + + sage: phi.coefficients() + [z, 1, 1] + + sage: phi.coefficient(1) + 1 + sage: phi.coefficient(1) + 1 One can retrieve basic properties:: @@ -305,9 +314,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) Defn: X |--> t^2 + t + z - .. RUBRIC:: Height, rank and j-invariant - - One can compute the rank and height:: + One can compute the rank and height (which is always `1`):: sage: phi.rank() 2 @@ -319,39 +326,12 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: phi.j_invariant() # j-invariant 1 - .. RUBRIC:: The generator of a Drinfeld module - - For a `a` in the function ring, `\phi_a` is computed by calling - `phi`:: - - sage: phi(X) # phi_X - t^2 + t + z - sage: phi(X^3 + X + 1) # phi_X^3 +X + 1 - t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 - sage: phi(1) # phi_1 - 1 - - This is especially useful to quickly retrive `\phi_X`, the generator - of the Drinfeld module. Furthermore, a Drinfeld `\Fq[X]`-module can - be seen as an Ore polynomial with positive degree and constant - coefficient `\gamma(X)`. This analogy is the motivation for the - following methods:: - - sage: phi.coefficients() - [z, 1, 1] - - sage: phi.coefficient(1) - 1 - sage: phi.coefficient(1) - 1 - .. RUBRIC:: Morphisms, isogenies A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for - every `a` in the function ring. In our specific case, this is - equivalent to verifying `f \phi_X = \psi_X f`. An *isogeny* is a - non-zero morphism. + every `a` in the function ring. In our case, this is equivalent to + `f \phi_X = \psi_X f`. An *isogeny* is a non-zero morphism. Use the ``in`` syntax to test if an Ore polynomial defines a morphism:: @@ -370,7 +350,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): True To create a SageMath object representing the morphism, call the - homset (``hom`` in the next example):: + homset (``hom``):: sage: hom = Hom(phi, phi) sage: frobenius_endomorphism = hom(t^6) @@ -392,15 +372,16 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 Defn: 0 - One can retrieve the underlying Ore polynomial with the method - :meth:`sage.rings.function_field.drinfeld_module.finite_drinfeld_module.DrinfeldModule.ore_polynomial`:: + The underlying Ore polynomial is retrieved with the method + :meth:`ore_polynomial`:: sage: frobenius_endomorphism.ore_polynomial() t^6 + sage: identity_morphism.ore_polynomial() + 1 - And one can easily check if a morphism defines an isogeny or an - isomorphism (i.e. an isogeny whose underlying Ore polynomial has - degree `0`):: + It is easy to check if a morphism is an isogeny, endomorphism or + isomorphism:: sage: frobenius_endomorphism.is_isogeny() True @@ -417,10 +398,9 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: The Vélu formula - Let ``ore_pol`` be a non-zero Ore polynomial. For Drinfeld module, - it is easy to decide if there exists a Drinfeld module ``psi`` such - that ``ore_pol`` is an isogeny from ``self`` to ``psi``. If so, we - find ``psi``:: + Let ``ore_pol`` be a non-zero Ore polynomial. We can decide if there + exists a Drinfeld module ``psi`` such that ``ore_pol`` is an isogeny + from ``self`` to ``psi``. If so, we find ``psi``:: sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z sage: psi = phi.velu(ore_pol) @@ -444,18 +424,21 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. RUBRIC:: The action of a Drinfeld module - An `\Fq[X]`-Drinfeld module `\phi` notoriously makes any field - extension `L/K` a left `\Fq[X]`-module. Let `x \in L`, let `a` be in - the function ring; the action is defined as `(a, x) \mapsto - \phi_a(x)`. The method :meth:`action` returns an ``Action`` object - representing the Drinfeld module action; in this implementation, `K - = L`. + The `\Fq[X]`-Drinfeld module `\phi` induces a special left + `\Fq[X]`-module structure on any field extension `L/K`. Let `x \in + L` and `a` be in the function ring; the action is defined as `(a, + x) \mapsto \phi_a(x)`. The method :meth:`action` returns an + ``Action`` object representing the Drinfeld module action. + + .. NOTE:: + + In this implementation, `L` is `L`. sage: action = phi.action() sage: action Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - The action on elements is computed as follows:: + The action on elements is computed by calling the action object:: sage: P = X + 1 sage: a = z @@ -474,7 +457,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: extended_action Action on Finite Field in z60 of size 3^60 induced by Drinfeld module defined by X |--> t^2 + t + 2*z60^59 + z60^56 + 2*z60^55 + 2*z60^54 + 2*z60^53 + z60^49 + z60^48 + z60^47 + 2*z60^45 + z60^44 + 2*z60^41 + 2*z60^40 + 2*z60^39 + 2*z60^37 + 2*z60^36 + z60^34 + z60^33 + z60^32 + 2*z60^31 + 2*z60^30 + 2*z60^27 + 2*z60^25 + z60^23 + z60^22 + z60^21 + 2*z60^20 + z60^19 + z60^18 + z60^17 + z60^16 + z60^15 + 2*z60^14 + z60^12 + 2*z60^11 + 2*z60^10 + z60^8 + z60^6 + 2*z60^5 + z60^4 + z60^3 + z60 + 1 over Finite Field in z60 of size 3^60 - .. RUBRIC:: Inversion of the Drinfeld module + .. RUBRIC:: Inverting the Drinfeld module Given an Ore polynomial that equals `\phi_a` for some function ring elelement `a`, one can retrieve `a` (as a morphism, a Drinfeld @@ -569,14 +552,14 @@ def __init__(self, gen, category): def __call__(self, a): r""" Return the image of ``a`` by the morphism that defines the - Drinfeld module, i.e. `\phi_a` if the Drinfeld module is denoted + Drinfeld module; i.e. `\phi_a` if the Drinfeld module is denoted `phi`. INPUT: - - ``a`` -- an element in the function ring + - ``a`` -- a function ring element - OUTPUT: an element of the base ring + OUTPUT: a base ring element TESTS: @@ -603,7 +586,6 @@ def __call__(self, a): sage: phi(a)[0] == phi.category().morphism()(a) True """ - return self._morphism(a) def _Hom_(self, other, category): @@ -691,7 +673,9 @@ def _repr_(self): def action(self): r""" - Return the action object that represents the action on the base that is + Return the action object + (:class:`sage.rings.function_field.drinfeld_modules.action.Action`) + that represents the module action, on the base ring, that is induced by the Drinfeld module. OUTPUT: a Drinfeld module action object @@ -725,9 +709,6 @@ def base_ring(self): r""" Return the base ring of the Drinfeld module. - This is the Ore polynomial ring base. In particular, the base - ring is always a field. - OUTPUT: a field EXAMPLES: @@ -747,9 +728,13 @@ def base_ring(self): sage: phi.base_ring() is K True - Note that in the above example, the constant coefficient - generates a strict sub-extension of `K/\Fq`. In fact, the base - ring may also be the same as ``Fq``:: + The base ring can be infinite:: + + sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) + sage: sigma.base_ring() + Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + + Or it can be ``Fq``:: sage: psi = DrinfeldModule(FqX, [Fq(1), Fq.gen()]) sage: psi.base_ring() @@ -778,12 +763,11 @@ def base_ring(self): def change_ring(self, new_field, name=None): r""" - Return a Drinfeld module defined like the current one, but with + Return a Drinfeld module defined like the current one, but whose base ring ``new_field``. - The new base can either be a field extension of the base ring, - or field that has a coercion map from the field of definitions - of the coefficients of the generator. + The new base is valid whether it has a coercion map from the + current base. INPUT: @@ -848,7 +832,7 @@ def constant_coefficient(self): r""" Return the constant coefficient of the generator. - OUTPUT: an element in the base ring + OUTPUT: a base ring element EXAMPLES: @@ -896,7 +880,7 @@ def coefficient(self, n): - ``n`` -- a non-negative integer - OUTPUT: an element in the base ring + OUTPUT: a base ring element EXAMPLES: @@ -928,13 +912,14 @@ def coefficients(self, sparse=True): r""" Return the coefficients of the generator, as a list. - If the the flag ``sparse`` is ``True`` (default), only return the + If the flag ``sparse`` is ``True`` (default), only return the non-zero coefficients; otherwise, return all of them. INPUT: + - ``sparse`` -- a boolean - OUTPUT: a list of elements in the base ring + OUTPUT: a list of base ring elements EXAMPLES: @@ -968,10 +953,7 @@ def function_ring(self): r""" Return the function ring of the Drinfeld module. - In our case, the function ring is an univariate polynomial ring - whose coefficients lie in a finite field `\Fq`. - - OUTPUT: a polynomial ring + OUTPUT: a univariate polynomial ring EXAMPLES: @@ -989,12 +971,6 @@ def gen(self): r""" Return the generator of the Drinfeld module. - Let `\phi` denote the Drinfeld module, and `\Fq[X]` be the - function ring. This method returns `\phi_X`. - - This method makes sense the function ring is `\Fq`-generated by - a single element, whose image characterizes the Drinfeld module. - OUTPUT: an Ore polynomial EXAMPLES: @@ -1040,7 +1016,7 @@ def invert(self, ore_pol): - ``ore_pol`` -- the Ore polynomial whose preimage we want to compute - OUTPUT: an element in the function ring + OUTPUT: a function ring element EXAMPLES: @@ -1145,9 +1121,8 @@ def is_finite(self): def j_invariant(self): r""" - Return the j-invariant of the Drinfeld module; only the rank two - case has been implemented, a NotImplementedError is raised if - the rank is not two. + Return the j-invariant of the Drinfeld module if the rank is + two; raise a NotImplementedError otherwise. Assume the rank is two. Write the generator `\phi_X = \gamma(X) + g\tau + \Delta\tau^2`. The j-invariant is defined by @@ -1155,8 +1130,7 @@ def j_invariant(self): of the function ring. In our case, this base field is always finite. - OUTPUT: an element in the base ring if the rank is two; an - exception is raised otherwise + OUTPUT: a base ring element EXAMPLES: @@ -1192,7 +1166,7 @@ def morphism(self): r""" Return the morphism object that defines the Drinfeld module. - OUTPUT: a ring morphism, from the function ring to the Ore + OUTPUT: a ring morphism from the function ring to the Ore polynomial ring EXAMPLES: @@ -1278,9 +1252,6 @@ def ore_variable(self): r""" Return the Ore variable. - The Ore variable is defined as the generator of the Ore - polynomial ring. - OUTPUT: an Ore polynomial EXAMPLES: diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index c7cd5c6d2f5..ce87148eb59 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -26,22 +26,20 @@ class FiniteDrinfeldModule(DrinfeldModule): r""" - This class represnets a finite Drinfeld module. + This class represents a finite Drinfeld module. A *finite Drinfeld module* is a Drinfeld module whose base ring is - finite. For general definitions and help on Drinfeld modules, see - class + finite. + + For general definitions and help on Drinfeld modules, see class :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. - In this specific documentation, we only present the specifics of - ``FiniteDrinfeldModle``. .. RUBRIC:: Construction: The user does not ever need to directly call - ``FiniteDrinfeldModule``, as it is the (meta)class - ``DrinfeldModule`` that is reponsible for instanciating - ``DrinfeldModule`` or ``FiniteDrinfeldModule`` depending on its - input:: + ``FiniteDrinfeldModule`` --- the metaclass ``DrinfeldModule`` is + responsible for instantiating ``DrinfeldModule`` or + ``FiniteDrinfeldModule`` depending on the input:: sage: Fq = GF(343) sage: FqX. = Fq[] @@ -113,9 +111,8 @@ def __init__(self, gen, category): def frobenius_endomorphism(self): r""" - Return the Frobenius endomorphism, as an instance of - ``DrinfeldModuleMorphism``, of the Drinfeld module, if the rank - is two; raise a NotImplementedError otherwise.. + Return the Frobenius endomorphism of the Drinfeld module as a + morphism object. Let `q` be the order of the base field of the function ring. The *Frobenius endomorphism* is defined as the endomorphism whose @@ -209,12 +206,9 @@ def frobenius_charpoly(self, var='T'): trace. This gives the Frobenius characteristic polynomial. See [SM2019]_, Section 4. - See docstrings of methods - :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.FiniteDrinfeldModule.frobenius_norm` - and - :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.FiniteDrinfeldModule.frobenius_trace` - for furthere details on the computation of the norm and of - the trace. + See docstrings of methods :meth:`frobenius_norm` and + :meth:`frobenius_trace` for furthere details on the + computation of the norm and of the trace. """ self._check_rank_two() A = self._function_ring # Fq[X] @@ -291,12 +285,11 @@ def frobenius_trace(self): Let `A(X)` denote the Frobenius trace and `B(X)` denote the Frobenius norm. We begin by computing `B(X)`, see docstring - of method - :meth:`sage.rings.function_fields.drinfeld_module.finite_drinfeld_module.FiniteDrinfeldModule.frobenius_norm` - for details. The characteristic polynomial of the Frobenius - yields `t^{2n} - \phi_A t^n + \phi_B = 0`, where `t^n` is - the Frobenius endomorphism. As `\phi_B` is now known, we can - compute `\phi_A = (t^{2n} + \phi_B) / t^n`. We get `A(X)` by + of method :meth:`frobenius_norm` for details. The + characteristic polynomial of the Frobenius yields `t^{2n} - + \phi_A t^n + \phi_B = 0`, where `t^n` is the Frobenius + endomorphism. As `\phi_B` is now known, we can compute + `\phi_A = (t^{2n} + \phi_B) / t^n`. We get `A(X)` by inverting this quantity, using the method :meth:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule.invert`, see its docstring for details. @@ -333,7 +326,7 @@ def is_ordinary(self): NotImplementedError if the rank is not two. A rank two finite Drinfeld module is *ordinary* if and only if - the `\Fq[X]-characteristic of the base ring does not devide the + the `\Fq[X]-characteristic of the base ring does not divide the Frobenius trace. A *supersingular* rank two finite Drinfeld module is a Drinfeld module that is not ordinary. @@ -372,7 +365,7 @@ def is_supersingular(self): NotImplementedError if the rank is not two. A rank two finite Drinfeld module is *supersingular* if and only - if the function field-characteristic of the base ring devides + if the function field-characteristic of the base ring divides the Frobenius trace. An *ordinary* rank two finite Drinfeld module is a Drinfeld module that is not supersingular. diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 8ec98e7562f..b442ec9fe0d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -176,8 +176,7 @@ def _repr_(self): def __contains__(self, x): r""" Implement the ``in`` operator for the homset; return ``True`` if - the input defines a morphism in the homset, return ``False`` - otherwise. + whether the input defines a morphism in the homset. INPUT: @@ -233,8 +232,8 @@ def __contains__(self, x): def _element_constructor_(self, *args, **kwds): r""" - Return a Drinfeld module morphism defined by the input, which is - an Ore polynomial. + Return the Drinfeld module morphism defined by the input Ore + polynomial. INPUT: an Ore polynomial diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 67f7d5a1aba..f5f274f51c2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -36,16 +36,12 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, and base ring `K`, whose `\Fq[X]`-field structure is given by a morphism `\gamma`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a - f` for every `a \in \Fq[X]`. In our case, this is equivalent to - verifying `f \phi_X = \psi_X f`. An *isogeny* is a non-zero - morphism. + f` for every `a \in \Fq[X]`. In our case, this is equivalent to `f + \phi_X = \psi_X f`. An *isogeny* is a non-zero morphism. - A Drinfeld module morphism is represented by instances of the class - `DrinfeldModuleMorphism`. - - To create a morphism object, do not explicitely use - `DrinfeldModuleMorphism`, but rather call the parent homset with the - defining Ore polynomial:: + To create a morphism object, the user should never explicitly + instantiate `DrinfeldModuleMorphism`, but rather call the parent + homset with the defining Ore polynomial:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -101,9 +97,9 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, .. NOTE:: - For the sake of completness, we explain how the user can - directly instanciate the class, even though this should never be - explicitely done:: + For the sake of completeness, we explain how the user can + directly instantiate the class, even though this should never be + explicitly done:: sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) @@ -235,7 +231,7 @@ def domain(self): def is_zero(self): r""" - Return ``True`` whethere the morphism is the zero morphism. + Return ``True`` whether the morphism is the zero morphism. EXAMPLES: From ea4a843f7832515cd008587e5f1747624f8bee2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 29 Aug 2022 17:31:14 +0200 Subject: [PATCH 111/197] Lint Drinfeld modules code --- src/sage/categories/drinfeld_modules.py | 16 +++--- .../function_field/drinfeld_modules/action.py | 10 ++-- .../drinfeld_modules/drinfeld_module.py | 50 +++++++++---------- .../finite_drinfeld_module.py | 4 +- .../function_field/drinfeld_modules/homset.py | 16 +++--- .../drinfeld_modules/morphism.py | 22 ++++---- 6 files changed, 62 insertions(+), 56 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index f9d854de1d4..4d4f851fc31 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -207,9 +207,11 @@ def __init__(self, morphism, name='t'): # Check domain is Fq[X] function_ring = self._function_ring if not isinstance(function_ring, PolynomialRing_general): - raise NotImplementedError('function ring must be a polynomial ring') + raise NotImplementedError('function ring must be a polynomial ' + 'ring') function_ring_base = function_ring.base_ring() - if not function_ring_base.is_field() or not function_ring_base.is_finite() : + if not function_ring_base.is_field() \ + or not function_ring_base.is_finite(): raise TypeError('function ring base must be a finite field') Fq = function_ring_base FqX = function_ring @@ -226,7 +228,7 @@ def __init__(self, morphism, name='t'): d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) self._ore_polring = OrePolynomialRing(K, tau, names=name, - polcast=False) + polcast=False) # Create constant coefficient self._constant_coefficient = gamma(X) # Create characteristic @@ -269,8 +271,8 @@ def _call_(self, gen): X = self._function_ring.gen() gamma = self._morphism if gen[0] != gamma(X): - raise ValueError('constant coefficient must be the generator ' \ - 'of the morphism that defines the category') + raise ValueError('constant coefficient must be the generator ' + 'of the morphism that defines the category') return DrinfeldModule(self._function_ring, gen) # Somehow required for the class definition @@ -300,7 +302,7 @@ def _latex_(self): \end{array} """ return f'\\text{{Category{{ }}of{{ }}Drinfeld{{ }}modules{{ }}' \ - f'defined{{ }}by{latex(self._morphism)}' + f'defined{{ }}by{latex(self._morphism)}' def _repr_(self): r""" @@ -380,7 +382,7 @@ def characteristic(self): """ if self._characteristic is None: raise NotImplementedError - return self._characteristic + return self._characteristic def constant_coefficient(self): r""" diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 0e5edfb74e6..28b585cd5dc 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -1,5 +1,5 @@ r""" -The left-module action induced by a Drinfeld module +The module action induced by a Drinfeld module This module provides the class :class:`sage.rings.function_field.drinfeld_module.action.DrinfeldModuleAction`. @@ -85,7 +85,7 @@ def __init__(self, drinfeld_module): raise TypeError('input must be a DrinfeldModule') self._drinfeld_module = drinfeld_module super().__init__(drinfeld_module.function_ring(), - drinfeld_module.base_ring()) + drinfeld_module.base_ring()) def _act_(self, pol, x): r""" @@ -137,8 +137,8 @@ def _latex_(self): \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 11^2 """ return f'\\text{{Action{{ }}on{{ }}}}' \ - f'{latex(self._drinfeld_module.base_ring())}\\text{{{{ }}' \ - f'induced{{ }}by{{ }}}}{self._drinfeld_module}' + f'{latex(self._drinfeld_module.base_ring())}\\text{{{{ }}' \ + f'induced{{ }}by{{ }}}}{self._drinfeld_module}' def _repr_(self): r""" @@ -157,7 +157,7 @@ def _repr_(self): Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 11^2 """ return f'Action on {self._drinfeld_module.base_ring()} induced by ' \ - f'{self._drinfeld_module}' + f'{self._drinfeld_module}' def drinfeld_module(self): r""" diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 8e9df953a43..40bbfe4633a 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -30,7 +30,6 @@ from sage.modules.free_module_element import vector from sage.rings.integer import Integer from sage.rings.polynomial.ore_polynomial_element import OrePolynomial -from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.structure.category_object import CategoryObject from sage.structure.sequence import Sequence @@ -493,13 +492,12 @@ def __classcall_private__(cls, function_ring, gen, name='t'): # here and in the category constructor, which is not ideal. # Check domain is Fq[X] if not isinstance(function_ring, PolynomialRing_general): - raise NotImplementedError('function ring must be a polynomial ring') + raise NotImplementedError('function ring must be a polynomial ' + 'ring') function_ring_base = function_ring.base_ring() - if not function_ring_base.is_field() or not function_ring_base.is_finite() : + if not function_ring_base.is_field() \ + or not function_ring_base.is_finite(): raise TypeError('function ring base must be a finite field') - Fq = function_ring_base - FqX = function_ring - X = FqX.gen() # Check all possible input types for gen # `gen` is an Ore polynomial: @@ -512,11 +510,12 @@ def __classcall_private__(cls, function_ring, gen, name='t'): ore_polring = None ore_polring_base = Sequence(gen).universe() else: - raise TypeError('generator must be list of coefficients or Ore ' \ - 'polynomial') + raise TypeError('generator must be list of coefficients or Ore ' + 'polynomial') # The coefficients are in a base ring that has coercion from Fq: - if not (hasattr(ore_polring_base, 'has_coerce_map_from') and \ - ore_polring_base.has_coerce_map_from(function_ring.base_ring())): + if not (hasattr(ore_polring_base, 'has_coerce_map_from') + and ore_polring_base.has_coerce_map_from( + function_ring.base_ring())): raise ValueError('function ring base must coerce into base ring') # Build the morphism that defines the category @@ -625,11 +624,11 @@ def _Hom_(self, other, category): return DrinfeldModuleHomset(self, other, category) def _check_rank_two(self): - r""" - Raise ``NotImplementedError`` if the rank is not two. - """ - if self.rank() != 2: - raise NotImplementedError('rank must be 2') + r""" + Raise ``NotImplementedError`` if the rank is not two. + """ + if self.rank() != 2: + raise NotImplementedError('rank must be 2') def _latex_(self): r""" @@ -648,9 +647,9 @@ def _latex_(self): \text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }}\Bold{F}_{5^{12}} """ return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ - f'{latex(self._function_ring.gen())} '\ - f'\\mapsto {latex(self._gen)}' \ - f'\\text{{{{ }}over{{ }}}}{latex(self._base_ring)}' + f'{latex(self._function_ring.gen())} '\ + f'\\mapsto {latex(self._gen)}' \ + f'\\text{{{{ }}over{{ }}}}{latex(self._base_ring)}' def _repr_(self): r""" @@ -669,7 +668,7 @@ def _repr_(self): Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 """ return f'Drinfeld module defined by {self._function_ring.gen()} ' \ - f'|--> {self._gen} over {self._base_ring}' + f'|--> {self._gen} over {self._base_ring}' def action(self): r""" @@ -708,7 +707,7 @@ def action(self): def base_ring(self): r""" Return the base ring of the Drinfeld module. - + OUTPUT: a field EXAMPLES: @@ -824,7 +823,7 @@ def change_ring(self, new_field, name=None): """ coeffs = self._gen.coefficients() new_coeffs = list(map(new_field, coeffs)) - if name == None: + if name is None: name = self._ore_polring.variable_name() return DrinfeldModule(self._function_ring, new_coeffs, name=name) @@ -1073,12 +1072,13 @@ def invert(self, ore_pol): """ deg = ore_pol.degree() r = self.rank() - if not ore_pol in self._ore_polring: + if ore_pol not in self._ore_polring: raise TypeError('input must be an Ore polynomial') if ore_pol in self._base_ring: return self._Fq(ore_pol) if deg % r != 0: - raise ValueError('input must be in the image of the Drinfeld module') + raise ValueError('input must be in the image of the Drinfeld ' + 'module') k = deg // r X = self._function_ring.gen() @@ -1319,7 +1319,7 @@ def velu(self, isog): OUTPUT: a Drinfeld module ALGORITHM: - + The input defines an isogeny if only if: 1. The degree of the characteristic divides the height of the input. (The height of an Ore polynomial @@ -1376,7 +1376,7 @@ def velu(self, isog): ... ValueError: the input does not define an isogeny """ - if not isog in self.ore_polring(): + if isog not in self.ore_polring(): raise TypeError('input must be an Ore polynomial') e = ValueError('the input does not define an isogeny') if isog == 0: diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index ce87148eb59..afb02c5eba6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -24,6 +24,7 @@ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule + class FiniteDrinfeldModule(DrinfeldModule): r""" This class represents a finite Drinfeld module. @@ -263,7 +264,8 @@ def frobenius_norm(self): m = n // d delta = self._gen[2] norm = self._base_ring.over(self._Fq)(delta).norm() - self._frobenius_norm = ((-1)**n) * (self.characteristic()**m) / norm + char = self.characteristic() + self._frobenius_norm = ((-1)**n) * (char**m) / norm return self._frobenius_norm def frobenius_trace(self): diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index b442ec9fe0d..df176bfafa6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -20,11 +20,12 @@ #***************************************************************************** from sage.categories.drinfeld_modules import DrinfeldModules -from sage.categories.homset import Homset, Hom +from sage.categories.homset import Homset from sage.misc.latex import latex from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism from sage.structure.parent import Parent + class DrinfeldModuleHomset(Homset): r""" This class represents the set of morphisms between two Drinfeld @@ -113,7 +114,7 @@ class DrinfeldModuleHomset(Homset): sage: frobenius_endomorphism in hom False """ - + Element = DrinfeldModuleMorphism __contains__ = Parent.__contains__ @@ -123,7 +124,8 @@ def __init__(self, X, Y, category=None, check=True): if check: if X.category() != Y.category() \ or not isinstance(X.category(), DrinfeldModules): - raise NotImplementedError('Drinfeld modules must be in the same category') + raise NotImplementedError('Drinfeld modules must be in the ' + 'same category') if category != X.category(): raise NotImplementedError('category should be DrinfeldModules') base = category.base() @@ -147,8 +149,8 @@ def _latex_(self): \text{Set{ }of{ }Drinfeld{ }module{ }morphisms{ }from}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto 2 t^{2} + z_{6} t + z_{6}\text{{ }over{ }}\Bold{F}_{3^{6}}\text{{ }to{ }}Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 """ return f'\\text{{Set{{ }}of{{ }}Drinfeld{{ }}module{{ }}morphisms' \ - f'{{ }}from}}{latex(self.domain())}\\text{{{{ }}to{{ }}}}' \ - f'{self.codomain()}' + f'{{ }}from}}{latex(self.domain())}\\text{{{{ }}to{{ }}}}' \ + f'{self.codomain()}' def _repr_(self): r""" @@ -170,8 +172,8 @@ def _repr_(self): To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 """ return f'Set of Drinfeld module morphisms:\n' \ - f' From: {self.domain()}\n' \ - f' To: {self.codomain()}' + f' From: {self.domain()}\n' \ + f' To: {self.codomain()}' def __contains__(self, x): r""" diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index f5f274f51c2..d8e8a7adc41 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -22,9 +22,6 @@ from sage.misc.latex import latex from sage.structure.element import Element from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.polynomial.ore_polynomial_element import OrePolynomial -from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing -from sage.categories.drinfeld_modules import DrinfeldModules class DrinfeldModuleMorphism(UniqueRepresentation, Element, @@ -158,11 +155,14 @@ def _latex_(self): \end{array} """ return f'\\begin{{array}}{{l}}\n' \ - f'\\text{{Drinfeld{{ }}module{{ }}morphism:}}\\\\\n' \ - f'\\text{{{{ }}{{ }}From:{{ }}}}{latex(self._domain)}}}\\\\\n' \ - f'\\text{{{{ }}{{ }}To:{{ }}}}{{ }}{{ }}{latex(self._codomain)}\\\\\n' \ - f'\\text{{{{ }}{{ }}Defn:{{ }}}}{latex(self._ore_polynomial)}\n' \ - f'\\end{{array}}' + f'\\text{{Drinfeld{{ }}module{{ }}morphism:}}\\\\\n' \ + f'\\text{{{{ }}{{ }}From:{{ }}}}'\ + f'{latex(self._domain)}}}\\\\\n' \ + f'\\text{{{{ }}{{ }}To:{{ }}}}{{ }}{{ }}' \ + f'{latex(self._codomain)}\\\\\n' \ + f'\\text{{{{ }}{{ }}Defn:{{ }}}}' \ + f'{latex(self._ore_polynomial)}\n' \ + f'\\end{{array}}' def _repr_(self): r""" @@ -185,9 +185,9 @@ def _repr_(self): Defn: t + z6^5 + z6^2 + 1 """ return f'Drinfeld Module morphism:\n' \ - f' From: {self._domain}\n' \ - f' To: {self._codomain}\n' \ - f' Defn: {self._ore_polynomial}' + f' From: {self._domain}\n' \ + f' To: {self._codomain}\n' \ + f' Defn: {self._ore_polynomial}' def codomain(self): r""" From 5d21c5432cb4386d126b3da1098c7dde40d47683 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 29 Aug 2022 17:34:43 +0200 Subject: [PATCH 112/197] Change sage.categories.drinfeld_modules title This is to comply with other modules in sage.categories. --- src/sage/categories/drinfeld_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 4d4f851fc31..a39228ce57c 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -1,5 +1,5 @@ r""" -Category of Drinfeld modules +Drinfeld modules over a base This module provides the class :class:`sage.category.drinfeld_modules.DrinfeldModules`. From 43c430f273a372e316a0061646f8bd4830837895 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 29 Aug 2022 17:36:25 +0200 Subject: [PATCH 113/197] Refactor Drinfeld modules in reference Drinfeld modules now have their own directory (sage/src/doc/en/reference/drinfeld_modules), and they are indexed on the main entry point of the SageMath reference. --- src/doc/en/reference/drinfeld_modules/conf.py | 1 + .../en/reference/drinfeld_modules/index.rst | 42 +++++++++++++++++++ .../en/reference/function_fields/index.rst | 1 - src/doc/en/reference/index.rst | 1 + 4 files changed, 44 insertions(+), 1 deletion(-) create mode 120000 src/doc/en/reference/drinfeld_modules/conf.py create mode 100644 src/doc/en/reference/drinfeld_modules/index.rst diff --git a/src/doc/en/reference/drinfeld_modules/conf.py b/src/doc/en/reference/drinfeld_modules/conf.py new file mode 120000 index 00000000000..2bdf7e68470 --- /dev/null +++ b/src/doc/en/reference/drinfeld_modules/conf.py @@ -0,0 +1 @@ +../conf_sub.py \ No newline at end of file diff --git a/src/doc/en/reference/drinfeld_modules/index.rst b/src/doc/en/reference/drinfeld_modules/index.rst new file mode 100644 index 00000000000..316f50cb830 --- /dev/null +++ b/src/doc/en/reference/drinfeld_modules/index.rst @@ -0,0 +1,42 @@ +Drinfeld modules +==================================== + +Sage include facilities to manipulate Drinfeld modules and their morphisms. The +main entry point is the class +:class:`sage.rings.function_field.drinfeld_modules.drinfeld_module.DrinfeldModule`. + +Drinfeld modules +---------------- + +.. toctree:: + :maxdepth: 2 + + sage/rings/function_field/drinfeld_modules/drinfeld_module + sage/rings/function_field/drinfeld_modules/finite_drinfeld_module + +Morphisms and isogenies +----------------------- + +.. toctree:: + :maxdepth: 2 + + sage/rings/function_field/drinfeld_modules/morphism + sage/rings/function_field/drinfeld_modules/homset + +The module action induced by a Drinfeld module +---------------------------------------------- + +.. toctree:: + :maxdepth: 2 + + sage/rings/function_field/drinfeld_modules/action + +The category of Drinfeld modules +-------------------------------- + +.. toctree:: + :maxdepth: 2 + + sage/categories/drinfeld_modules + +.. include:: ../footer.txt diff --git a/src/doc/en/reference/function_fields/index.rst b/src/doc/en/reference/function_fields/index.rst index bc2350116ad..50c04560a33 100644 --- a/src/doc/en/reference/function_fields/index.rst +++ b/src/doc/en/reference/function_fields/index.rst @@ -21,7 +21,6 @@ algebraic closure of `\QQ`. sage/rings/function_field/maps sage/rings/function_field/extensions sage/rings/function_field/constructor - sage/rings/function_field/drinfeld_modules/drinfeld_module A basic reference for the theory of algebraic function fields is [Stich2009]_. diff --git a/src/doc/en/reference/index.rst b/src/doc/en/reference/index.rst index 065bccac955..88f1bd814dd 100644 --- a/src/doc/en/reference/index.rst +++ b/src/doc/en/reference/index.rst @@ -111,6 +111,7 @@ Number Fields, Function Fields, and Valuations * :doc:`Number Fields ` * :doc:`Function Fields ` * :doc:`Discrete Valuations ` +* :doc:`Drinfeld Modules ` Number Theory ------------- From cf503d8d2c10fdd187cc5090f2724723dc12a0a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 8 Sep 2022 13:37:38 +0200 Subject: [PATCH 114/197] Change _call_ to object in DrinfeldModules This is to emphasize that DrinfeldModules is not a parent. --- src/sage/categories/drinfeld_modules.py | 80 +++++++++---------- .../drinfeld_modules/drinfeld_module.py | 8 +- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index a39228ce57c..d87436233a5 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -135,10 +135,10 @@ class DrinfeldModules(CategoryWithParameters): .. RUBRIC:: Creating Drinfeld module objects from the category - Calling the category with an Ore polynomial creates a Drinfeld - module object in the category whose generator is the input:: + Calling :meth:`object` with an Ore polynomial creates a Drinfeld module + object in the category whose generator is the input:: - sage: psi = cat([p_root, 1]) + sage: psi = cat.object([p_root, 1]) sage: psi Drinfeld module defined by X |--> t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 sage: psi.category() is cat @@ -147,7 +147,7 @@ class DrinfeldModules(CategoryWithParameters): Of course, the constant coefficient of the input must be the same as the category':: - sage: cat([z, 1]) + sage: cat.object([z, 1]) Traceback (most recent call last): ... ValueError: constant coefficient must be the generator of the morphism that defines the category @@ -240,41 +240,6 @@ def __init__(self, morphism, name='t'): elif FqX.is_subring(K): self._characteristic = Integer(0) - def _call_(self, gen): - r""" - Return a Drinfeld module object in the category whose generator - is the input. - - INPUT: the generator of the Drinfeld module, given as an Ore - polynomial or a list of coefficients - - OUTPUT: a Drinfeld module in the category - - EXAMPLES: - - sage: Fq = GF(11) - sage: FqX. = Fq[] - sage: K. = Fq.extension(4) - sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: psi = cat([p_root, 0, 1]) - sage: psi - Drinfeld module defined by X |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 - sage: t = phi.ore_variable() - sage: cat(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi - True - """ - from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule - # If gen is not in the Ore polring, an exception is raised - gen = self._ore_polring(gen) - X = self._function_ring.gen() - gamma = self._morphism - if gen[0] != gamma(X): - raise ValueError('constant coefficient must be the generator ' - 'of the morphism that defines the category') - return DrinfeldModule(self._function_ring, gen) - # Somehow required for the class definition def _make_named_class_key(self, name): return self._function_ring.category() @@ -451,6 +416,41 @@ def morphism(self): """ return self._morphism + def object(self, gen): + r""" + Return a Drinfeld module object in the category whose generator + is the input. + + INPUT: the generator of the Drinfeld module, given as an Ore + polynomial or a list of coefficients + + OUTPUT: a Drinfeld module in the category + + EXAMPLES: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: psi = cat.object([p_root, 0, 1]) + sage: psi + Drinfeld module defined by X |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + sage: t = phi.ore_variable() + sage: cat.object(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi + True + """ + from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule + # If gen is not in the Ore polring, an exception is raised + gen = self._ore_polring(gen) + X = self._function_ring.gen() + gamma = self._morphism + if gen[0] != gamma(X): + raise ValueError('constant coefficient must be the generator ' + 'of the morphism that defines the category') + return DrinfeldModule(self._function_ring, gen) + def ore_polring(self): r""" Return the Ore polynomial ring of the category. @@ -529,7 +529,7 @@ def random_object(self, rank): dom_coeff = K.random_element() coeffs.append(dom_coeff) - return self(coeffs) + return self.object(coeffs) # Somehow required for the class definition def super_categories(self): diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 40bbfe4633a..4263bbd44f8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -167,7 +167,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): morphism that defines the category:: sage: cat = phi.category() - sage: cat([1, 1, K(1)]) + sage: cat.object([1, 1, K(1)]) Traceback (most recent call last): ... ValueError: constant coefficient must be the generator of the morphism that defines the category @@ -856,7 +856,7 @@ def constant_coefficient(self): same constant coefficient:: sage: t = phi.ore_variable() - sage: psi = cat(phi.constant_coefficient() + t^3) + sage: psi = cat.object(phi.constant_coefficient() + t^3) sage: psi Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 @@ -864,7 +864,7 @@ def constant_coefficient(self): this category if they do not share the same constant coefficient:: - sage: rho = cat(phi.constant_coefficient() + 1 + t^3) + sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) Traceback (most recent call last): ... ValueError: constant coefficient must be the generator of the morphism that defines the category @@ -1387,4 +1387,4 @@ def velu(self, isog): or rem != 0: raise e else: - return self.category()(quo) + return self.category().object(quo) From f98c9686008acb0024d3be1bb562beaea42c4912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 8 Sep 2022 14:31:26 +0200 Subject: [PATCH 115/197] Change base for Drinfeld modules classes The so-called base of a Drinfeld module (or its category) was a field (the field of coefficients of the Ore polring). This was fundamentally misleading, as the good notion for the base is that of a morphism Fq[X] -> K. The base is now this morphism. Method change_ring was deleted. Doc is updated; tests pass. Some changes were made to other _repr_ and _latex_ methods. --- src/sage/categories/drinfeld_modules.py | 187 ++++---- .../function_field/drinfeld_modules/action.py | 42 +- .../drinfeld_modules/drinfeld_module.py | 398 +++++++----------- .../finite_drinfeld_module.py | 47 ++- .../function_field/drinfeld_modules/homset.py | 60 ++- .../drinfeld_modules/morphism.py | 67 +-- 6 files changed, 342 insertions(+), 459 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index d87436233a5..27480adb460 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -33,28 +33,26 @@ class DrinfeldModules(CategoryWithParameters): This class represents the category of Drinfeld modules on a given base. - Let `\phi` be a Drinfeld module in the present category. We denote - its function ring `\Fq[X]` and its base ring `K`. The `\Fq[X]`-field - structure on `K` is given by a morphism `\gamma` + The category is uniquely defined by its base, which is a ring + morphism from the function ring `\Fq[X]` to a field `K`. Note that + the base is a morphism, but not a field. The base is often denoted + `\gamma`, and we call `K` an *`\Fq[X]-field`*. + + The monic polynomial that generates the kernel of the base is called + the *`\Fq[X]`-characteristic of the `\Fq[X]`-field `K`*. .. NOTE:: These notations will be used throughout this documentation. - We say that `\Fq[X]` is the *function ring of the category*; `K` is - the *base of the category* (or simply its *base ring* or *base - field*); `\Fq[X]` is the *function ring of the category*; + We say that `\Fq[X]` is the *function ring of the category*; *K\{\tau\}* is the *Ore polynomial ring of the category*; `t` is the *Ore variable of the category*. The *constant coefficient of the - category* is `\gamma(X)`. The `\Fq[X]`-characteristic of the base - ring `K` can also be referred to as its *function - ring-characteristic*. - - .. NOTE:: - - The base is always a field. + category* is the image of `X` under the base. The + `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also be + referred to as its *function ring-characteristic*. - INPUT: a ring morphism from the function ring to the base ring + INPUT: the base, a ring morphism .. RUBRIC:: Construction @@ -69,20 +67,20 @@ class DrinfeldModules(CategoryWithParameters): sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat - Category of Drinfeld modules defined by Ring morphism: + Category of Drinfeld modules defined over base Ring morphism: From: Univariate Polynomial Ring in X over Finite Field of size 11 To: Finite Field in z of size 11^4 Defn: X |--> z^3 + 7*z^2 + 6*z + 10 - The output tells the user that the category is only defined by the - ring morphism `\gamma`. + The output tells the user that the category is only defined by its + base. .. RUBRIC:: Properties of the category - The defining morphism is retrieved using the method + The base, which is a morphism, is retrieved using the method :meth:`morphism`:: - sage: cat.morphism() + sage: cat.base() Ring morphism: From: Univariate Polynomial Ring in X over Finite Field of size 11 To: Finite Field in z of size 11^4 @@ -94,44 +92,35 @@ class DrinfeldModules(CategoryWithParameters): sage: cat.constant_coefficient() z^3 + 7*z^2 + 6*z + 10 - sage: cat.morphism()(X) == cat.constant_coefficient() + sage: cat.base()(X) == cat.constant_coefficient() True Similarly, the *function ring-characteristic* of the category is either `0` or the unique monic polynomial in `\Fq[X]` that generates - `\mathrm{Ker}(\gamma)`:: + the kernel of the base:: sage: cat.characteristic() X^2 + 7*X + 2 - sage: cat.morphism()(cat.characteristic()) + sage: cat.base()(cat.characteristic()) 0 - Like for its Drinfeld modules, the *base* of the category is the - field `K`:: + The base, function ring and Ore polynomial ring are the + same for the category and its objects:: - sage: cat.base() - Finite Field in z of size 11^4 - sage: cat.base() is K + sage: cat.base() is phi.base() True - sage: cat.base() is phi.base_ring() - True - - And the *function ring* is the polynomial ring `\Fq[X]`:: + sage: cat.function_ring() is phi.function_ring() + True sage: cat.function_ring() Univariate Polynomial Ring in X over Finite Field of size 11 - sage: cat.function_ring() is FqX - True - sage: cat.function_ring() is phi.function_ring() + sage: cat.function_ring() is cat.base().domain() True - And as expected, the *Ore polynomial ring* is that of - the Drinfeld modules in the category: - - sage: cat.ore_polring() - Ore Polynomial Ring in t over Finite Field in z of size 11^4 twisted by z |--> z^11 sage: cat.ore_polring() is phi.ore_polring() True + sage: cat.ore_polring() + Ore Polynomial Ring in t over Finite Field in z of size 11^4 twisted by z |--> z^11 .. RUBRIC:: Creating Drinfeld module objects from the category @@ -140,7 +129,10 @@ class DrinfeldModules(CategoryWithParameters): sage: psi = cat.object([p_root, 1]) sage: psi - Drinfeld module defined by X |--> t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + Drinfeld module defined by X |--> t + z^3 + 7*z^2 + 6*z + 10 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^4 + Defn: X |--> z^3 + 7*z^2 + 6*z + 10 sage: psi.category() is cat True @@ -169,42 +161,41 @@ class DrinfeldModules(CategoryWithParameters): sage: FqX. = Fq[] sage: K. = Fq.extension(4) sage: from sage.categories.drinfeld_modules import DrinfeldModules - sage: gamma = Hom(FqX, K)(0) - sage: cat = DrinfeldModules(gamma) + sage: base = Hom(FqX, K)(0) + sage: cat = DrinfeldModules(base) Traceback (most recent call last): ... - ValueError: the morphism must be non zero + ValueError: base must be a non zero morphism - sage: gamma = Hom(FqX, FqX)(1) - sage: cat = DrinfeldModules(gamma) + sage: base = Hom(FqX, FqX)(1) + sage: cat = DrinfeldModules(base) Traceback (most recent call last): ... - TypeError: base must be a field + TypeError: base codomain must be a field - sage: gamma = 'I hate Rostropovitch' - sage: cat = DrinfeldModules(gamma) # known bug (blankline) + sage: base = 'I hate Rostropovitch' + sage: cat = DrinfeldModules(base) # known bug (blankline) Traceback (most recent call last): ... TypeError: input must be a ring morphism sage: ZZT. = ZZ[] - sage: gamma = Hom(ZZT, K)(1) - sage: cat = DrinfeldModules(gamma) # known bug (blankline) + sage: base = Hom(ZZT, K)(1) + sage: cat = DrinfeldModules(base) # known bug (blankline) Traceback (most recent call last): ... TypeError: function ring base must be a finite field """ - def __init__(self, morphism, name='t'): - gamma = morphism + def __init__(self, base, name='t'): # Check input is a ring Morphism - if not isinstance(gamma, RingHomomorphism): + if not isinstance(base, RingHomomorphism): raise TypeError('input must be a Ring morphism') - self._morphism = morphism - self._function_ring = gamma.domain() - # Check domain is Fq[X] + self._base = base + self._function_ring = base.domain() + # Check domain of base is Fq[X] function_ring = self._function_ring if not isinstance(function_ring, PolynomialRing_general): raise NotImplementedError('function ring must be a polynomial ' @@ -216,27 +207,26 @@ def __init__(self, morphism, name='t'): Fq = function_ring_base FqX = function_ring X = FqX.gen() - # Check codomain of gamma is field - K = gamma.codomain() - self._base = K + # Check codomain of base is a field + K = base.codomain() if not K.is_field(): - raise TypeError('base must be a field') - # Check morphism is non zero - if gamma(X).is_zero(): - raise ValueError('the morphism must be non zero') + raise TypeError('base codomain must be a field') + # Check base is a non zero morphism + if base(X).is_zero(): + raise ValueError('base must be a non zero morphism') # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) self._ore_polring = OrePolynomialRing(K, tau, names=name, polcast=False) # Create constant coefficient - self._constant_coefficient = gamma(X) + self._constant_coefficient = base(X) # Create characteristic self._characteristic = None if K.is_finite(): - f = gamma * FqX.coerce_map_from(Fq) # Fq -> K + f = base * FqX.coerce_map_from(Fq) # Fq -> K E = K.over(f) - self._characteristic = FqX(E(gamma(X)).minpoly()) + self._characteristic = FqX(E(base(X)).minpoly()) elif FqX.is_subring(K): self._characteristic = Integer(0) @@ -259,7 +249,7 @@ def _latex_(self): sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: latex(cat) - \text{Category{ }of{ }Drinfeld{ }modules{ }defined{ }by\begin{array}{l} + \text{Category{ }of{ }Drinfeld{ }modules{ }defined{ }over{ }base{ }\begin{array}{l} \text{\texttt{Ring{ }morphism:}}\\ \text{\texttt{{ }{ }From:{ }Univariate{ }Polynomial{ }Ring{ }in{ }X{ }over{ }Finite{ }Field{ }of{ }size{ }11}}\\ \text{\texttt{{ }{ }To:{ }{ }{ }Finite{ }Field{ }in{ }z{ }of{ }size{ }11{\char`\^}4}}\\ @@ -267,7 +257,7 @@ def _latex_(self): \end{array} """ return f'\\text{{Category{{ }}of{{ }}Drinfeld{{ }}modules{{ }}' \ - f'defined{{ }}by{latex(self._morphism)}' + f'defined{{ }}over{{ }}base{{ }}{latex(self._base)}' def _repr_(self): r""" @@ -284,12 +274,12 @@ def _repr_(self): sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat - Category of Drinfeld modules defined by Ring morphism: + Category of Drinfeld modules defined over base Ring morphism: From: Univariate Polynomial Ring in X over Finite Field of size 11 To: Finite Field in z of size 11^4 Defn: X |--> z^3 + 7*z^2 + 6*z + 10 """ - return f'Category of Drinfeld modules defined by {self._morphism}' + return f'Category of Drinfeld modules defined over base {self._base}' # Somehow required for the class definition def Homsets(self): @@ -303,7 +293,7 @@ def base(self): r""" Return the base of the category. - OUTPUT: a ring + OUTPUT: a ring morphism EXAMPLES: @@ -313,9 +303,13 @@ def base(self): sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() - sage: cat.base() - Finite Field in z of size 11^4 - sage: cat.base() is K + sage: base = cat.base() + sage: base + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^4 + Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + sage: base(X) == cat.constant_coefficient() True """ return self._base @@ -340,7 +334,10 @@ def characteristic(self): sage: L = Frac(FqX) sage: psi = DrinfeldModule(FqX, [L.gen(), 1]) sage: psi - Drinfeld module defined by X |--> t + X over Fraction Field of Univariate Polynomial Ring in X over Finite Field of size 11 + Drinfeld module defined by X |--> t + X over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Fraction Field of Univariate Polynomial Ring in X over Finite Field of size 11 + Defn: X |--> X sage: fox = psi.category() sage: fox.characteristic() 0 @@ -353,7 +350,7 @@ def constant_coefficient(self): r""" Return the constant coefficient of the category. - OUTPUT: a base element + OUTPUT: an element in the base codomain EXAMPLES: @@ -365,7 +362,7 @@ def constant_coefficient(self): sage: cat = phi.category() sage: cat.constant_coefficient() z^3 + 7*z^2 + 6*z + 10 - sage: cat.constant_coefficient() == cat.morphism()(X) + sage: cat.constant_coefficient() == cat.base()(X) True """ return self._constant_coefficient @@ -391,31 +388,6 @@ def function_ring(self): """ return self._function_ring - def morphism(self): - r""" - Return the morphism that defines the category. - - OUTPUT: a ring morphism - - EXAMPLES: - - sage: Fq = GF(11) - sage: FqX. = Fq[] - sage: K. = Fq.extension(4) - sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: gamma = cat.morphism() - sage: gamma - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^4 - Defn: X |--> z^3 + 7*z^2 + 6*z + 10 - sage: gamma(X) == cat.constant_coefficient() - True - """ - return self._morphism - def object(self, gen): r""" Return a Drinfeld module object in the category whose generator @@ -436,7 +408,10 @@ def object(self, gen): sage: cat = phi.category() sage: psi = cat.object([p_root, 0, 1]) sage: psi - Drinfeld module defined by X |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + Drinfeld module defined by X |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^4 + Defn: X |--> z^3 + 7*z^2 + 6*z + 10 sage: t = phi.ore_variable() sage: cat.object(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi True @@ -445,8 +420,8 @@ def object(self, gen): # If gen is not in the Ore polring, an exception is raised gen = self._ore_polring(gen) X = self._function_ring.gen() - gamma = self._morphism - if gen[0] != gamma(X): + base = self._base + if gen[0] != base(X): raise ValueError('constant coefficient must be the generator ' 'of the morphism that defines the category') return DrinfeldModule(self._function_ring, gen) @@ -520,7 +495,7 @@ def random_object(self, rank): if rank <= 0: raise ValueError('rank must be a positive integer') - K = self._base + K = self._base.codomain() coeffs = [self._constant_coefficient] for _ in range(rank-1): coeffs.append(K.random_element()) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 28b585cd5dc..fbc1bbfe47b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -52,7 +52,10 @@ class DrinfeldModuleAction(Action): sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 11^2 + Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^2 + Defn: X |--> z The action on elements is computed as follows:: @@ -66,13 +69,6 @@ class DrinfeldModuleAction(Action): sage: action(FqX.random_element(), 0) 0 - To act on a field larger than `K`, one can change the ring of the - Drinfeld module, then create the action:: - - sage: extended_action = phi.change_ring(K.extension(2)).action() - sage: extended_action - Action on Finite Field in z4 of size 11^4 induced by Drinfeld module defined by X |--> t + 10*z4^3 + 4*z4^2 + 5*z4 + 5 over Finite Field in z4 of size 11^4 - Finally, given a Drinfeld module action, it is easy to recover the corresponding Drinfeld module:: @@ -84,8 +80,8 @@ def __init__(self, drinfeld_module): if not isinstance(drinfeld_module, DrinfeldModule): raise TypeError('input must be a DrinfeldModule') self._drinfeld_module = drinfeld_module - super().__init__(drinfeld_module.function_ring(), - drinfeld_module.base_ring()) + self._field = drinfeld_module.base().codomain() + super().__init__(drinfeld_module.function_ring(), self._field) def _act_(self, pol, x): r""" @@ -94,9 +90,9 @@ def _act_(self, pol, x): INPUT: - ``pol`` -- a function ring element - - ``x`` -- a base ring element + - ``x`` -- an element in the field acted upon - OUTPUT: an element in the base ring of the Drinfeld module + OUTPUT: an element in the base field of the Drinfeld module EXAMPLES: @@ -116,8 +112,8 @@ def _act_(self, pol, x): """ if pol not in self._drinfeld_module.function_ring(): raise TypeError('first input must be in the function ring') - if x not in self._drinfeld_module.base_ring(): - raise TypeError('second input must be in the base ring') + if x not in self._field: + raise TypeError('second input must be in the field acted upon') return self._drinfeld_module(pol)(x) def _latex_(self): @@ -134,11 +130,16 @@ def _latex_(self): sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) sage: action = phi.action() sage: latex(action) - \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 11^2 + \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto t^{3} + z\text{{ }over{ }base{ }}\begin{array}{l} + \text{\texttt{Ring{ }morphism:}}\\ + \text{\texttt{{ }{ }From:{ }Univariate{ }Polynomial{ }Ring{ }in{ }X{ }over{ }Finite{ }Field{ }of{ }size{ }11}}\\ + \text{\texttt{{ }{ }To:{ }{ }{ }Finite{ }Field{ }in{ }z{ }of{ }size{ }11{\char`\^}2}}\\ + \text{\texttt{{ }{ }Defn:{ }X{ }|{-}{-}>{ }z}} + \end{array} """ return f'\\text{{Action{{ }}on{{ }}}}' \ - f'{latex(self._drinfeld_module.base_ring())}\\text{{{{ }}' \ - f'induced{{ }}by{{ }}}}{self._drinfeld_module}' + f'{latex(self._field)}\\text{{{{ }}' \ + f'induced{{ }}by{{ }}}}{latex(self._drinfeld_module)}' def _repr_(self): r""" @@ -154,9 +155,12 @@ def _repr_(self): sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 11^2 + Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^2 + Defn: X |--> z """ - return f'Action on {self._drinfeld_module.base_ring()} induced by ' \ + return f'Action on {self._field} induced by ' \ f'{self._drinfeld_module}' def drinfeld_module(self): diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 4263bbd44f8..63313a25d1b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -40,15 +40,22 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): r""" This class represents a Drinfeld module. - Let `\Fq` be a finite field with order `q` and let `K` be a field - equipped a ring morphism `\gamma: \Fq[X] \to K`. The field `K` is - called an *`\Fq[X]`-field*, and the monic generator of - `\Ker(\gamma)` is called the *`\Fq[X]`-characteristic of the - `\Fq[X]`-field `K`*. + Let `\Fq[X]` be a polynomial ring with coefficients in a finite + field `\Fq` and let `K` be a field. We fix a ring morphism `\gamma: + \Fq[X] \to K`, which we call the *base* of the Drinfeld module. + We also call `K` an *`\Fq[X]`-field*. + + .. NOTE:: + + The base of the Drinfeld module is the base of the category of + the Drinfeld module. + + The monic polynomial that generates the kernel of the base is called + the *`\Fq[X]`-characteristic of the `\Fq[X]`-field `K`*. Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in `K` and Frobenius variable `\tau: x \mapsto x^q`. A *Drinfeld - `\Fq[X]`-module over the `\Fq[X]`-field `K`* is an `\Fq`-algebra + `\Fq[X]`-module over the base `\gamma`* is an `\Fq`-algebra morphism `\phi: \Fq[X] \to K\{\tau\}` such that: 1. The image of `\phi` contains non-constant Ore polynomials. @@ -57,25 +64,16 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. - .. NOTE:: - - These notations will be used throughout the documentation. - - We say that `\Fq[X]` is the *function ring of `\phi`*; `K` is the - *base ring of `\phi`* (or simply its *base* or *base field*); - *K\{\tau\}* is the *Ore polynomial ring of `\phi`*; `t` is the *Ore - variable of `\phi`*. Further, the *generator of `\phi`* is `\phi_X` - and its *constant coefficient* is the constant coefficient of - `\phi_X`. The `\Fq[X]`-characteristic of the base ring `K` can also - be referred to as its *function ring-characteristic*. - - .. NOTE:: - - The base ring is always a field. - The Drinfeld module `\phi` is uniquely determined by the image `\phi_X` of `X`, which is an input of the class. + We say that `\Fq[X]` is the *function ring of `\phi`*; *K\{\tau\}* + is the *Ore polynomial ring of `\phi`*; `t` is the *Ore variable of + `\phi`*. Further, the *generator of `\phi`* is `\phi_X` and its + *constant coefficient* is the constant coefficient of `\phi_X`. The + `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also be referred to as + its *function ring-characteristic*. + Classical references on Drinfeld modules include [Gos1998]_, [Rosen2002]_, [VS06]_ and [Gek1998]_. @@ -105,7 +103,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z, 1, 1]) sage: phi - Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + Drinfeld module defined by X |--> t^2 + t + z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z In this example, we used a list of coefficients (``[z, 1, 1]``) to represent the generator `\phi_X = z + t + t^2`, `K = \Fq(z)`. One can @@ -116,7 +117,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: psi_X = z + t^3 sage: psi = DrinfeldModule(FqX, psi_X) sage: psi - Drinfeld module defined by X |--> t^3 + z over Finite Field in z of size 3^12 + Drinfeld module defined by X |--> t^3 + z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z sage: psi(X) == psi_X True @@ -132,7 +136,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: DrinfeldModule(FqX, [K(0), K(1)]) Traceback (most recent call last): ... - ValueError: the morphism must be non zero + ValueError: base must be a non zero morphism The coefficients of the generator must lie in an `\Fq[X]`-field, where `\Fq[X]` is the function ring of the Drinfeld module:: @@ -140,12 +144,12 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: DrinfeldModule(FqX, [z, QQ(1)]) Traceback (most recent call last): ... - ValueError: function ring base must coerce into base ring + ValueError: function ring base must coerce into base codomain sage: DrinfeldModule(FqX, [1, QQ(1)]) Traceback (most recent call last): ... - ValueError: function ring base must coerce into base ring + ValueError: function ring base must coerce into base codomain The function ring must be an univariate polynomial ring whose base is a finite field:: @@ -175,44 +179,36 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): .. NOTE:: The reader may think that it is odd to build a Drinfeld module - without specifying the `\Fq[X]`-characteristic of the base (or, - more generally, its parent category). Indeed, the - `\Fq[X]`-characteristic plays the role of the characteristic of - a function field, and thus preexists Drinfeld modules. The base - field `K` should rather be seen as an `\Fq[X]`-field, i.e. the - field `K` equipped with a morphism `\gamma: \Fq[X] \to K`, - instead of just a field. - - However, as the characteristic may be deduced from the constant - coefficient of the Drinfeld module (it is its minimal polynomial - over the function ring), we chose to ommit the characteristic - in the input of the class in order to have concise definitions. - - .. RUBRIC:: Possible base rings - - The morphism `\gamma` does not need be surjective like in the above - examples. This is equivalent to say that the constant coefficient of - the Drinfeld module may be different to the generator of `K` over - `\Fq`. In the following example, `K` is still a degree six extension - of `\Fq`, but `\gamma` is a projection over a degree two extension - with modulus `X^3 + (z_2 + 2)X^2 + (6*z_2 + 1)X + 3z_2 + 5`:: + without explicitly specifying the base. However, the base can be + deduced from the generator, and we omit the base in the input + of the class for conciseness. + + .. RUBRIC:: Possible bases + + The base does not need be surjective like in the above examples. In + the following example, the base codomain is still a degree six + extension of `\Fq`, but the base is a projection over a degree two + extension with modulus `X^3 + (z_2 + 2)X^2 + (6*z_2 + 1)X + 3z_2 + + 5`:: sage: p = X^2 + z2 + 2 sage: p_root = z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z sage: rho = DrinfeldModule(FqX, [p_root, 1, 1]) sage: rho - Drinfeld module defined by X |--> t^2 + t + z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z over Finite Field in z of size 3^12 + Drinfeld module defined by X |--> t^2 + t + z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z - The morphisms `\gamma` are not the same for ``phi`` and ``rho``, and - that the `\gamma` associated to `\phi` is surjective, while the - other one is not:: + Drinfeld modules `\phi` and `\rho` have different based. That of + `\phi` is surjective while that of `\rho` is note:: - sage: rho.category().morphism() + sage: rho.category().base() Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 Defn: X |--> z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z - sage: phi.category().morphism() + sage: phi.category().base() Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 @@ -223,27 +219,22 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1, 1]) sage: sigma - Drinfeld module defined by X |--> t^2 + t + X over Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Drinfeld module defined by X |--> t^2 + t + X over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Defn: X |--> X sage: sigma.is_finite() False sage: phi.is_finite() True - It is possible to change the base ring:: - - sage: L = K.extension(2) - sage: phi_rebased = phi.change_ring(L) - sage: Ltau = phi_rebased.ore_polring() - sage: Ltau(phi(X)) == phi_rebased(X) - True - .. RUBRIC:: The category of Drinfeld modules Drinfeld modules have their own category (see class :class:`sage.categories.drinfeld_modules.DrinfeldModules`):: sage: phi.category() - Category of Drinfeld modules defined by Ring morphism: + Category of Drinfeld modules defined over base Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 Defn: X |--> z @@ -257,8 +248,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: char = phi.category().characteristic() - As the output of :meth:`category` suggests, the morphism `\gamma` - uniquely determines the category. + As the output of :meth:`category` suggests, the base uniquely + determines the category. .. RUBRIC:: Basics @@ -274,8 +265,8 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): This is useful to quickly retrieve the generator of the Drinfeld module. Furthermore, a Drinfeld `\Fq[X]`-module can be seen as an Ore polynomial with positive degree and constant coefficient - `\gamma(X)`. This analogy is the motivation for the following - methods:: + `\gamma(X)`, where `\gamma` is the base. This analogy is the + motivation for the following methods:: sage: phi.coefficients() [z, 1, 1] @@ -287,8 +278,11 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): One can retrieve basic properties:: - sage: phi.base_ring() # K - Finite Field in z of size 3^12 + sage: phi.base() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z sage: phi.ore_polring() # K{t} Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) @@ -357,19 +351,19 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: zero_morphism = hom(0) sage: frobenius_endomorphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - Defn: t^6 + From (gen): t^2 + t + z + To (gen): t^2 + t + z + Defn: t^6 sage: identity_morphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - Defn: 1 + From (gen): t^2 + t + z + To (gen): t^2 + t + z + Defn: 1 sage: zero_morphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - To: Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 - Defn: 0 + From (gen): t^2 + t + z + To (gen): t^2 + t + z + Defn: 0 The underlying Ore polynomial is retrieved with the method :meth:`ore_polynomial`:: @@ -404,7 +398,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z sage: psi = phi.velu(ore_pol) sage: psi - Drinfeld module defined by X |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over Finite Field in z of size 3^12 + Drinfeld module defined by X |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z sage: ore_pol in Hom(phi, psi) True sage: ore_pol * phi(X) == psi(X) * ore_pol @@ -435,7 +432,10 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: action = phi.action() sage: action - Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by X |--> t^2 + t + z over Finite Field in z of size 3^12 + Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by X |--> t^2 + t + z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z The action on elements is computed by calling the action object:: @@ -449,13 +449,6 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: action(FqX.random_element(), 0) 0 - To act on a field larger than `K`, one can change the ring of the - Drinfeld module, then create the action:: - - sage: extended_action = phi.change_ring(K.extension(5)).action() - sage: extended_action - Action on Finite Field in z60 of size 3^60 induced by Drinfeld module defined by X |--> t^2 + t + 2*z60^59 + z60^56 + 2*z60^55 + 2*z60^54 + 2*z60^53 + z60^49 + z60^48 + z60^47 + 2*z60^45 + z60^44 + 2*z60^41 + 2*z60^40 + 2*z60^39 + 2*z60^37 + 2*z60^36 + z60^34 + z60^33 + z60^32 + 2*z60^31 + 2*z60^30 + 2*z60^27 + 2*z60^25 + z60^23 + z60^22 + z60^21 + 2*z60^20 + z60^19 + z60^18 + z60^17 + z60^16 + z60^15 + 2*z60^14 + z60^12 + 2*z60^11 + 2*z60^10 + z60^8 + z60^6 + 2*z60^5 + z60^4 + z60^3 + z60 + 1 over Finite Field in z60 of size 3^60 - .. RUBRIC:: Inverting the Drinfeld module Given an Ore polynomial that equals `\phi_a` for some function ring @@ -473,7 +466,7 @@ class DrinfeldModule(UniqueRepresentation, CategoryObject): sage: phi = DrinfeldModule(FqX, [1, 1]) Traceback (most recent call last): ... - ValueError: function ring base must coerce into base ring + ValueError: function ring base must coerce into base codomain sage: Fq = K = GF(2) sage: FqX. = Fq[] @@ -516,13 +509,13 @@ def __classcall_private__(cls, function_ring, gen, name='t'): if not (hasattr(ore_polring_base, 'has_coerce_map_from') and ore_polring_base.has_coerce_map_from( function_ring.base_ring())): - raise ValueError('function ring base must coerce into base ring') + raise ValueError('function ring base must coerce into base codomain') # Build the morphism that defines the category - gamma = function_ring.hom([ore_polring_base(gen[0])]) + base = function_ring.hom([ore_polring_base(gen[0])]) # Other checks in the category definition - category = DrinfeldModules(gamma, name=name) + category = DrinfeldModules(base, name=name) # Check gen as Ore polynomial if ore_polring is not None and \ @@ -541,7 +534,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): def __init__(self, gen, category): super().__init__(category=category) - self._base_ring = category.base() + self._base = category.base() self._function_ring = category.function_ring() self._gen = gen self._morphism = category._function_ring.hom([gen]) @@ -558,7 +551,7 @@ def __call__(self, a): - ``a`` -- a function ring element - OUTPUT: a base ring element + OUTPUT: an element in the base codomain TESTS: @@ -582,7 +575,7 @@ def __call__(self, a): True sage: a = FqX.random_element(5) - sage: phi(a)[0] == phi.category().morphism()(a) + sage: phi(a)[0] == phi.category().base()(a) True """ return self._morphism(a) @@ -644,12 +637,17 @@ def _latex_(self): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: latex(phi) - \text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }}\Bold{F}_{5^{12}} + \text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }base{ }}\begin{array}{l} + \text{\texttt{Ring{ }morphism:}}\\ + \text{\texttt{{ }{ }From:{ }Univariate{ }Polynomial{ }Ring{ }in{ }X{ }over{ }Finite{ }Field{ }in{ }z2{ }of{ }size{ }5{\char`\^}2}}\\ + \text{\texttt{{ }{ }To:{ }{ }{ }Finite{ }Field{ }in{ }z12{ }of{ }size{ }5{\char`\^}12}}\\ + \text{\texttt{{ }{ }Defn:{ }X{ }|{-}{-}>{ }2*z12{\char`\^}11{ }+{ }2*z12{\char`\^}10{ }+{ }z12{\char`\^}9{ }+{ }3*z12{\char`\^}8{ }+{ }z12{\char`\^}7{ }+{ }2*z12{\char`\^}5{ }+{ }2*z12{\char`\^}4{ }+{ }3*z12{\char`\^}3{ }+{ }z12{\char`\^}2{ }+{ }2*z12}} + \end{array} """ return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ f'{latex(self._function_ring.gen())} '\ f'\\mapsto {latex(self._gen)}' \ - f'\\text{{{{ }}over{{ }}}}{latex(self._base_ring)}' + f'\\text{{{{ }}over{{ }}base{{ }}}}{latex(self._base)}' def _repr_(self): r""" @@ -665,16 +663,19 @@ def _repr_(self): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: phi - Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 """ return f'Drinfeld module defined by {self._function_ring.gen()} ' \ - f'|--> {self._gen} over {self._base_ring}' + f'|--> {self._gen} over base {self._base}' def action(self): r""" Return the action object (:class:`sage.rings.function_field.drinfeld_modules.action.Action`) - that represents the module action, on the base ring, that is + that represents the module action, on the base codomain, that is induced by the Drinfeld module. OUTPUT: a Drinfeld module action object @@ -688,7 +689,10 @@ def action(self): sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: action = phi.action() sage: action - Action on Finite Field in z12 of size 5^12 induced by Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + Action on Finite Field in z12 of size 5^12 induced by Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 The action on elements is computed as follows:: @@ -704,11 +708,11 @@ def action(self): from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction return DrinfeldModuleAction(self) - def base_ring(self): + def base(self): r""" - Return the base ring of the Drinfeld module. + Return the base of the Drinfeld module. - OUTPUT: a field + OUTPUT: a ring morphism EXAMPLES: @@ -717,29 +721,29 @@ def base_ring(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.base_ring() - Finite Field in z12 of size 5^12 - - This is always true:: - - sage: phi.base_ring() is phi.ore_polring().base_ring() - True - sage: phi.base_ring() is K - True + sage: phi.base() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - The base ring can be infinite:: + The base codomain can be infinite:: sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) - sage: sigma.base_ring() - Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + sage: sigma.base() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + Defn: X |--> X - Or it can be ``Fq``:: + And it can also be the base field of the function ring:: sage: psi = DrinfeldModule(FqX, [Fq(1), Fq.gen()]) - sage: psi.base_ring() - Finite Field in z2 of size 5^2 - sage: psi.base_ring() is Fq - True + sage: psi.base() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z2 of size 5^2 + Defn: X |--> 1 In this case the Ore polynomial ring is isomorphic to a regular polynomial ring:: @@ -754,84 +758,17 @@ def base_ring(self): sage: psi.ore_polring().twisting_morphism().is_identity() True - sage: psi.base_ring() is psi.function_ring().base_ring() + sage: psi.base().codomain() is psi.function_ring().base_ring() True """ - return self._base_ring - - def change_ring(self, new_field, name=None): - r""" - Return a Drinfeld module defined like the current one, but whose - base ring ``new_field``. - - The new base is valid whether it has a coercion map from the - current base. - - INPUT: - - - ``new_field`` -- the field extension of the base ring that - serves as base ring for the new Drinfeld module - - OUTPUT: a Drinfeld module - - EXAMPLES: - - The new ring can be an extension of the base ring:: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, p_root^3, 2]) - sage: K2 = K.extension(2) - sage: phi_2 = phi.change_ring(K2) - sage: phi_2 - Drinfeld module defined by X |--> 2*t^2 + (3*z24^23 + 2*z24^22 + 2*z24^20 + z24^19 + 4*z24^18 + 3*z24^17 + 4*z24^15 + 2*z24^13 + 4*z24^12 + 4*z24^11 + 3*z24^10 + 3*z24^9 + 4*z24^8 + 4*z24^6 + 3*z24^5 + 4*z24^4 + 4*z24^3 + 2*z24)*t + 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 over Finite Field in z24 of size 5^24 - - And one can check various things:: - - sage: phi.change_ring(K2).change_ring(K) is phi - True - sage: phi_2.base_ring() is K2 - True - - Naturally, the category has changed:: - - sage: phi_2.category() - Category of Drinfeld modules defined by Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z24 of size 5^24 - Defn: X |--> 2*z24^23 + 2*z24^22 + z24^21 + 2*z24^20 + z24^19 + 2*z24^18 + 3*z24^17 + 2*z24^16 + 4*z24^12 + 3*z24^11 + 4*z24^10 + z24^9 + z24^8 + 3*z24^7 + 2*z24^6 + z24^4 + 4*z24^3 + 3*z24^2 + 3*z24 + 2 - - One can also change the base ring to a subfield, even though some things - do not work as expected:: - - sage: K0 = Fq.extension(2) - sage: phi_0 = phi.change_ring(K0) - sage: phi_0.base_ring() is K0 - True - sage: phi.change_ring(K0).change_ring(K) # known bug - Traceback (most recent call last): - ... - TypeError: no coercion defined - - Furthermore:: - - sage: phi.change_ring(K) is phi - True - """ - coeffs = self._gen.coefficients() - new_coeffs = list(map(new_field, coeffs)) - if name is None: - name = self._ore_polring.variable_name() - return DrinfeldModule(self._function_ring, new_coeffs, name=name) + return self._base def constant_coefficient(self): r""" Return the constant coefficient of the generator. - OUTPUT: a base ring element + OUTPUT: an element in the base codomain EXAMPLES: @@ -843,13 +780,13 @@ def constant_coefficient(self): sage: phi.constant_coefficient() == p_root True - Let `\Fq[X]` be the function ring, and let `\gamma` the morphism - defining the `\Fq[X]`-field structure of the base ring. The - constant coefficient equals `\gamma(X)`:: + Let `\Fq[X]` be the function ring, and let `\gamma` the base of + the Drinfeld module. The constant coefficient equals + `\gamma(X)`:: sage: cat = phi.category() - sage: gamma = cat.morphism() - sage: gamma(X) == phi.constant_coefficient() + sage: base = cat.base() + sage: base(X) == phi.constant_coefficient() True Naturally, two Drinfeld modules in the same category have the @@ -858,7 +795,10 @@ def constant_coefficient(self): sage: t = phi.ore_variable() sage: psi = cat.object(phi.constant_coefficient() + t^3) sage: psi - Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 Reciprocally, it is impossible to create two Drinfeld modules in this category if they do not share the same constant @@ -879,7 +819,7 @@ def coefficient(self, n): - ``n`` -- a non-negative integer - OUTPUT: a base ring element + OUTPUT: an element in the base codomain EXAMPLES: @@ -918,7 +858,7 @@ def coefficients(self, sparse=True): - ``sparse`` -- a boolean - OUTPUT: a list of base ring elements + OUTPUT: a list of elements in the base codomain EXAMPLES: @@ -1074,7 +1014,7 @@ def invert(self, ore_pol): r = self.rank() if ore_pol not in self._ore_polring: raise TypeError('input must be an Ore polynomial') - if ore_pol in self._base_ring: + if ore_pol in self._base.codomain(): return self._Fq(ore_pol) if deg % r != 0: raise ValueError('input must be in the image of the Drinfeld ' @@ -1124,13 +1064,12 @@ def j_invariant(self): Return the j-invariant of the Drinfeld module if the rank is two; raise a NotImplementedError otherwise. - Assume the rank is two. Write the generator `\phi_X = \gamma(X) - + g\tau + \Delta\tau^2`. The j-invariant is defined by + Assume the rank is two. Write the generator `\phi_X = \omega + + g\tau + \Delta\tau^2`. The j-invariant is defined by `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base field - of the function ring. In our case, this base field is always - finite. + of the function ring. In our case, this field is always finite. - OUTPUT: a base ring element + OUTPUT: an element in the base codomain EXAMPLES: @@ -1163,50 +1102,6 @@ def j_invariant(self): return (g**(q+1)) / delta def morphism(self): - r""" - Return the morphism object that defines the Drinfeld module. - - OUTPUT: a ring morphism from the function ring to the Ore - polynomial ring - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.morphism() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) - Defn: X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: from sage.rings.morphism import RingHomomorphism - sage: isinstance(phi.morphism(), RingHomomorphism) - True - - Actually, the ``DrinfeldModule`` method ``__call__`` simply - class the ``__call__`` method of this morphism:: - - sage: phi.morphism()(X) == phi(X) - True - sage: a = FqX.random_element() - sage: phi.morphism()(a) == phi(a) - True - - And many methods of the Drinfeld module have a counterpart in - the morphism object:: - - sage: m = phi.morphism() - sage: m.domain() is phi.function_ring() - True - sage: m.codomain() is phi.ore_polring() - True - sage: m.im_gens() - [z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12] - sage: phi(X) == m.im_gens()[0] - True - """ return self._morphism def ore_polring(self): @@ -1349,7 +1244,10 @@ def velu(self, isog): sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) sage: psi - Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: isog in Hom(phi, psi) True diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index afb02c5eba6..18003d62d3c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -29,7 +29,7 @@ class FiniteDrinfeldModule(DrinfeldModule): r""" This class represents a finite Drinfeld module. - A *finite Drinfeld module* is a Drinfeld module whose base ring is + A *finite Drinfeld module* is a Drinfeld module whose base field is finite. For general definitions and help on Drinfeld modules, see class @@ -70,9 +70,9 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: frobenius_endomorphism = phi.frobenius_endomorphism() sage: frobenius_endomorphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> 5*t^2 + z6 over Finite Field in z6 of size 7^6 - To: Drinfeld module defined by X |--> 5*t^2 + z6 over Finite Field in z6 of size 7^6 - Defn: t^2 + From (gen): 5*t^2 + z6 + To (gen): 5*t^2 + z6 + Defn: t^2 Its characteristic polynomial can be computed:: @@ -129,15 +129,15 @@ def frobenius_endomorphism(self): sage: phi = DrinfeldModule(FqX, [1, 0, z6]) sage: phi.frobenius_endomorphism() Drinfeld Module morphism: - From: Drinfeld module defined by X |--> z6*t^2 + 1 over Finite Field in z6 of size 7^6 - To: Drinfeld module defined by X |--> z6*t^2 + 1 over Finite Field in z6 of size 7^6 - Defn: t^2 + From (gen): z6*t^2 + 1 + To (gen): z6*t^2 + 1 + Defn: t^2 sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism sage: isinstance(phi.frobenius_endomorphism(), DrinfeldModuleMorphism) True """ t = self.ore_variable() - L = self._base_ring + L = self._base.codomain() Fq = self._function_ring.base_ring() deg = L.over(Fq).degree(Fq) return self._Hom_(self, category=self.category())(t**deg) @@ -160,7 +160,7 @@ def frobenius_charpoly(self, var='T'): Let `\chi = T^2 - A(X)T + B(X)` be the characteristic polynomial of the Frobenius endomorphism, let `t^n` be the Ore polynomial that defines the Frobenius endomorphism of `\phi`; by - definition, `n` is the degree of the base ring over `\Fq`. We + definition, `n` is the degree over `\Fq` of the base codomain. We have `\chi(t^n)(\phi(X)) = t^{2n} - \phi_A t^n + \phi_B = 0`, with `\deg(A) \leq \frac{n}{2}` and `\deg(B) = n`. @@ -195,7 +195,7 @@ def frobenius_charpoly(self, var='T'): sage: B (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 - sage: n = 2 # Degree of the base ring over Fq + sage: n = 2 # Degree over Fq of the base codomain sage: A.degree() <= n/2 True sage: B.degree() == n @@ -229,7 +229,7 @@ def frobenius_norm(self): Frobenius endomorphism. The *Frobenius norm* is defined as the polynomial `B(X) \in \Fq[X]`. - Let `n` be the degree of the base ring over `\Fq`. Then the + Let `n` be the degree over `\Fq` of the base codomain. Then the Frobenius norm has degree `n`. OUTPUT: an element in the function ring @@ -244,7 +244,7 @@ def frobenius_norm(self): sage: B (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 - sage: n = 2 # Degree of the base ring over Fq + sage: n = 2 # Degree over Fq of the base codomain sage: B.degree() == n True @@ -257,13 +257,14 @@ def frobenius_norm(self): Gekeler, given in [SM2019]_, Section 4, Proposition 3. """ self._check_rank_two() + L = self._base.codomain().over(self._Fq) # Notations from Schost-Musleh: if self._frobenius_norm is None: - n = self._base_ring.over(self._Fq).degree_over(self._Fq) + n = L.degree_over(self._Fq) d = self.characteristic().degree() m = n // d delta = self._gen[2] - norm = self._base_ring.over(self._Fq)(delta).norm() + norm = L(delta).norm() char = self.characteristic() self._frobenius_norm = ((-1)**n) * (char**m) / norm return self._frobenius_norm @@ -278,7 +279,7 @@ def frobenius_trace(self): Frobenius endomorphism. The *Frobenius norm* is defined as the polynomial `B(X) \in \Fq[X]`. - Let `n` be the degree of the base ring over `\Fq`. Then the + Let `n` be the degree over `\Fq` of the base codomain. Then the Frobenius trace has degree `\leq \frac{n}{2}`. OUTPUT: an element in the function ring @@ -306,7 +307,7 @@ def frobenius_trace(self): sage: A (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 - sage: n = 2 # Degree of the base ring over Fq + sage: n = 2 # Degree over Fq of the base codomain sage: A.degree() <= n/2 True @@ -316,7 +317,7 @@ def frobenius_trace(self): self._check_rank_two() # Notations from Schost-Musleh: if self._frobenius_trace is None: - n = self._base_ring.over(self._Fq).degree_over(self._Fq) + n = self._base.codomain().over(self._Fq).degree_over(self._Fq) B = self.frobenius_norm() t = self.ore_polring().gen() self._frobenius_trace = self.invert(t**n + (self(B) // t**n)) @@ -328,9 +329,9 @@ def is_ordinary(self): NotImplementedError if the rank is not two. A rank two finite Drinfeld module is *ordinary* if and only if - the `\Fq[X]-characteristic of the base ring does not divide the - Frobenius trace. A *supersingular* rank two finite Drinfeld - module is a Drinfeld module that is not ordinary. + the function field-characteristic does not divide the Frobenius + trace. A *supersingular* rank two finite Drinfeld module is a + Drinfeld module that is not ordinary. A rnak two Drinfeld module is *ordinary* if and only if it is note supersingular; see :meth:`is_supersingular`. @@ -367,9 +368,9 @@ def is_supersingular(self): NotImplementedError if the rank is not two. A rank two finite Drinfeld module is *supersingular* if and only - if the function field-characteristic of the base ring divides - the Frobenius trace. An *ordinary* rank two finite Drinfeld - module is a Drinfeld module that is not supersingular. + if the function field-characteristic divides the Frobenius + trace. An *ordinary* rank two finite Drinfeld module is a + Drinfeld module that is not supersingular. OUTPUT: a boolean diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index df176bfafa6..e70a06db2b3 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -45,9 +45,7 @@ class DrinfeldModuleHomset(Homset): sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: hom - Set of Drinfeld module morphisms: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 + Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 sage: from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset sage: isinstance(hom, DrinfeldModuleHomset) @@ -57,9 +55,7 @@ class DrinfeldModuleHomset(Homset): sage: end = End(phi) sage: end - Set of Drinfeld module morphisms: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 + Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + z6*t + z6 sage: end is Hom(phi, phi) True @@ -69,23 +65,23 @@ class DrinfeldModuleHomset(Homset): sage: identity_morphism = end(1) sage: identity_morphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - Defn: 1 + From (gen): 2*t^2 + z6*t + z6 + To (gen): 2*t^2 + z6*t + z6 + Defn: 1 sage: frobenius_endomorphism = end(t^6) sage: frobenius_endomorphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - Defn: t^6 + From (gen): 2*t^2 + z6*t + z6 + To (gen): 2*t^2 + z6*t + z6 + Defn: t^6 sage: isogeny = hom(t + 1) sage: isogeny Drinfeld Module morphism: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 - Defn: t + 1 + From (gen): 2*t^2 + z6*t + z6 + To (gen): 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 + Defn: t + 1 And one can test if an Ore polynomial defines a morphism using the ``in`` syntax:: @@ -146,11 +142,12 @@ def _latex_(self): sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: latex(hom) - \text{Set{ }of{ }Drinfeld{ }module{ }morphisms{ }from}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto 2 t^{2} + z_{6} t + z_{6}\text{{ }over{ }}\Bold{F}_{3^{6}}\text{{ }to{ }}Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 + \text{Set{ }of{ }Drinfeld{ }module{ }morphisms{ }from{ }(gen){ }}2 t^{2} + z_{6} t + z_{6}\text{{ }to{ }(gen){ }}2 t^{2} + \left(2 z_{6}^{5} + 2 z_{6}^{4} + 2 z_{6} + 1\right) t + z_{6} """ return f'\\text{{Set{{ }}of{{ }}Drinfeld{{ }}module{{ }}morphisms' \ - f'{{ }}from}}{latex(self.domain())}\\text{{{{ }}to{{ }}}}' \ - f'{self.codomain()}' + f'{{ }}from{{ }}(gen){{ }}}}{latex(self.domain().gen())}' \ + f'\\text{{{{ }}to{{ }}(gen){{ }}}}'\ + f'{latex(self.codomain().gen())}' def _repr_(self): r""" @@ -167,13 +164,10 @@ def _repr_(self): sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: hom - Set of Drinfeld module morphisms: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 + Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 """ - return f'Set of Drinfeld module morphisms:\n' \ - f' From: {self.domain()}\n' \ - f' To: {self.codomain()}' + return f'Set of Drinfeld module morphisms from (gen) '\ + f'{self.domain().gen()} to (gen) {self.codomain().gen()}' def __contains__(self, x): r""" @@ -254,23 +248,23 @@ def _element_constructor_(self, *args, **kwds): sage: identity_morphism = end(1) sage: identity_morphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - Defn: 1 + From (gen): 2*t^2 + z6*t + z6 + To (gen): 2*t^2 + z6*t + z6 + Defn: 1 sage: frobenius_endomorphism = end(t^6) sage: frobenius_endomorphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - Defn: t^6 + From (gen): 2*t^2 + z6*t + z6 + To (gen): 2*t^2 + z6*t + z6 + Defn: t^6 sage: isogeny = hom(t + 1) sage: isogeny Drinfeld Module morphism: - From: Drinfeld module defined by X |--> 2*t^2 + z6*t + z6 over Finite Field in z6 of size 3^6 - To: Drinfeld module defined by X |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 over Finite Field in z6 of size 3^6 - Defn: t + 1 + From (gen): 2*t^2 + z6*t + z6 + To (gen): 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 + Defn: t + 1 """ # NOTE: This used to be self.element_class(self, ...), but this # would call __init__ instead of __classcall_private__. This diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index d8e8a7adc41..503c9a8fd6b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -29,12 +29,11 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, r""" This class represents a Drinfeld module morphism. - Let `\phi, \psi` be two Drinfeld modules with function ring `\Fq[X]` - and base ring `K`, whose `\Fq[X]`-field structure is given by a - morphism `\gamma`. A *morphism of Drinfeld modules `\phi \to \psi`* - is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a - f` for every `a \in \Fq[X]`. In our case, this is equivalent to `f - \phi_X = \psi_X f`. An *isogeny* is a non-zero morphism. + Let `\phi, \psi` be two Drinfeld modules with base `\Fq[X] \to K`. A + *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial + `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a \in + \Fq[X]`. In our case, this is equivalent to `f \phi_X = \psi_X f`. + An *isogeny* is a non-zero morphism. To create a morphism object, the user should never explicitly instantiate `DrinfeldModuleMorphism`, but rather call the parent @@ -51,19 +50,25 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, sage: morphism = Hom(phi, psi)(ore_pol) sage: morphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 - To: Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 - Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + From (gen): z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + To (gen): (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 We can get basic data on the morphism:: sage: morphism.domain() - Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: morphism.domain() is phi True sage: morphism.codomain() - Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 + Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: morphism.codomain() is psi True @@ -101,9 +106,9 @@ class DrinfeldModuleMorphism(UniqueRepresentation, Element, sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) Drinfeld Module morphism: - From: Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 - To: Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 - Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + From (gen): z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + To (gen): (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) is morphism True """ @@ -149,17 +154,17 @@ def _latex_(self): sage: latex(morphism) \begin{array}{l} \text{Drinfeld{ }module{ }morphism:}\\ - \text{{ }{ }From:{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto t^{2} + t + z_{6}\text{{ }over{ }}\Bold{F}_{2^{6}}}\\ - \text{{ }{ }To:{ }}{ }{ }\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto t^{2} + \left(z_{6}^{4} + z_{6}^{2} + 1\right) t + z_{6}\text{{ }over{ }}\Bold{F}_{2^{6}}\\ + \text{{ }{ }From (gen):{ }}t^{2} + t + z_{6}\\ + \text{{ }{ }To (gen):{ }}{ }{ }t^{2} + \left(z_{6}^{4} + z_{6}^{2} + 1\right) t + z_{6}\\ \text{{ }{ }Defn:{ }}t + z_{6}^{5} + z_{6}^{2} + 1 \end{array} """ return f'\\begin{{array}}{{l}}\n' \ f'\\text{{Drinfeld{{ }}module{{ }}morphism:}}\\\\\n' \ - f'\\text{{{{ }}{{ }}From:{{ }}}}'\ - f'{latex(self._domain)}}}\\\\\n' \ - f'\\text{{{{ }}{{ }}To:{{ }}}}{{ }}{{ }}' \ - f'{latex(self._codomain)}\\\\\n' \ + f'\\text{{{{ }}{{ }}From (gen):{{ }}}}'\ + f'{latex(self._domain.gen())}\\\\\n' \ + f'\\text{{{{ }}{{ }}To (gen):{{ }}}}{{ }}{{ }}' \ + f'{latex(self._codomain.gen())}\\\\\n' \ f'\\text{{{{ }}{{ }}Defn:{{ }}}}' \ f'{latex(self._ore_polynomial)}\n' \ f'\\end{{array}}' @@ -180,14 +185,14 @@ def _repr_(self): sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism Drinfeld Module morphism: - From: Drinfeld module defined by X |--> t^2 + t + z6 over Finite Field in z6 of size 2^6 - To: Drinfeld module defined by X |--> t^2 + (z6^4 + z6^2 + 1)*t + z6 over Finite Field in z6 of size 2^6 - Defn: t + z6^5 + z6^2 + 1 + From (gen): t^2 + t + z6 + To (gen): t^2 + (z6^4 + z6^2 + 1)*t + z6 + Defn: t + z6^5 + z6^2 + 1 """ return f'Drinfeld Module morphism:\n' \ - f' From: {self._domain}\n' \ - f' To: {self._codomain}\n' \ - f' Defn: {self._ore_polynomial}' + f' From (gen): {self._domain.gen()}\n' \ + f' To (gen): {self._codomain.gen()}\n' \ + f' Defn: {self._ore_polynomial}' def codomain(self): r""" @@ -203,7 +208,10 @@ def codomain(self): sage: t = phi.ore_variable() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism.codomain() - Drinfeld module defined by X |--> t^2 + (z6^4 + z6^2 + 1)*t + z6 over Finite Field in z6 of size 2^6 + Drinfeld module defined by X |--> t^2 + (z6^4 + z6^2 + 1)*t + z6 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 2 (using GF2X) + To: Finite Field in z6 of size 2^6 + Defn: X |--> z6 sage: morphism.codomain() is psi True """ @@ -223,7 +231,10 @@ def domain(self): sage: t = phi.ore_variable() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism.domain() - Drinfeld module defined by X |--> t^2 + t + z6 over Finite Field in z6 of size 2^6 + Drinfeld module defined by X |--> t^2 + t + z6 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 2 (using GF2X) + To: Finite Field in z6 of size 2^6 + Defn: X |--> z6 sage: morphism.domain() is phi True """ From cfad9600155f0c1ae0cc95c83cc86f9a3b464bed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 8 Sep 2022 18:00:27 +0200 Subject: [PATCH 116/197] Use Parent instead of CategoryObject in DrinfeldModule --- .../function_field/drinfeld_modules/drinfeld_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 63313a25d1b..7a2fb2c3b8d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -31,12 +31,12 @@ from sage.rings.integer import Integer from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.polynomial_ring import PolynomialRing_general -from sage.structure.category_object import CategoryObject +from sage.structure.parent import Parent from sage.structure.sequence import Sequence from sage.structure.unique_representation import UniqueRepresentation -class DrinfeldModule(UniqueRepresentation, CategoryObject): +class DrinfeldModule(Parent, UniqueRepresentation): r""" This class represents a Drinfeld module. @@ -533,13 +533,13 @@ def __classcall_private__(cls, function_ring, gen, name='t'): return cls.__classcall__(cls, gen, category) def __init__(self, gen, category): - super().__init__(category=category) self._base = category.base() self._function_ring = category.function_ring() self._gen = gen self._morphism = category._function_ring.hom([gen]) self._ore_polring = gen.parent() self._Fq = self._function_ring.base_ring() # Must be last + super().__init__(base=self._base, category=category) def __call__(self, a): r""" From dbcbaf74fc617f8605466fce3223abccc8874053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 8 Sep 2022 18:00:49 +0200 Subject: [PATCH 117/197] Implement base_ring in DrinfeldModule Why? See docstring of the method. --- .../drinfeld_modules/drinfeld_module.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 7a2fb2c3b8d..bc6fc8e76ac 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -764,6 +764,18 @@ def base(self): """ return self._base + def base_ring(self): + r""" + Raise an exception. + + The base of a Drinfeld module is a ring morphism, not a ring. + + This method is implemented in CategoryObjects, of which + Parent inherits. It returns the base of the category. I + overloaded it to avoid confusion. + """ + raise TypeError('the base of a Drinfeld module is a morphism') + def constant_coefficient(self): r""" Return the constant coefficient of the generator. From 771f406ec3e9d4fb0317da89af4b3b0abc2bf788 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 8 Sep 2022 19:08:15 +0200 Subject: [PATCH 118/197] Use Category_over_base instead of CategoryWithParameters for DrinfeldModules --- src/sage/categories/drinfeld_modules.py | 36 ++++--------------------- 1 file changed, 5 insertions(+), 31 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 27480adb460..5a85c3e31d3 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -18,8 +18,9 @@ # http://www.gnu.org/licenses/ #****************************************************************************** -from sage.categories.category import CategoryWithParameters +from sage.categories.category_types import Category_over_base from sage.categories.homsets import Homsets +from sage.misc.cachefunc import cached_method from sage.misc.functional import log from sage.misc.latex import latex from sage.rings.integer import Integer @@ -28,7 +29,7 @@ from sage.rings.polynomial.polynomial_ring import PolynomialRing_general -class DrinfeldModules(CategoryWithParameters): +class DrinfeldModules(Category_over_base): r""" This class represents the category of Drinfeld modules on a given base. @@ -229,10 +230,7 @@ def __init__(self, base, name='t'): self._characteristic = FqX(E(base(X)).minpoly()) elif FqX.is_subring(K): self._characteristic = Integer(0) - - # Somehow required for the class definition - def _make_named_class_key(self, name): - return self._function_ring.category() + super().__init__(base=base) def _latex_(self): r""" @@ -289,31 +287,6 @@ def Homsets(self): def Endsets(self): return Homsets() - def base(self): - r""" - Return the base of the category. - - OUTPUT: a ring morphism - - EXAMPLES: - - sage: Fq = GF(11) - sage: FqX. = Fq[] - sage: K. = Fq.extension(4) - sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: base = cat.base() - sage: base - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^4 - Defn: X |--> z^3 + 7*z^2 + 6*z + 10 - sage: base(X) == cat.constant_coefficient() - True - """ - return self._base - def characteristic(self): r""" Return the function ring-characteristic of the category. @@ -507,6 +480,7 @@ def random_object(self, rank): return self.object(coeffs) # Somehow required for the class definition + @cached_method def super_categories(self): return [] From 674faeb30a406932eb67c44a126b561a7cf03e3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 8 Sep 2022 19:27:39 +0200 Subject: [PATCH 119/197] Make DrinfeldModuleMorphism inherit Morphism As DrinfeldModule now inherits Parent, this is possible. --- .../drinfeld_modules/morphism.py | 82 +------------------ 1 file changed, 3 insertions(+), 79 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 503c9a8fd6b..391aa44855a 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -20,12 +20,12 @@ from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.misc.latex import latex -from sage.structure.element import Element +from sage.categories.morphism import Morphism from sage.structure.unique_representation import UniqueRepresentation -class DrinfeldModuleMorphism(UniqueRepresentation, Element, - metaclass=InheritComparisonClasscallMetaclass): +class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, + metaclass=InheritComparisonClasscallMetaclass): r""" This class represents a Drinfeld module morphism. @@ -194,52 +194,6 @@ def _repr_(self): f' To (gen): {self._codomain.gen()}\n' \ f' Defn: {self._ore_polynomial}' - def codomain(self): - r""" - Return the codomain of the morphism. - - EXAMPLES: - - sage: Fq = GF(2) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() - sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) - sage: morphism.codomain() - Drinfeld module defined by X |--> t^2 + (z6^4 + z6^2 + 1)*t + z6 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 2 (using GF2X) - To: Finite Field in z6 of size 2^6 - Defn: X |--> z6 - sage: morphism.codomain() is psi - True - """ - return self._codomain - - def domain(self): - r""" - Return the codomain of the morphism. - - EXAMPLES: - - sage: Fq = GF(2) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() - sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) - sage: morphism.domain() - Drinfeld module defined by X |--> t^2 + t + z6 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 2 (using GF2X) - To: Finite Field in z6 of size 2^6 - Defn: X |--> z6 - sage: morphism.domain() is phi - True - """ - return self._domain - def is_zero(self): r""" Return ``True`` whether the morphism is the zero morphism. @@ -262,36 +216,6 @@ def is_zero(self): """ return self._ore_polynomial.is_zero() - def is_endomorphism(self): - r""" - Return ``True`` whether the morphism is an endomorphism. - - EXAMPLES: - - sage: Fq = GF(2) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() - sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) - sage: morphism.is_endomorphism() - False - - sage: zero_morphism = End(phi)(0) - sage: zero_morphism.is_endomorphism() - True - - sage: identity_morphism = End(phi)(1) - sage: identity_morphism.is_endomorphism() - True - - sage: frobenius_endomorphism = phi.frobenius_endomorphism() - sage: frobenius_endomorphism.is_endomorphism() - True - """ - return self._domain is self._codomain - def is_isogeny(self): r""" Return ``True`` whether the morphism is an isogeny. From d597953e9c51bd2474c263d7c0c49730511125f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 9 Sep 2022 10:52:01 +0200 Subject: [PATCH 120/197] Remove methods ore_variable from Drinfeld module classes --- src/sage/categories/drinfeld_modules.py | 32 ++------- .../drinfeld_modules/drinfeld_module.py | 65 ++++--------------- .../finite_drinfeld_module.py | 2 +- .../function_field/drinfeld_modules/homset.py | 6 +- .../drinfeld_modules/morphism.py | 14 ++-- 5 files changed, 27 insertions(+), 92 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 5a85c3e31d3..5ca42f87ce4 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -47,11 +47,10 @@ class DrinfeldModules(Category_over_base): These notations will be used throughout this documentation. We say that `\Fq[X]` is the *function ring of the category*; - *K\{\tau\}* is the *Ore polynomial ring of the category*; `t` is the - *Ore variable of the category*. The *constant coefficient of the - category* is the image of `X` under the base. The - `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also be - referred to as its *function ring-characteristic*. + *K\{\tau\}* is the *Ore polynomial ring of the category*. The + *constant coefficient of the category* is the image of `X` under the + base. The `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also + be referred to as its *function ring-characteristic*. INPUT: the base, a ring morphism @@ -385,7 +384,7 @@ def object(self, gen): From: Univariate Polynomial Ring in X over Finite Field of size 11 To: Finite Field in z of size 11^4 Defn: X |--> z^3 + 7*z^2 + 6*z + 10 - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: cat.object(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi True """ @@ -420,27 +419,6 @@ def ore_polring(self): """ return self._ore_polring - def ore_variable(self): - r""" - Return the Ore variable of the category. - - OUTPUT: an Ore polynomial - - EXAMPLES: - - sage: Fq = GF(11) - sage: FqX. = Fq[] - sage: K. = Fq.extension(4) - sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat.ore_variable() - t - sage: cat.ore_variable() is phi.ore_variable() - True - """ - return self._ore_polring.gen() - def random_object(self, rank): r""" Return a random Drinfeld module in the category, whose rank is diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index bc6fc8e76ac..1740c3cdbfa 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -68,11 +68,11 @@ class DrinfeldModule(Parent, UniqueRepresentation): `\phi_X` of `X`, which is an input of the class. We say that `\Fq[X]` is the *function ring of `\phi`*; *K\{\tau\}* - is the *Ore polynomial ring of `\phi`*; `t` is the *Ore variable of - `\phi`*. Further, the *generator of `\phi`* is `\phi_X` and its - *constant coefficient* is the constant coefficient of `\phi_X`. The - `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also be referred to as - its *function ring-characteristic*. + is the *Ore polynomial ring of `\phi`*. Further, the *generator of + `\phi`* is `\phi_X` and its *constant coefficient* is the constant + coefficient of `\phi_X`. The `\Fq[X]`-characteristic of the + `\Fq[X]`-field `K` can also be referred to as its *function + ring-characteristic*. Classical references on Drinfeld modules include [Gos1998]_, [Rosen2002]_, [VS06]_ and [Gek1998]_. @@ -92,7 +92,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` (optional) -- the name of the Ore variable + - ``name`` (optional) -- the name of the Ore polynomial ring gen .. RUBRIC:: Construction @@ -113,7 +113,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): also use regular Ore polynomials:: sage: ore_polring = phi.ore_polring() - sage: t = phi.ore_variable() # same as ore_polring.gen() + sage: t = phi.ore_polring().gen() sage: psi_X = z + t^3 sage: psi = DrinfeldModule(FqX, psi_X) sage: psi @@ -287,9 +287,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.ore_polring() # K{t} Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) - sage: phi.ore_variable() # t - t - sage: phi.function_ring() # Fq[X] Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 @@ -603,7 +600,7 @@ def _Hom_(self, other, category): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) sage: hom = phi._Hom_(psi, category=phi.category()) @@ -804,7 +801,7 @@ def constant_coefficient(self): Naturally, two Drinfeld modules in the same category have the same constant coefficient:: - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: psi = cat.object(phi.constant_coefficient() + t^3) sage: psi Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: @@ -987,7 +984,7 @@ def invert(self, ore_pol): When the input is not in the image of the Drinfeld module, an exception is raised:: - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: phi.invert(t + 1) Traceback (most recent call last): ... @@ -1144,49 +1141,9 @@ def ore_polring(self): sage: phi(X) in ore_polring True - - The Ore variable is just the generator of the Ore polynomial - ring:: - - sage: ore_polring.gen() - t - sage: phi.ore_variable() is ore_polring.gen() - True """ return self._ore_polring - def ore_variable(self): - r""" - Return the Ore variable. - - OUTPUT: an Ore polynomial - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.ore_variable() - t - sage: phi.ore_variable() is phi.ore_polring().gen() - True - - One can use the Ore variable to instanciate new Drinfeld - modules...:: - - sage: t = phi.ore_variable() - sage: psi_X = phi.constant_coefficient() + 3*t + 2*t^4 - sage: psi = DrinfeldModule(FqX, psi_X) - - ...or morphisms and isogenies:: - - sage: t^6 in End(phi) # Frobenius endomorphism - True - """ - return self._ore_polring.gen() - def rank(self): r""" Return the rank of the Drinfeld module. @@ -1252,7 +1209,7 @@ def velu(self, isog): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) sage: psi diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 18003d62d3c..6d02da7dff9 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -136,7 +136,7 @@ def frobenius_endomorphism(self): sage: isinstance(phi.frobenius_endomorphism(), DrinfeldModuleMorphism) True """ - t = self.ore_variable() + t = self.ore_polring().gen() L = self._base.codomain() Fq = self._function_ring.base_ring() deg = L.over(Fq).degree(Fq) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index e70a06db2b3..43cd91849f7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -61,7 +61,7 @@ class DrinfeldModuleHomset(Homset): One can create morphism objects by calling the homset:: - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: identity_morphism = end(1) sage: identity_morphism Drinfeld Module morphism: @@ -191,7 +191,7 @@ def __contains__(self, x): sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: end = End(phi) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: 1 in hom False @@ -244,7 +244,7 @@ def _element_constructor_(self, *args, **kwds): sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: end = End(phi) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: identity_morphism = end(1) sage: identity_morphism Drinfeld Module morphism: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 391aa44855a..1aa36eba277 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -44,7 +44,7 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: ore_pol = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(ore_pol) sage: morphism = Hom(phi, psi)(ore_pol) @@ -149,7 +149,7 @@ def _latex_(self): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: latex(morphism) \begin{array}{l} @@ -181,7 +181,7 @@ def _repr_(self): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism Drinfeld Module morphism: @@ -205,7 +205,7 @@ def is_zero(self): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism.is_zero() False @@ -227,7 +227,7 @@ def is_isogeny(self): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism.is_isogeny() True @@ -257,7 +257,7 @@ def is_isomorphism(self): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism.is_isomorphism() False @@ -287,7 +287,7 @@ def ore_polynomial(self): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) - sage: t = phi.ore_variable() + sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: ore_pol = morphism.ore_polynomial() sage: ore_pol From 2ca21832b445169e246edce63498f01e53f76a42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 9 Sep 2022 10:54:22 +0200 Subject: [PATCH 121/197] Remove `class ElementMethods` from DrinfeldModules This seems to change nothing. --- src/sage/categories/drinfeld_modules.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 5ca42f87ce4..c2fdf902cc7 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -467,7 +467,3 @@ class ParentMethods: def characteristic(self): return self.category().characteristic() - - # Somehow required for the class definition - class ElementMethods: - pass From 15efff9de6413a48c7f8bf000466c341485fb4d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 9 Sep 2022 10:58:23 +0200 Subject: [PATCH 122/197] (Fix) Add doc of morphism method in DrinfeldModule It was deleted by accident. --- .../drinfeld_modules/drinfeld_module.py | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 1740c3cdbfa..b3007bc171b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1111,6 +1111,50 @@ def j_invariant(self): return (g**(q+1)) / delta def morphism(self): + r""" + Return the morphism object that defines the Drinfeld module. + + OUTPUT: a ring morphism from the function ring to the Ore + polynomial ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + Defn: X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: from sage.rings.morphism import RingHomomorphism + sage: isinstance(phi.morphism(), RingHomomorphism) + True + + Actually, the ``DrinfeldModule`` method ``__call__`` simply + class the ``__call__`` method of this morphism:: + + sage: phi.morphism()(X) == phi(X) + True + sage: a = FqX.random_element() + sage: phi.morphism()(a) == phi(a) + True + + And many methods of the Drinfeld module have a counterpart in + the morphism object:: + + sage: m = phi.morphism() + sage: m.domain() is phi.function_ring() + True + sage: m.codomain() is phi.ore_polring() + True + sage: m.im_gens() + [z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12] + sage: phi(X) == m.im_gens()[0] + True + """ return self._morphism def ore_polring(self): From 2e7745256a9c152d975b77de812bb31efed9414d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 9 Sep 2022 11:17:45 +0200 Subject: [PATCH 123/197] Enhance base and base ring distinctions in doc --- src/sage/categories/drinfeld_modules.py | 17 +++++++++++------ .../function_field/drinfeld_modules/action.py | 10 ++++------ .../drinfeld_modules/drinfeld_module.py | 8 +++++--- .../drinfeld_modules/finite_drinfeld_module.py | 13 +++++++------ .../function_field/drinfeld_modules/morphism.py | 10 +++++----- 5 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index c2fdf902cc7..a3e7f8d6c9d 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -34,10 +34,14 @@ class DrinfeldModules(Category_over_base): This class represents the category of Drinfeld modules on a given base. - The category is uniquely defined by its base, which is a ring - morphism from the function ring `\Fq[X]` to a field `K`. Note that - the base is a morphism, but not a field. The base is often denoted - `\gamma`, and we call `K` an *`\Fq[X]-field`*. + Let `\Fq[X]` be a polynomial ring with coefficients in a finite + field `\Fq` and let `K` be a field. We fix a ring morphism `\gamma: + \Fq[X] \to K`, which we call the *base* of the category (it is the + base of the Drinfeld modules in the category). + Please note that the base is not a ring; in particular, it is + not the field `K`. We also call `K` an *`\Fq[X]`-field*. + + The category is uniquely defined by its base. The monic polynomial that generates the kernel of the base is called the *`\Fq[X]`-characteristic of the `\Fq[X]`-field `K`*. @@ -50,7 +54,8 @@ class DrinfeldModules(Category_over_base): *K\{\tau\}* is the *Ore polynomial ring of the category*. The *constant coefficient of the category* is the image of `X` under the base. The `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also - be referred to as its *function ring-characteristic*. + be referred to as its *function ring-characteristic*. Finally, `K` + is just refered to as the codomain base. INPUT: the base, a ring morphism @@ -288,7 +293,7 @@ def Endsets(self): def characteristic(self): r""" - Return the function ring-characteristic of the category. + Return the function ring-characteristic. OUTPUT: `0` or a monic prime polynomial in the function ring diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index fbc1bbfe47b..4b348bff6e2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -29,11 +29,9 @@ class DrinfeldModuleAction(Action): This class represents the module action induced by a Drinfeld module. - Let `\phi` be a Drinfeld module with function ring `\Fq[X]` and base - ring `K`, whose `\Fq[X]`-field structure is given by a morphism - `\gamma`. Let `L/K` be a field extension, let `x \in L`, let `a` be - a function ring element; the action is defined as `(a, x) \mapsto - \phi_a(x)`. + Let `\phi` be a Drinfeld module with base `\gamma: \Fq[X] \to K`. + Let `L/K` be a field extension, let `x \in L`, let `a` be a function + ring element; the action is defined as `(a, x) \mapsto \phi_a(x)`. .. NOTE:: @@ -92,7 +90,7 @@ def _act_(self, pol, x): - ``pol`` -- a function ring element - ``x`` -- an element in the field acted upon - OUTPUT: an element in the base field of the Drinfeld module + OUTPUT: an element in the base codomain EXAMPLES: diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index b3007bc171b..c1e61416d1e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -43,7 +43,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): Let `\Fq[X]` be a polynomial ring with coefficients in a finite field `\Fq` and let `K` be a field. We fix a ring morphism `\gamma: \Fq[X] \to K`, which we call the *base* of the Drinfeld module. - We also call `K` an *`\Fq[X]`-field*. + Please note that the base is not a ring; in particular, it is + not the field `K`. We also call `K` an *`\Fq[X]`-field*. .. NOTE:: @@ -72,7 +73,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): `\phi`* is `\phi_X` and its *constant coefficient* is the constant coefficient of `\phi_X`. The `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also be referred to as its *function - ring-characteristic*. + ring-characteristic*. Finally, `K` is just refered to as the + codomain base. Classical references on Drinfeld modules include [Gos1998]_, [Rosen2002]_, [VS06]_ and [Gek1998]_. @@ -1075,7 +1077,7 @@ def j_invariant(self): Assume the rank is two. Write the generator `\phi_X = \omega + g\tau + \Delta\tau^2`. The j-invariant is defined by - `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base field + `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base ring of the function ring. In our case, this field is always finite. OUTPUT: an element in the base codomain diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 6d02da7dff9..3ccb00cd361 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -29,8 +29,9 @@ class FiniteDrinfeldModule(DrinfeldModule): r""" This class represents a finite Drinfeld module. - A *finite Drinfeld module* is a Drinfeld module whose base field is - finite. + A *finite Drinfeld module* is a Drinfeld module whose base, which is + a morphism, is surjective. This is equivalent to say that the base + codomain is a finite field. For general definitions and help on Drinfeld modules, see class :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. @@ -115,7 +116,7 @@ def frobenius_endomorphism(self): Return the Frobenius endomorphism of the Drinfeld module as a morphism object. - Let `q` be the order of the base field of the function ring. The + Let `q` be the order of the base ring of the function ring. The *Frobenius endomorphism* is defined as the endomorphism whose defining Ore polynomial is `t^q`. @@ -148,7 +149,7 @@ def frobenius_charpoly(self, var='T'): endomorphism, if the rank is two; raise a NotImplementedError otherwise. - Let `\Fq` be the base field of the function ring. The + Let `\Fq` be the base ring of the function ring. The *characteristic polynomial `\chi` of the Frobenius endomorphism* is defined in [Gek1991]_. An important feature of this polynomial is that it is a monic univariate polynomial with @@ -329,7 +330,7 @@ def is_ordinary(self): NotImplementedError if the rank is not two. A rank two finite Drinfeld module is *ordinary* if and only if - the function field-characteristic does not divide the Frobenius + the function ring-characteristic does not divide the Frobenius trace. A *supersingular* rank two finite Drinfeld module is a Drinfeld module that is not ordinary. @@ -368,7 +369,7 @@ def is_supersingular(self): NotImplementedError if the rank is not two. A rank two finite Drinfeld module is *supersingular* if and only - if the function field-characteristic divides the Frobenius + if the function ring-characteristic divides the Frobenius trace. An *ordinary* rank two finite Drinfeld module is a Drinfeld module that is not supersingular. diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 1aa36eba277..d80ff19643b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -29,11 +29,11 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, r""" This class represents a Drinfeld module morphism. - Let `\phi, \psi` be two Drinfeld modules with base `\Fq[X] \to K`. A - *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial - `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a \in - \Fq[X]`. In our case, this is equivalent to `f \phi_X = \psi_X f`. - An *isogeny* is a non-zero morphism. + Let `\phi, \psi` be two Drinfeld modules with base `\gamma: \Fq[X] + \to K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore + polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for + every `a \in \Fq[X]`. In our case, this is equivalent to `f \phi_X = + \psi_X f`. An *isogeny* is a non-zero morphism. To create a morphism object, the user should never explicitly instantiate `DrinfeldModuleMorphism`, but rather call the parent From 4412ad191092e9d5bab82d1ad73a669dc83d6f96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Fri, 9 Sep 2022 11:29:58 +0200 Subject: [PATCH 124/197] Move 'category methods' from DrinfeldModule to DrinfeldModules Methods of DrinfeldModule that are a getter to a property of the category (e.g. base or function_ring) are now declared in the `class ParentMethods` part of the category. --- src/sage/categories/drinfeld_modules.py | 186 ++++++++++++++++++ .../drinfeld_modules/drinfeld_module.py | 164 --------------- 2 files changed, 186 insertions(+), 164 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index a3e7f8d6c9d..485372a96ab 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -470,5 +470,191 @@ def super_categories(self): # Somehow required for the class definition class ParentMethods: + def base(self): + r""" + Return the base of the Drinfeld module. + + OUTPUT: a ring morphism + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.base() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + + The base codomain can be infinite:: + + sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) + sage: sigma.base() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + Defn: X |--> X + + And it can also be the base field of the function ring:: + + sage: psi = DrinfeldModule(FqX, [Fq(1), Fq.gen()]) + sage: psi.base() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z2 of size 5^2 + Defn: X |--> 1 + + In this case the Ore polynomial ring is isomorphic to a regular + polynomial ring:: + + sage: psi.ore_polring() + Ore Polynomial Ring in t over Finite Field in z2 of size 5^2 twisted by Identity + sage: psi.ore_polring().twisting_morphism() + Identity endomorphism of Finite Field in z2 of size 5^2 + + TESTS:: + + sage: psi.ore_polring().twisting_morphism().is_identity() + True + + sage: psi.base().codomain() is psi.function_ring().base_ring() + True + + """ + return self.category().base() + + def base_ring(self): + r""" + Raise an exception. + + The base of a Drinfeld module is a ring morphism, not a ring. + + This method is implemented in CategoryObjects, of which + Parent inherits. It returns the base of the category. I + overloaded it to avoid confusion. + """ + raise TypeError('the base of a Drinfeld module is a morphism') + def characteristic(self): + r""" + Return the function ring-characteristic. + + OUTPUT: a univariate polynomial ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.characteristic() + X^2 + (4*z2 + 2)*X + 2 + sage: phi.characteristic()(phi.constant_coefficient()) + 0 + + sage: L = Frac(FqX) + sage: psi = DrinfeldModule(FqX, [L(1), 0, 0, L(1)]) + sage: psi.characteristic() + 0 + """ return self.category().characteristic() + + def function_ring(self): + r""" + Return the function ring of the Drinfeld module. + + OUTPUT: a univariate polynomial ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.function_ring() is FqX + True + """ + return self.category().function_ring() + + def constant_coefficient(self): + r""" + Return the constant coefficient of the generator. + + OUTPUT: an element in the base codomain + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.constant_coefficient() == p_root + True + + Let `\Fq[X]` be the function ring, and let `\gamma` the base of + the Drinfeld module. The constant coefficient equals + `\gamma(X)`:: + + sage: cat = phi.category() + sage: base = cat.base() + sage: base(X) == phi.constant_coefficient() + True + + Naturally, two Drinfeld modules in the same category have the + same constant coefficient:: + + sage: t = phi.ore_polring().gen() + sage: psi = cat.object(phi.constant_coefficient() + t^3) + sage: psi + Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + + Reciprocally, it is impossible to create two Drinfeld modules in + this category if they do not share the same constant + coefficient:: + + sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) + Traceback (most recent call last): + ... + ValueError: constant coefficient must be the generator of the morphism that defines the category + """ + return self.category().constant_coefficient() + + def ore_polring(self): + r""" + Return the Ore polynomial ring of the Drinfeld module. + + OUTPUT: an Ore polynomial ring + + EXAMPLES: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: ore_polring = phi.ore_polring() + sage: ore_polring + Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + + The Ore polynomial ring can also be retrieved from the category + of the Drinfeld module:: + + sage: ore_polring is phi.category().ore_polring() + True + + The generator of the Drinfeld module is in the Ore polynomial + ring:: + + sage: phi(X) in ore_polring + True + """ + return self.category().ore_polring() diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index c1e61416d1e..29de3ba191b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -707,121 +707,6 @@ def action(self): from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction return DrinfeldModuleAction(self) - def base(self): - r""" - Return the base of the Drinfeld module. - - OUTPUT: a ring morphism - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.base() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - - The base codomain can be infinite:: - - sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) - sage: sigma.base() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - Defn: X |--> X - - And it can also be the base field of the function ring:: - - sage: psi = DrinfeldModule(FqX, [Fq(1), Fq.gen()]) - sage: psi.base() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z2 of size 5^2 - Defn: X |--> 1 - - In this case the Ore polynomial ring is isomorphic to a regular - polynomial ring:: - - sage: psi.ore_polring() - Ore Polynomial Ring in t over Finite Field in z2 of size 5^2 twisted by Identity - sage: psi.ore_polring().twisting_morphism() - Identity endomorphism of Finite Field in z2 of size 5^2 - - TESTS:: - - sage: psi.ore_polring().twisting_morphism().is_identity() - True - - sage: psi.base().codomain() is psi.function_ring().base_ring() - True - - """ - return self._base - - def base_ring(self): - r""" - Raise an exception. - - The base of a Drinfeld module is a ring morphism, not a ring. - - This method is implemented in CategoryObjects, of which - Parent inherits. It returns the base of the category. I - overloaded it to avoid confusion. - """ - raise TypeError('the base of a Drinfeld module is a morphism') - - def constant_coefficient(self): - r""" - Return the constant coefficient of the generator. - - OUTPUT: an element in the base codomain - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.constant_coefficient() == p_root - True - - Let `\Fq[X]` be the function ring, and let `\gamma` the base of - the Drinfeld module. The constant coefficient equals - `\gamma(X)`:: - - sage: cat = phi.category() - sage: base = cat.base() - sage: base(X) == phi.constant_coefficient() - True - - Naturally, two Drinfeld modules in the same category have the - same constant coefficient:: - - sage: t = phi.ore_polring().gen() - sage: psi = cat.object(phi.constant_coefficient() + t^3) - sage: psi - Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - - Reciprocally, it is impossible to create two Drinfeld modules in - this category if they do not share the same constant - coefficient:: - - sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) - Traceback (most recent call last): - ... - ValueError: constant coefficient must be the generator of the morphism that defines the category - """ - return self.coefficient(0) - def coefficient(self, n): r""" Return the n-th coefficient of the generator. @@ -899,24 +784,6 @@ def coefficients(self, sparse=True): """ return self._gen.coefficients(sparse=sparse) - def function_ring(self): - r""" - Return the function ring of the Drinfeld module. - - OUTPUT: a univariate polynomial ring - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.function_ring() is FqX - True - """ - return self._function_ring - def gen(self): r""" Return the generator of the Drinfeld module. @@ -1159,37 +1026,6 @@ class the ``__call__`` method of this morphism:: """ return self._morphism - def ore_polring(self): - r""" - Return the Ore polynomial ring of the Drinfeld module. - - OUTPUT: an Ore polynomial ring - - EXAMPLES: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: ore_polring = phi.ore_polring() - sage: ore_polring - Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) - - The Ore polynomial ring can also be retrieved from the category - of the Drinfeld module:: - - sage: ore_polring is phi.category().ore_polring() - True - - The generator of the Drinfeld module is in the Ore polynomial - ring:: - - sage: phi(X) in ore_polring - True - """ - return self._ore_polring - def rank(self): r""" Return the rank of the Drinfeld module. From 52e6d06336bc0802624670c4a4c6f43c6e24961b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 20 Sep 2022 13:37:40 +0200 Subject: [PATCH 125/197] (minor) Change a comment in a doctstring --- .../function_field/drinfeld_modules/finite_drinfeld_module.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 3ccb00cd361..82e2d304048 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -383,8 +383,7 @@ def is_supersingular(self): sage: phi = DrinfeldModule(FqX, [1, 0, z6]) sage: phi.is_supersingular() True - sage: phi_p = phi(phi.characteristic()) - sage: phi_p # Purely inseparable + sage: phi(phi.characteristic()) # Purely inseparable z6*t^2 ALGORITHM: From d6174065977780a9fdaa8d7de4131f1c52e421c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 20 Sep 2022 13:31:59 +0200 Subject: [PATCH 126/197] Add a better error message in DrinfeldModules when the characteristic is not implemented --- src/sage/categories/drinfeld_modules.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 485372a96ab..61d0482af48 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -320,7 +320,8 @@ def characteristic(self): 0 """ if self._characteristic is None: - raise NotImplementedError + raise NotImplementedError('function ring characteristic not' \ + 'implemented in this case') return self._characteristic def constant_coefficient(self): From 1eab9ee6a594ee1f50148325cbdc23632d1ffc1b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 20 Sep 2022 13:33:31 +0200 Subject: [PATCH 127/197] Fix height method The height used to return always 1. This was a mistake (despite being the generic case) that is now corrected. For example, in rank two, a Drinfeld module is supersingular if and only if its height is 2. --- .../drinfeld_modules/drinfeld_module.py | 49 +++++++++++++++++-- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 29de3ba191b..31aff8ced91 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -306,7 +306,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) Defn: X |--> t^2 + t + z - One can compute the rank and height (which is always `1`):: + One can compute the rank and height:: sage: phi.rank() 2 @@ -804,9 +804,23 @@ def gen(self): def height(self): r""" - Return the height of the Drinfeld module. + Return the height of the Drinfeld module if the function field + characteristic is a prime ideal; raise ValueError otherwise. - In our case, the height is always 1. + The height of a Drinfeld module is defined when the function + field characteristic is a prime ideal. In our case, this ideal + is even generated by a monic polynomial `\mathfrak{p}` in the + function field. Write `\phi_\mathfrak{p} = a_s \tau^s + \dots + + \tau^{r*\deg(\mathfrak{p})}`. The *height* of the Drinfeld + module is the well-defined positive integer `h = + \frac{s}{\deg(\mathfrak{p})}`. + + .. NOTE:: + + See [Gos1998]_, Definition 4.5.8 for the general definition. + + A rank two Drinfeld module is supersingular if and only if its + height equals its rank. OUTPUT: an integer @@ -819,8 +833,35 @@ def height(self): sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: phi.height() == 1 True + sage: phi.is_ordinary() + True + + sage: L = Frac(FqX) + sage: phi = DrinfeldModule(FqX, [L(2), L(1)]) + sage: phi.height() + Traceback (most recent call last): + ... + ValueError: height is defined for prime function field characteristic + + sage: Fq = GF(343) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi.height() + 2 + sage: phi.is_supersingular() + True + """ - return Integer(1) + try: + if self.characteristic().is_zero(): + raise ValueError('height is defined for prime ' \ + 'function field characteristic') + else: + p = self.characteristic() + return Integer((self(p).valuation()) // (p.degree())) + except NotImplementedError: + raise NotImplementedError('height not implemented in this case') def invert(self, ore_pol): r""" From bcdfb60284efc88d246625b61b72ae86773ee6d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 20 Sep 2022 13:37:07 +0200 Subject: [PATCH 128/197] Fix definition of finite drinfeld module in FiniteDrinfeldModule --- .../drinfeld_modules/finite_drinfeld_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 82e2d304048..a64af067a34 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -29,9 +29,9 @@ class FiniteDrinfeldModule(DrinfeldModule): r""" This class represents a finite Drinfeld module. - A *finite Drinfeld module* is a Drinfeld module whose base, which is - a morphism, is surjective. This is equivalent to say that the base - codomain is a finite field. + A *finite Drinfeld module* is a Drinfeld module whose base codomain + is a finite field. In this case, the function field characteristic + is a prime ideal. For general definitions and help on Drinfeld modules, see class :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. From 0cdd80f5c8b694e67151b97b9eeef36e5340e086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 21 Sep 2022 12:13:15 +0200 Subject: [PATCH 129/197] (fix) Fix function_field/all.py Thank you David, see Ticket 33713, comment 125. --- src/sage/rings/function_field/all.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/function_field/all.py b/src/sage/rings/function_field/all.py index 6904f3ef4fb..17cf87636e3 100644 --- a/src/sage/rings/function_field/all.py +++ b/src/sage/rings/function_field/all.py @@ -1 +1,2 @@ +from .constructor import FunctionField from .drinfeld_modules.all import * From 8052550f00cd9ae9e8ba0e83a5c84022d462b361 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 22 Sep 2022 14:46:26 +0200 Subject: [PATCH 130/197] Remove .all import See ticket 33713, comment 247. --- src/sage/rings/function_field/all.py | 2 +- src/sage/rings/function_field/drinfeld_modules/all.py | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) delete mode 100644 src/sage/rings/function_field/drinfeld_modules/all.py diff --git a/src/sage/rings/function_field/all.py b/src/sage/rings/function_field/all.py index 17cf87636e3..5557bbb349f 100644 --- a/src/sage/rings/function_field/all.py +++ b/src/sage/rings/function_field/all.py @@ -1,2 +1,2 @@ from .constructor import FunctionField -from .drinfeld_modules.all import * +from .drinfeld_modules.drinfeld_module import DrinfeldModule diff --git a/src/sage/rings/function_field/drinfeld_modules/all.py b/src/sage/rings/function_field/drinfeld_modules/all.py deleted file mode 100644 index 71d56f75a35..00000000000 --- a/src/sage/rings/function_field/drinfeld_modules/all.py +++ /dev/null @@ -1,3 +0,0 @@ -from sage.misc.lazy_import import lazy_import - -lazy_import('sage.rings.function_field.drinfeld_modules.drinfeld_module', 'DrinfeldModule') From 1e158033ab0ad13a6694dac9118e9d108d258e9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 27 Sep 2022 16:38:58 +0200 Subject: [PATCH 131/197] (minor) Fix typo in docstring --- .../function_field/drinfeld_modules/finite_drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index a64af067a34..db4e1052fd5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -166,7 +166,7 @@ def frobenius_charpoly(self, var='T'): with `\deg(A) \leq \frac{n}{2}` and `\deg(B) = n`. Note that the *Frobenius trace* is defined as `A(X)` and the - *Frobenius norm` is defined as `B(X)`. + *Frobenius norm* is defined as `B(X)`. INPUT: From 5fe2745a4edb559dab0fb6ba1dd72ac7a62a9806 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Thu, 29 Sep 2022 10:17:59 +0200 Subject: [PATCH 132/197] Fix lint error E265 This error was due to using \#*** \#*** and not \# *** \# *** for the license preambule in .py files. --- src/sage/categories/drinfeld_modules.py | 12 ++++++------ .../function_field/drinfeld_modules/action.py | 16 ++++++++-------- .../drinfeld_modules/drinfeld_module.py | 16 ++++++++-------- .../drinfeld_modules/finite_drinfeld_module.py | 16 ++++++++-------- .../function_field/drinfeld_modules/homset.py | 16 ++++++++-------- .../function_field/drinfeld_modules/morphism.py | 16 ++++++++-------- 6 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 61d0482af48..f222795f947 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -10,13 +10,13 @@ - Xavier Caruso (2022-06) """ -#***************************************************************************** -# Copyright (C) 2022 Xavier Caruso -# Antoine Leudière +# ***************************************************************************** +# Copyright (C) 2022 Xavier Caruso +# Antoine Leudière # -# Distributed under the terms of the GNU General Public License (GPL) -# http://www.gnu.org/licenses/ -#****************************************************************************** +# Distributed under the terms of the GNU General Public License (GPL) +# http://www.gnu.org/licenses/ +# ****************************************************************************** from sage.categories.category_types import Category_over_base from sage.categories.homsets import Homsets diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 4b348bff6e2..b3c1b8a8c5d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -9,15 +9,15 @@ - Antoine Leudière (2022-04) """ -#***************************************************************************** -# Copyright (C) 2022 Antoine Leudière +# ***************************************************************************** +# Copyright (C) 2022 Antoine Leudière # -# 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. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# 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. +# http://www.gnu.org/licenses/ +# ***************************************************************************** from sage.categories.action import Action from sage.misc.latex import latex diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 31aff8ced91..dc3bfaa5944 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -14,15 +14,15 @@ - Xavier Caruso (2022-06) """ -#***************************************************************************** -# Copyright (C) 2022 Antoine Leudière +# ***************************************************************************** +# Copyright (C) 2022 Antoine Leudière # -# 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. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# 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. +# http://www.gnu.org/licenses/ +# ***************************************************************************** from sage.categories.drinfeld_modules import DrinfeldModules from sage.matrix.constructor import Matrix diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index db4e1052fd5..1b51ed079ad 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -11,15 +11,15 @@ - Antoine Leudière (2022-04) """ -#***************************************************************************** -# Copyright (C) 2022 Antoine Leudière +# ***************************************************************************** +# Copyright (C) 2022 Antoine Leudière # -# 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. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# 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. +# http://www.gnu.org/licenses/ +# ***************************************************************************** from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 43cd91849f7..1b8f0004e1b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -9,15 +9,15 @@ - Antoine Leudière (2022-04) """ -#***************************************************************************** -# Copyright (C) 2022 Antoine Leudière +# ***************************************************************************** +# Copyright (C) 2022 Antoine Leudière # -# 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. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# 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. +# http://www.gnu.org/licenses/ +# ***************************************************************************** from sage.categories.drinfeld_modules import DrinfeldModules from sage.categories.homset import Homset diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index d80ff19643b..c96ca7b438d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -8,15 +8,15 @@ - Antoine Leudière (2022-04) """ -#***************************************************************************** -# Copyright (C) 2022 Antoine Leudière +# ***************************************************************************** +# Copyright (C) 2022 Antoine Leudière # -# 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. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# 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. +# http://www.gnu.org/licenses/ +# ***************************************************************************** from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass from sage.misc.latex import latex From 1e0cfc8989beebeeb4353344795ef47d0df10242 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 3 Oct 2022 11:28:47 +0200 Subject: [PATCH 133/197] (minor) Remove a blank line in DrinfeldModules --- src/sage/categories/drinfeld_modules.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index f222795f947..7df87ad55ef 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -193,7 +193,6 @@ class DrinfeldModules(Category_over_base): ... TypeError: function ring base must be a finite field """ - def __init__(self, base, name='t'): # Check input is a ring Morphism if not isinstance(base, RingHomomorphism): From e0b22d4d2d137e3dee9413ce36088eb6f637a779 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 19:11:56 +0200 Subject: [PATCH 134/197] (minor) Change Sage to SageMath in Drinfeld modules reference --- src/doc/en/reference/drinfeld_modules/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/en/reference/drinfeld_modules/index.rst b/src/doc/en/reference/drinfeld_modules/index.rst index 316f50cb830..d7485c9762e 100644 --- a/src/doc/en/reference/drinfeld_modules/index.rst +++ b/src/doc/en/reference/drinfeld_modules/index.rst @@ -1,7 +1,7 @@ Drinfeld modules ==================================== -Sage include facilities to manipulate Drinfeld modules and their morphisms. The +SageMath include facilities to manipulate Drinfeld modules and their morphisms. The main entry point is the class :class:`sage.rings.function_field.drinfeld_modules.drinfeld_module.DrinfeldModule`. From e9c99e843c73f06b53184bc21202f463affc4197 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 16:44:57 +0200 Subject: [PATCH 135/197] (minor) Remove unused import --- src/sage/categories/drinfeld_modules.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 7df87ad55ef..9aec38e4347 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -20,7 +20,6 @@ from sage.categories.category_types import Category_over_base from sage.categories.homsets import Homsets -from sage.misc.cachefunc import cached_method from sage.misc.functional import log from sage.misc.latex import latex from sage.rings.integer import Integer From a687972e1158c18330edf577fbed2bf484531b84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 5 Oct 2022 11:12:46 +0200 Subject: [PATCH 136/197] (minor) Remove useless comment --- src/sage/categories/drinfeld_modules.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 9aec38e4347..8c20fbdaea0 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -466,7 +466,6 @@ def random_object(self, rank): def super_categories(self): return [] - # Somehow required for the class definition class ParentMethods: def base(self): From 15df1104c93a76642889d1eb57081320760bfda3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 3 Oct 2022 11:30:14 +0200 Subject: [PATCH 137/197] (fix) Address first part of ticket 33713 comment 123 Thank you David. All suggestions of comment 123 were addressed. The last one, concerning non finite Drinfeld modules in doctesting is for another commit. In particular, the `if` statement (cf. comment) was removed. --- .../drinfeld_modules/drinfeld_module.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index dc3bfaa5944..f5d3b977f21 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -60,7 +60,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): morphism `\phi: \Fq[X] \to K\{\tau\}` such that: 1. The image of `\phi` contains non-constant Ore polynomials. - 2. For every element `a` in the function ring, the constant + 2. For every element `a` in the `\Fq[X]`, the constant coefficient `\phi(a)` is `\gamma(a)`. For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. @@ -202,7 +202,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 Defn: X |--> z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z - Drinfeld modules `\phi` and `\rho` have different based. That of + Drinfeld modules `\phi` and `\rho` have different bases. That of `\phi` is surjective while that of `\rho` is note:: sage: rho.category().base() @@ -427,7 +427,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. NOTE:: - In this implementation, `L` is `L`. + In this implementation, `L` is `K`. sage: action = phi.action() sage: action @@ -517,9 +517,6 @@ def __classcall_private__(cls, function_ring, gen, name='t'): category = DrinfeldModules(base, name=name) # Check gen as Ore polynomial - if ore_polring is not None and \ - ore_polring != category.ore_polring(): - raise ValueError(f'generator must lie in {category.ore_polring()}') ore_polring = category.ore_polring() # Sanity cast gen = ore_polring(gen) if gen.degree() <= 0: @@ -709,7 +706,7 @@ def action(self): def coefficient(self, n): r""" - Return the n-th coefficient of the generator. + Return the `n`-th coefficient of the generator. INPUT: @@ -1091,7 +1088,7 @@ def rank(self): sage: rho.rank() 4 """ - return self.gen().degree() + return self._gen.degree() def velu(self, isog): r""" From f55ebfe55d9d02fbdc93cce53cc746e2673df3c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 3 Oct 2022 18:23:29 +0200 Subject: [PATCH 138/197] Emphasis on K infinite in DrinfeldModule docstring In the DrinfeldModule doctest, emphasis that the field K can be any extension of Fq. See ticket 33713, comment 123. --- .../drinfeld_modules/drinfeld_module.py | 123 +++++++++--------- 1 file changed, 61 insertions(+), 62 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index f5d3b977f21..f193f805a29 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -43,13 +43,14 @@ class DrinfeldModule(Parent, UniqueRepresentation): Let `\Fq[X]` be a polynomial ring with coefficients in a finite field `\Fq` and let `K` be a field. We fix a ring morphism `\gamma: \Fq[X] \to K`, which we call the *base* of the Drinfeld module. - Please note that the base is not a ring; in particular, it is - not the field `K`. We also call `K` an *`\Fq[X]`-field*. .. NOTE:: + The base is not a ring. Specifically, it is not the field `K`. We say + however that `K` is an *`\Fq[X]`-field*. + The base of the Drinfeld module is the base of the category of - the Drinfeld module. + the Drinfeld module. The monic polynomial that generates the kernel of the base is called the *`\Fq[X]`-characteristic of the `\Fq[X]`-field `K`*. @@ -66,14 +67,42 @@ class DrinfeldModule(Parent, UniqueRepresentation): For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. The Drinfeld module `\phi` is uniquely determined by the image - `\phi_X` of `X`, which is an input of the class. + `\phi_X` of `X`. This serves as input of the class. + + Despite an emphasis on the finite case, the base codomain can be any + extension of the field `\Fq`:: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z, 4, 1]) + sage: phi + Drinfeld module defined by X |--> t^2 + 4*t + z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z of size 5^12 + Defn: X |--> z + + sage: Fq = GF(49) + sage: FqX. = Fq[] + sage: K. = Frac(FqX) + sage: phi = DrinfeldModule(FqX, [z, X+1]) + sage: phi + Drinfeld module defined by X |--> (X + 1)*t + X over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 + To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 + Defn: X |--> X + + .. NOTE:: + + In the first case, the Drinfeld module is said to be *finite*. See + :class:`sage.rings.function_field.drinfeld_modules.finite_drinfeld_module`. We say that `\Fq[X]` is the *function ring of `\phi`*; *K\{\tau\}* is the *Ore polynomial ring of `\phi`*. Further, the *generator of `\phi`* is `\phi_X` and its *constant coefficient* is the constant coefficient of `\phi_X`. The `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also be referred to as its *function - ring-characteristic*. Finally, `K` is just refered to as the + ring-characteristic*. Finally, `K` is just referred to as the codomain base. Classical references on Drinfeld modules include [Gos1998]_, @@ -98,7 +127,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: Construction - A Drinfeld module object is constructed as follows:: + A Drinfeld module object is constructed by precising the function + ring and the generator:: sage: Fq. = GF(3^2) sage: FqX. = Fq[] @@ -110,20 +140,34 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 Defn: X |--> z - In this example, we used a list of coefficients (``[z, 1, 1]``) to - represent the generator `\phi_X = z + t + t^2`, `K = \Fq(z)`. One can - also use regular Ore polynomials:: + The above Drinfeld module is finite; it can also be infinite:: + + sage: L = Frac(FqX) + sage: psi = DrinfeldModule(FqX, [L(X), 1, X^3 + X + 1]) + sage: psi + Drinfeld module defined by X |--> (X^3 + X + 1)*t^2 + t + X over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Defn: X |--> X + sage: phi.is_finite() + True + sage: psi.is_finite() + False + + In those examples, we used a list of coefficients (``[z, 1, 1]``) to + represent the generator `\phi_X = z + t + t^2`. One can also use + regular Ore polynomials:: sage: ore_polring = phi.ore_polring() sage: t = phi.ore_polring().gen() - sage: psi_X = z + t^3 - sage: psi = DrinfeldModule(FqX, psi_X) - sage: psi + sage: rho_X = z + t^3 + sage: rho = DrinfeldModule(FqX, rho_X) + sage: rho Drinfeld module defined by X |--> t^3 + z over base Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 Defn: X |--> z - sage: psi(X) == psi_X + sage: rho(X) == rho_X True The generator must have positive degree:: @@ -185,51 +229,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): deduced from the generator, and we omit the base in the input of the class for conciseness. - .. RUBRIC:: Possible bases - - The base does not need be surjective like in the above examples. In - the following example, the base codomain is still a degree six - extension of `\Fq`, but the base is a projection over a degree two - extension with modulus `X^3 + (z_2 + 2)X^2 + (6*z_2 + 1)X + 3z_2 + - 5`:: - - sage: p = X^2 + z2 + 2 - sage: p_root = z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z - sage: rho = DrinfeldModule(FqX, [p_root, 1, 1]) - sage: rho - Drinfeld module defined by X |--> t^2 + t + z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z - - Drinfeld modules `\phi` and `\rho` have different bases. That of - `\phi` is surjective while that of `\rho` is note:: - - sage: rho.category().base() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z^10 + 2*z^9 + z^8 + z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z - sage: phi.category().base() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z - - Note that ``phi`` and ``psi`` are *finite* Drinfeld modules, in the - sense that `K` is finite. But `K` can be infinite:: - - sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1, 1]) - sage: sigma - Drinfeld module defined by X |--> t^2 + t + X over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - Defn: X |--> X - sage: sigma.is_finite() - False - sage: phi.is_finite() - True - .. RUBRIC:: The category of Drinfeld modules Drinfeld modules have their own category (see class @@ -241,9 +240,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 Defn: X |--> z sage: phi.category() is psi.category() - True - sage: phi.category() is sigma.category() False + sage: phi.category() is rho.category() + True This category holds crucial information, like the function ring-characteristic of the base:: @@ -334,11 +333,11 @@ class DrinfeldModule(Parent, UniqueRepresentation): True sage: t^5 + 2*t^3 + 1 in Hom(phi, phi) False - sage: 1 in Hom(phi, psi) + sage: 1 in Hom(phi, rho) False sage: 1 in Hom(phi, phi) True - sage: 0 in Hom(phi, psi) + sage: 0 in Hom(phi, rho) True To create a SageMath object representing the morphism, call the From a207fdd64ed4fcd964f72e1f73d9c7e8733dba35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 3 Oct 2022 19:13:23 +0200 Subject: [PATCH 139/197] Add 100% doctest coverage Thank you Travis, see ticket 33713, comment 259. I added docstrings and doctests for the following methods: - DrinfeldModule.__classcall_private__ - DrinfeldModule.__init__ - DrinfeldModule._check_rank_two - FiniteDrinfeldModule.__init__ - DrinfeldModuleAction.__init__ - DrinfeldModuleHomset.__init__ - DrinfeldModuleMorphism.__init__ - DrinfeldModuleMorphism.__classcall_private__ - DrinfeldModules.__init__ - DrinfeldModules.Homsets - DrinfeldModules.Endsets - DrinfeldModules.super_categories --- src/sage/categories/drinfeld_modules.py | 82 +++++++++++++++++-- .../function_field/drinfeld_modules/action.py | 17 ++++ .../drinfeld_modules/drinfeld_module.py | 78 ++++++++++++++++++ .../finite_drinfeld_module.py | 26 ++++++ .../function_field/drinfeld_modules/homset.py | 39 +++++++++ .../drinfeld_modules/morphism.py | 62 ++++++++++++++ 6 files changed, 299 insertions(+), 5 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 8c20fbdaea0..89ae3e4bf2c 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -56,7 +56,7 @@ class DrinfeldModules(Category_over_base): be referred to as its *function ring-characteristic*. Finally, `K` is just refered to as the codomain base. - INPUT: the base, a ring morphism + INPUT: the base ring morphism .. RUBRIC:: Construction @@ -193,6 +193,36 @@ class DrinfeldModules(Category_over_base): TypeError: function ring base must be a finite field """ def __init__(self, base, name='t'): + r""" + Initialize `self`. + + INPUT: + + - ``base`` -- the base ring morphism + - ``name`` (default: `'t'`) -- the name of the Ore polynomial + ring generator + + TESTS:: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: ore_polring. = OrePolynomialRing(K, K.frobenius_endomorphism()) + sage: cat._ore_polring is ore_polring + True + sage: base = Hom(FqX, K)(p_root) + sage: cat._base == base + True + sage: cat._function_ring is FqX + True + sage: cat._constant_coefficient == base(X) + True + sage: cat._characteristic(cat._constant_coefficient) + 0 + """ # Check input is a ring Morphism if not isinstance(base, RingHomomorphism): raise TypeError('input must be a Ring morphism') @@ -281,12 +311,44 @@ def _repr_(self): """ return f'Category of Drinfeld modules defined over base {self._base}' - # Somehow required for the class definition def Homsets(self): + r""" + Return the category of homsets. + + OUTPUT: the category of homsets + + EXAMPLE:: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: from sage.categories.homsets import Homsets + sage: cat.Homsets() is Homsets() + True + """ return Homsets() - # Somehow required for the class definition def Endsets(self): + r""" + Return the category of endsets. + + OUTPUT: the category of endsets + + EXAMPLE:: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: from sage.categories.homsets import Endsets + sage: cat.Endsets() is Endsets() + True + """ return Homsets() def characteristic(self): @@ -461,9 +523,19 @@ def random_object(self, rank): return self.object(coeffs) - # Somehow required for the class definition - @cached_method def super_categories(self): + """ + EXAMPLES:: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.super_categories() + [] + """ return [] class ParentMethods: diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index b3c1b8a8c5d..0c85caea7bf 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -75,6 +75,23 @@ class DrinfeldModuleAction(Action): """ def __init__(self, drinfeld_module): + """ + Initialize `self`. + + INPUT: the Drinfeld module + + TESTS: + + sage: Fq. = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: action = phi.action() + sage: action._drinfeld_module is phi + True + sage: action._field is phi.base().codomain() + True + """ if not isinstance(drinfeld_module, DrinfeldModule): raise TypeError('input must be a DrinfeldModule') self._drinfeld_module = drinfeld_module diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index f193f805a29..c4992558db6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -475,6 +475,36 @@ class DrinfeldModule(Parent, UniqueRepresentation): @staticmethod def __classcall_private__(cls, function_ring, gen, name='t'): + """ + Check input validity and return a `DrinfeldModule` or + `FiniteDrinfeldModule` object accordingly. + + INPUT: + + - ``function_ring`` -- a univariate polynomial ring whose base + is a finite field + - ``gen`` -- the generator of the Drinfeld module; as a list of + coefficients or an Ore polynomial + - ``name`` (optional) -- the name of the Ore polynomial ring gen + + OUTPUT: a DrinfeldModule or FiniteDrinfeldModule + + TESTS: + + sage: from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: isinstance(phi, FiniteDrinfeldModule) + True + + sage: K = Frac(FqX) + sage: phi = DrinfeldModule(FqX, [K(X), 1]) + sage: isinstance(psi, FiniteDrinfeldModule) + False + """ # FIXME: function_ring must be checked before calling base_ring # on it. But then it is checked twice: firstly here, secondly in @@ -528,6 +558,40 @@ def __classcall_private__(cls, function_ring, gen, name='t'): return cls.__classcall__(cls, gen, category) def __init__(self, gen, category): + """ + Initialize `self`. + + Validity of the input is checked in `__classcall_private__`. The + `__init__` just saves attributes. + + INPUT: + + - ``function_ring`` -- a univariate polynomial ring whose base + is a finite field + - ``gen`` -- the generator of the Drinfeld module; as a list of + coefficients or an Ore polynomial + - ``name`` (optional) -- the name of the Ore polynomial ring gen + + TESTS: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: gen = [p_root, z12^3, z12^5] + sage: phi = DrinfeldModule(FqX, gen) + sage: ore_polring = phi.ore_polring() + sage: phi._base == phi.category().base() + True + sage: phi._function_ring == FqX + True + sage: phi._gen == ore_polring(gen) + True + sage: phi._ore_polring == ore_polring + True + sage: phi._morphism == Hom(FqX, ore_polring)(phi._gen) + True + """ self._base = category.base() self._function_ring = category.function_ring() self._gen = gen @@ -614,6 +678,20 @@ def _Hom_(self, other, category): def _check_rank_two(self): r""" Raise ``NotImplementedError`` if the rank is not two. + + TESTS: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi._check_rank_two() + sage: phi = DrinfeldModule(FqX, [p_root, 1]) + sage: phi._check_rank_two() + Traceback (most recent call last): + ... + NotImplementedError: rank must be 2 """ if self.rank() != 2: raise NotImplementedError('rank must be 2') diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 1b51ed079ad..1044fc4a618 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -102,6 +102,32 @@ class FiniteDrinfeldModule(DrinfeldModule): """ def __init__(self, gen, category): + """ + Initialize `self`. + + Validity of the input is checked in `__classcall_private__`. The + `__init__` just saves attributes. + + INPUT: + + - ``function_ring`` -- a univariate polynomial ring whose base + is a finite field + - ``gen`` -- the generator of the Drinfeld module; as a list of + coefficients or an Ore polynomial + - ``name`` (optional) -- the name of the Ore polynomial ring gen + + TESTS: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: gen = [p_root, z12^3, z12^5] + sage: phi = DrinfeldModule(FqX, gen) + sage: ore_polring = phi.ore_polring() + sage: phi._gen == ore_polring(gen) + True + """ # NOTE: There used to be no __init__ here (which was fine). I # added one to ensure that FiniteDrinfeldModule would always diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 1b8f0004e1b..51b69fab180 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -59,6 +59,21 @@ class DrinfeldModuleHomset(Homset): sage: end is Hom(phi, phi) True + The domain and codomain must have the same Drinfeld modules + category:: + + sage: rho = DrinfeldModule(FqX, [Frac(FqX)(X), 1]) + sage: Hom(phi, rho) + Traceback (most recent call last): + ... + ValueError: Drinfeld modules must be in the same category + + sage: sigma = DrinfeldModule(FqX, [1, z6, 2]) + sage: Hom(phi, sigma) + Traceback (most recent call last): + ... + ValueError: Drinfeld modules must be in the same category + One can create morphism objects by calling the homset:: sage: t = phi.ore_polring().gen() @@ -115,6 +130,30 @@ class DrinfeldModuleHomset(Homset): __contains__ = Parent.__contains__ def __init__(self, X, Y, category=None, check=True): + """ + Initialize `self`. + + INPUT: + + - ``X`` -- the domain of the homset + - ``Y`` -- the codomain of the homset + - ``category`` (default: None) -- the Drinfeld modules category of + the domain and codomain + - ``check`` (default: True) -- check the validity of the category + + TESTS:: + + sage: Fq = GF(27) + sage: FqX. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) + sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: hom = Hom(phi, psi) + sage: hom.domain() is phi + True + sage: hom.codomain() is psi + True + """ if category is None: category = X.category() if check: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index c96ca7b438d..4f8fd2fa1e7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -54,6 +54,13 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, To (gen): (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + The input Ore polynomial must indeed define a morphism:: + + sage: morphism = Hom(phi, psi)(1) + Traceback (most recent call last): + ... + ValueError: Ore polynomial does not define a morphism + We can get basic data on the morphism:: sage: morphism.domain() @@ -115,6 +122,36 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, @staticmethod def __classcall_private__(cls, parent, x): + """ + Create the morphism. + + INPUT: + + - ``cls`` -- DrinfeldModuleMorphism + - ``parent`` -- The Drinfeld module homset + - ``x`` -- the Ore polynomial defining the morphism or a + DrinfeldModuleMorphism + + OUTPUT: the morphism object + + TESTS:: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_polring().gen() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism is Hom(phi, psi)(morphism) + True + + sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism + sage: morphism = DrinfeldModuleMorphism(Sets(), t + 1) + Traceback (most recent call last): + ... + TypeError: parent should be a DrinfeldModuleHomset + """ from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset if not isinstance(parent, DrinfeldModuleHomset): raise TypeError('parent should be a DrinfeldModuleHomset') @@ -132,6 +169,31 @@ def __classcall_private__(cls, parent, x): return cls.__classcall__(cls, parent, ore_pol) def __init__(self, parent, ore_pol): + r""" + Initialize `self`. + + INPUT: + + - ``parent`` -- The Drinfeld module homset + - ``ore_pol`` -- The Ore polynomial that defines the morphism + + TESTS:: + + sage: Fq = GF(2) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) + sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_polring().gen() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism._domain is phi + True + sage: morphism._codomain is psi + True + sage: morphism._ore_polynomial == t + z6^5 + z6^2 + 1 + True + """ + super().__init__(parent) self._domain = parent.domain() self._codomain = parent.codomain() From e042ae219c56574cef4ac7a9deb663fc8ba270b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 11:11:13 +0200 Subject: [PATCH 140/197] State that the code is for Drinfeld Fq[X]-modules in DrinfeldModule docstring --- .../function_field/drinfeld_modules/drinfeld_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index c4992558db6..592a3f9c4c4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -38,7 +38,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): r""" - This class represents a Drinfeld module. + This class represents a Drinfeld `\Fq[X]`-module. Let `\Fq[X]` be a polynomial ring with coefficients in a finite field `\Fq` and let `K` be a field. We fix a ring morphism `\gamma: @@ -66,8 +66,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. - The Drinfeld module `\phi` is uniquely determined by the image - `\phi_X` of `X`. This serves as input of the class. + The Drinfeld `\Fq[X]`-module `\phi` is uniquely determined by the + image `\phi_X` of `X`. This serves as input of the class. Despite an emphasis on the finite case, the base codomain can be any extension of the field `\Fq`:: From 89fa35188ec3a89ac332ac7f7a27f8bb774c6f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 13:58:59 +0200 Subject: [PATCH 141/197] (fix) Change a NotImplementedError to ValueError in DrinfeldModuleHomset --- src/sage/rings/function_field/drinfeld_modules/homset.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 51b69fab180..7eed56a5326 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -159,10 +159,10 @@ def __init__(self, X, Y, category=None, check=True): if check: if X.category() != Y.category() \ or not isinstance(X.category(), DrinfeldModules): - raise NotImplementedError('Drinfeld modules must be in the ' - 'same category') + raise ValueError('Drinfeld modules must be in the same ' + 'category') if category != X.category(): - raise NotImplementedError('category should be DrinfeldModules') + raise ValueError('category should be DrinfeldModules') base = category.base() super().__init__(X, Y, category=category, base=base, check=check) From e0846a39cfddc8abe13912c692d5a3b674c04132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 14:03:16 +0200 Subject: [PATCH 142/197] (fix) In INPUT docstring fields, change `default` to `optional: ...` --- src/sage/rings/function_field/drinfeld_modules/action.py | 2 +- .../function_field/drinfeld_modules/drinfeld_module.py | 8 +++++--- .../drinfeld_modules/finite_drinfeld_module.py | 7 +++---- .../rings/function_field/drinfeld_modules/morphism.py | 6 +++--- 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 0c85caea7bf..911fd11a26c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -40,7 +40,7 @@ class DrinfeldModuleAction(Action): The action is instantiated as follows. Note that the user should never explicitly instantiate the class `DrinfeldModuleAction`:: - INPUT: a Drinfeld module + INPUT: the Drinfeld module EXAMPLES: diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 592a3f9c4c4..fe8dbb0b5a7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -123,7 +123,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` (optional) -- the name of the Ore polynomial ring gen + - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring + generator .. RUBRIC:: Construction @@ -485,7 +486,8 @@ def __classcall_private__(cls, function_ring, gen, name='t'): is a finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` (optional) -- the name of the Ore polynomial ring gen + - ``name`` (default: `'t'`) -- the name of the Ore polynomial + ring gen OUTPUT: a DrinfeldModule or FiniteDrinfeldModule @@ -570,7 +572,7 @@ def __init__(self, gen, category): is a finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` (optional) -- the name of the Ore polynomial ring gen + - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring gen TESTS: diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 1044fc4a618..1ef152573d1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -114,7 +114,8 @@ def __init__(self, gen, category): is a finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` (optional) -- the name of the Ore polynomial ring gen + - ``name`` (default: `'t'`) -- the name of the Ore polynomial + ring gen TESTS: @@ -194,9 +195,7 @@ def frobenius_charpoly(self, var='T'): Note that the *Frobenius trace* is defined as `A(X)` and the *Frobenius norm* is defined as `B(X)`. - INPUT: - - - ``var`` -- (optional) the name of the second variable + INPUT: (default: `'T'`) the name of the second variable OUTPUT: an univariate polynomial with coefficients in the function ring diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 4f8fd2fa1e7..d0acab5b98a 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -128,7 +128,7 @@ def __classcall_private__(cls, parent, x): INPUT: - ``cls`` -- DrinfeldModuleMorphism - - ``parent`` -- The Drinfeld module homset + - ``parent`` -- the Drinfeld module homset - ``x`` -- the Ore polynomial defining the morphism or a DrinfeldModuleMorphism @@ -174,8 +174,8 @@ def __init__(self, parent, ore_pol): INPUT: - - ``parent`` -- The Drinfeld module homset - - ``ore_pol`` -- The Ore polynomial that defines the morphism + - ``parent`` -- the Drinfeld module homset + - ``ore_pol`` -- the Ore polynomial that defines the morphism TESTS:: From f71aff4637d5e3546bfe529c72d76d25c6ea88ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 15:29:39 +0200 Subject: [PATCH 143/197] (fix) Move base_ring from DrinfeldModules.ParentMethods to DrinfeldModule This is because calling `phi.base_ring()` used to call the `base_ring` method of `CategoryObject` and not the one defined in `DrinfeldModules.ParentMethod`. This works, but any better solution would be welcome. --- src/sage/categories/drinfeld_modules.py | 22 ++++++++--------- .../drinfeld_modules/drinfeld_module.py | 24 +++++++++++++++++++ 2 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 89ae3e4bf2c..c6f5ace355d 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -596,18 +596,6 @@ def base(self): """ return self.category().base() - def base_ring(self): - r""" - Raise an exception. - - The base of a Drinfeld module is a ring morphism, not a ring. - - This method is implemented in CategoryObjects, of which - Parent inherits. It returns the base of the category. I - overloaded it to avoid confusion. - """ - raise TypeError('the base of a Drinfeld module is a morphism') - def characteristic(self): r""" Return the function ring-characteristic. @@ -728,3 +716,13 @@ def ore_polring(self): True """ return self.category().ore_polring() + + # FIXME + # The parent method `base_ring` is defined not here, as it + # should be, but in `DrinfeldModule`. + # + # This is because calling `phi.base_ring()` calls the + # `base_ring` method of `CategoryObject` and not the one defined + # here. + # + # This works, but any better solution would be welcome. diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index fe8dbb0b5a7..74a6444c6ef 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -602,6 +602,30 @@ def __init__(self, gen, category): self._Fq = self._function_ring.base_ring() # Must be last super().__init__(base=self._base, category=category) + def base_ring(self): + r""" + Raise an exception. + + The base of a Drinfeld module is a ring morphism, not a ring. + + This method is implemented in CategoryObjects, of which + Parent inherits. It returns the base of the category. I + overloaded it to avoid confusion. + + TESTS:: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.base_ring() + Traceback (most recent call last): + ... + AttributeError: the base of a Drinfeld module is a morphism + """ + raise AttributeError('the base of a Drinfeld module is a morphism') + def __call__(self, a): r""" Return the image of ``a`` by the morphism that defines the From bf5a4a44e64ffb48a7d24fe487a7bd36dcf9b82d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 16:45:47 +0200 Subject: [PATCH 144/197] (fix) Replace TESTS: by TESTS:: and EXAMPLES: by EXAMPLES:: This was the cause for `make doc` errors. --- src/sage/categories/drinfeld_modules.py | 32 +++++++-------- .../function_field/drinfeld_modules/action.py | 12 +++--- .../drinfeld_modules/drinfeld_module.py | 40 +++++++++---------- .../finite_drinfeld_module.py | 14 +++---- .../function_field/drinfeld_modules/homset.py | 10 ++--- .../drinfeld_modules/morphism.py | 14 ++++--- 6 files changed, 62 insertions(+), 60 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index c6f5ace355d..4212e6b063c 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -159,7 +159,7 @@ class DrinfeldModules(Category_over_base): sage: rho.category() is cat True - TESTS: + TESTS:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -270,7 +270,7 @@ def _latex_(self): OUTPUT: a string - EXAMPLE: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -295,7 +295,7 @@ def _repr_(self): OUTPUT: a string - EXAMPLE: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -317,7 +317,7 @@ def Homsets(self): OUTPUT: the category of homsets - EXAMPLE:: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -337,7 +337,7 @@ def Endsets(self): OUTPUT: the category of endsets - EXAMPLE:: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -357,7 +357,7 @@ def characteristic(self): OUTPUT: `0` or a monic prime polynomial in the function ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -390,7 +390,7 @@ def constant_coefficient(self): OUTPUT: an element in the base codomain - EXAMPLES: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -411,7 +411,7 @@ def function_ring(self): OUTPUT: a univariate polynomial ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -436,7 +436,7 @@ def object(self, gen): OUTPUT: a Drinfeld module in the category - EXAMPLES: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -470,7 +470,7 @@ def ore_polring(self): OUTPUT: an Ore polynomial ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -494,7 +494,7 @@ def random_object(self, rank): OUTPUT: a Drinfeld module in the category - EXAMPLES: + EXAMPLES:: sage: Fq = GF(11) sage: FqX. = Fq[] @@ -546,7 +546,7 @@ def base(self): OUTPUT: a ring morphism - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -602,7 +602,7 @@ def characteristic(self): OUTPUT: a univariate polynomial ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -627,7 +627,7 @@ def function_ring(self): OUTPUT: a univariate polynomial ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -645,7 +645,7 @@ def constant_coefficient(self): OUTPUT: an element in the base codomain - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -692,7 +692,7 @@ def ore_polring(self): OUTPUT: an Ore polynomial ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 911fd11a26c..558c102b244 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -42,7 +42,7 @@ class DrinfeldModuleAction(Action): INPUT: the Drinfeld module - EXAMPLES: + EXAMPLES:: sage: Fq. = GF(11) sage: FqX. = Fq[] @@ -80,7 +80,7 @@ def __init__(self, drinfeld_module): INPUT: the Drinfeld module - TESTS: + TESTS:: sage: Fq. = GF(11) sage: FqX. = Fq[] @@ -109,7 +109,7 @@ def _act_(self, pol, x): OUTPUT: an element in the base codomain - EXAMPLES: + EXAMPLES:: sage: Fq. = GF(11) sage: FqX. = Fq[] @@ -137,7 +137,7 @@ def _latex_(self): OUTPUT: a string - EXAMPLES: + EXAMPLES:: sage: Fq. = GF(11) sage: FqX. = Fq[] @@ -162,7 +162,7 @@ def _repr_(self): OUTPUT: a string - EXAMPLES: + EXAMPLES:: sage: Fq. = GF(11) sage: FqX. = Fq[] @@ -184,7 +184,7 @@ def drinfeld_module(self): OUTPUT: a Drinfeld module - EXAMPLES: + EXAMPLES:: sage: Fq. = GF(11) sage: FqX. = Fq[] diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 74a6444c6ef..d83acc3d73a 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -458,7 +458,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.invert(phi(a)) == a True - TESTS: + TESTS:: sage: Fq = K = GF(2) sage: FqX. = Fq[] @@ -491,7 +491,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): OUTPUT: a DrinfeldModule or FiniteDrinfeldModule - TESTS: + TESTS:: sage: from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule sage: Fq = GF(25) @@ -574,7 +574,7 @@ def __init__(self, gen, category): coefficients or an Ore polynomial - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring gen - TESTS: + TESTS:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -638,7 +638,7 @@ def __call__(self, a): OUTPUT: an element in the base codomain - TESTS: + TESTS:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -681,7 +681,7 @@ def _Hom_(self, other, category): OUTPUT: an homset - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -704,8 +704,8 @@ def _Hom_(self, other, category): def _check_rank_two(self): r""" Raise ``NotImplementedError`` if the rank is not two. - - TESTS: + + TESTS:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -728,7 +728,7 @@ def _latex_(self): OUTPUT: a string - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -754,7 +754,7 @@ def _repr_(self): OUTPUT: a string - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -779,7 +779,7 @@ def action(self): OUTPUT: a Drinfeld module action object - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -817,7 +817,7 @@ def coefficient(self, n): OUTPUT: an element in the base codomain - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -856,7 +856,7 @@ def coefficients(self, sparse=True): OUTPUT: a list of elements in the base codomain - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -890,7 +890,7 @@ def gen(self): OUTPUT: an Ore polynomial - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -924,7 +924,7 @@ def height(self): OUTPUT: an integer - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -976,7 +976,7 @@ def invert(self, ore_pol): OUTPUT: a function ring element - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -1061,7 +1061,7 @@ def is_finite(self): OUTPUT: a boolean - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -1090,7 +1090,7 @@ def j_invariant(self): OUTPUT: an element in the base codomain - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -1127,7 +1127,7 @@ def morphism(self): OUTPUT: a ring morphism from the function ring to the Ore polynomial ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -1175,7 +1175,7 @@ def rank(self): OUTPUT: an integer - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -1225,7 +1225,7 @@ def velu(self, isog): Another possible algorithm is to recursively solve a system, see :arxiv:`2203.06970`, eq. 1.1. - EXAMPLES: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 1ef152573d1..1cfd2af886f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -117,7 +117,7 @@ def __init__(self, gen, category): - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring gen - TESTS: + TESTS:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -149,7 +149,7 @@ def frobenius_endomorphism(self): OUTPUT: a Drinfeld module morphism - EXAMPLES: + EXAMPLES:: sage: Fq = GF(343) sage: FqX. = Fq[] @@ -200,7 +200,7 @@ def frobenius_charpoly(self, var='T'): OUTPUT: an univariate polynomial with coefficients in the function ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(343) sage: FqX. = Fq[] @@ -260,7 +260,7 @@ def frobenius_norm(self): OUTPUT: an element in the function ring - EXAMPLES: + EXAMPLES:: sage: Fq = GF(343) sage: FqX. = Fq[] @@ -323,7 +323,7 @@ def frobenius_trace(self): :meth:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule.invert`, see its docstring for details. - EXAMPLES: + EXAMPLES:: sage: Fq = GF(343) sage: FqX. = Fq[] @@ -364,7 +364,7 @@ def is_ordinary(self): OUTPUT: a boolean - EXAMPLES: + EXAMPLES:: sage: Fq = GF(343) sage: FqX. = Fq[] @@ -400,7 +400,7 @@ def is_supersingular(self): OUTPUT: a boolean - EXAMPLES: + EXAMPLES:: sage: Fq = GF(343) sage: FqX. = Fq[] diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 7eed56a5326..dc3484c759a 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -36,7 +36,7 @@ class DrinfeldModuleHomset(Homset): - ``X`` -- the domain - ``Y`` -- the codomain - EXAMPLES: + EXAMPLES:: sage: Fq = GF(27) sage: FqX. = Fq[] @@ -172,7 +172,7 @@ def _latex_(self): OUTPUT: a string - EXAMPLES: + EXAMPLES:: sage: Fq = GF(27) sage: FqX. = Fq[] @@ -194,7 +194,7 @@ def _repr_(self): OUTPUT: a string - EXAMPLES: + EXAMPLES:: sage: Fq = GF(27) sage: FqX. = Fq[] @@ -219,7 +219,7 @@ def __contains__(self, x): OUTPUT: a boolean - EXAMPLES: + EXAMPLES:: In the next examples, the input is an Ore polynomial:: @@ -274,7 +274,7 @@ def _element_constructor_(self, *args, **kwds): OUTPUT: a Drinfeld module morphism - EXAMPLES: + EXAMPLES:: sage: Fq = GF(27) sage: FqX. = Fq[] diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index d0acab5b98a..bf511bc7df1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -205,7 +205,8 @@ def _latex_(self): OUTPUT: a string - EXAMPLES: + EXAMPLES:: + sage: Fq = GF(2) sage: FqX. = Fq[] sage: K. = Fq.extension(6) @@ -237,7 +238,8 @@ def _repr_(self): OUTPUT: a string - EXAMPLES: + EXAMPLES:: + sage: Fq = GF(2) sage: FqX. = Fq[] sage: K. = Fq.extension(6) @@ -260,7 +262,7 @@ def is_zero(self): r""" Return ``True`` whether the morphism is the zero morphism. - EXAMPLES: + EXAMPLES:: sage: Fq = GF(2) sage: FqX. = Fq[] @@ -282,7 +284,7 @@ def is_isogeny(self): r""" Return ``True`` whether the morphism is an isogeny. - EXAMPLES: + EXAMPLES:: sage: Fq = GF(2) sage: FqX. = Fq[] @@ -312,7 +314,7 @@ def is_isomorphism(self): r""" Return ``True`` whether the morphism is an isomorphism. - EXAMPLES: + EXAMPLES:: sage: Fq = GF(2) sage: FqX. = Fq[] @@ -342,7 +344,7 @@ def ore_polynomial(self): r""" Return the Ore polynomial that defines the morphism. - EXAMPLES: + EXAMPLES:: sage: Fq = GF(2) sage: FqX. = Fq[] From c821b208b0840b3d83407e8b2332fe4ff63daee3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 18:10:55 +0200 Subject: [PATCH 145/197] (fix) Fix various small issues in doc and `make doc` (see description) Fixes: - fix some broken references; - change some `:` to `::`; - change some ` to ``; - change `\Fq` to `\mathbb{F}_q`; - remove some italics (`*...*`) in inline definitions as Mathjax in italics is not rendered properly. --- src/sage/categories/drinfeld_modules.py | 42 ++--- .../function_field/drinfeld_modules/action.py | 13 +- .../drinfeld_modules/drinfeld_module.py | 151 +++++++++--------- .../finite_drinfeld_module.py | 46 +++--- .../function_field/drinfeld_modules/homset.py | 6 +- .../drinfeld_modules/morphism.py | 12 +- 6 files changed, 140 insertions(+), 130 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 4212e6b063c..7feba60d0b0 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -33,28 +33,30 @@ class DrinfeldModules(Category_over_base): This class represents the category of Drinfeld modules on a given base. - Let `\Fq[X]` be a polynomial ring with coefficients in a finite - field `\Fq` and let `K` be a field. We fix a ring morphism `\gamma: - \Fq[X] \to K`, which we call the *base* of the category (it is the - base of the Drinfeld modules in the category). - Please note that the base is not a ring; in particular, it is - not the field `K`. We also call `K` an *`\Fq[X]`-field*. + Let `\mathbb{F}_q[X]` be a polynomial ring with coefficients in a + finite field `\mathbb{F}_q` and let `K` be a field. We fix a ring + morphism `\gamma: \mathbb{F}_q[X] \to K`, which we call the *base* + of the category (it is the base of the Drinfeld modules in the + category). Please note that the base is not a ring; in particular, + it is not the field `K`. We also call `K` an + `\mathbb{F}_q[X]`-field. The category is uniquely defined by its base. The monic polynomial that generates the kernel of the base is called - the *`\Fq[X]`-characteristic of the `\Fq[X]`-field `K`*. + the `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field + `K`. .. NOTE:: These notations will be used throughout this documentation. - We say that `\Fq[X]` is the *function ring of the category*; - *K\{\tau\}* is the *Ore polynomial ring of the category*. The - *constant coefficient of the category* is the image of `X` under the - base. The `\Fq[X]`-characteristic of the `\Fq[X]`-field `K` can also - be referred to as its *function ring-characteristic*. Finally, `K` - is just refered to as the codomain base. + We say that `\mathbb{F}_q[X]` is the function ring of the category; + `K\{\tau\}` is the re polynomial ring of the category. The constant + coefficient of the category is the image of `X` under the base. The + `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field `K` + can also be referred to as its function ring-characteristic. + Finally, `K` is just refered to as the codomain base. INPUT: the base ring morphism @@ -90,7 +92,7 @@ class DrinfeldModules(Category_over_base): To: Finite Field in z of size 11^4 Defn: X |--> z^3 + 7*z^2 + 6*z + 10 - The so-called *constant coefficient* --- which is the same for all + The so-called constant coefficient --- which is the same for all Drinfeld modules in the category --- is simply the image of `X` by this morphism: @@ -99,9 +101,9 @@ class DrinfeldModules(Category_over_base): sage: cat.base()(X) == cat.constant_coefficient() True - Similarly, the *function ring-characteristic* of the category is either - `0` or the unique monic polynomial in `\Fq[X]` that generates - the kernel of the base:: + Similarly, the function ring-characteristic of the category is + either `0` or the unique monic polynomial in `\mathbb{F}_q[X]` that + generates the kernel of the base:: sage: cat.characteristic() X^2 + 7*X + 2 @@ -655,9 +657,9 @@ def constant_coefficient(self): sage: phi.constant_coefficient() == p_root True - Let `\Fq[X]` be the function ring, and let `\gamma` the base of - the Drinfeld module. The constant coefficient equals - `\gamma(X)`:: + Let `\mathbb{F}_q[X]` be the function ring, and let `\gamma` + the base of the Drinfeld module. The constant coefficient + equals `\gamma(X)`:: sage: cat = phi.category() sage: base = cat.base() diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 558c102b244..096b5428059 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -29,16 +29,17 @@ class DrinfeldModuleAction(Action): This class represents the module action induced by a Drinfeld module. - Let `\phi` be a Drinfeld module with base `\gamma: \Fq[X] \to K`. - Let `L/K` be a field extension, let `x \in L`, let `a` be a function - ring element; the action is defined as `(a, x) \mapsto \phi_a(x)`. + Let `\phi` be a Drinfeld module with base `\gamma: \mathbb{F}_q[X] + \to K`. Let `L/K` be a field extension, let `x \in L`, let `a` be a + function ring element; the action is defined as `(a, x) \mapsto + \phi_a(x)`. .. NOTE:: In this implementation, `L` is `K`. - The action is instantiated as follows. Note that the user should - never explicitly instantiate the class `DrinfeldModuleAction`:: + The user should never explicitly instantiate the class + `DrinfeldModuleAction`. INPUT: the Drinfeld module @@ -76,7 +77,7 @@ class DrinfeldModuleAction(Action): def __init__(self, drinfeld_module): """ - Initialize `self`. + Initialize ``self``. INPUT: the Drinfeld module diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index d83acc3d73a..b63b5aaef7c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -4,7 +4,7 @@ This module provides the class :class:`sage.rings.function_field.drinfeld_module.drinfeld_module.DrinfeldModule`. -For *finite* Drinfeld modules and their theory of complex multiplication, see +For finite Drinfeld modules and their theory of complex multiplication, see class :class:`sage.rings.function_field.drinfeld_module.finite_drinfeld_module.DrinfeldModule`. @@ -38,39 +38,42 @@ class DrinfeldModule(Parent, UniqueRepresentation): r""" - This class represents a Drinfeld `\Fq[X]`-module. + This class represents a Drinfeld `\mathbb{F}_q[X]`-module. - Let `\Fq[X]` be a polynomial ring with coefficients in a finite - field `\Fq` and let `K` be a field. We fix a ring morphism `\gamma: - \Fq[X] \to K`, which we call the *base* of the Drinfeld module. + Let `\mathbb{F}_q[X]` be a polynomial ring with coefficients in a + finite field `\mathbb{F}_q` and let `K` be a field. We fix a ring + morphism `\gamma: \mathbb{F}_q[X] \to K`, which we call the base of + the Drinfeld module. .. NOTE:: - The base is not a ring. Specifically, it is not the field `K`. We say - however that `K` is an *`\Fq[X]`-field*. + The base is not a ring. Specifically, it is not the field `K`. + We say however that `K` is an `\mathbb{F}_q[X]`-field. The base of the Drinfeld module is the base of the category of the Drinfeld module. The monic polynomial that generates the kernel of the base is called - the *`\Fq[X]`-characteristic of the `\Fq[X]`-field `K`*. + the `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field + `K`. Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in - `K` and Frobenius variable `\tau: x \mapsto x^q`. A *Drinfeld - `\Fq[X]`-module over the base `\gamma`* is an `\Fq`-algebra - morphism `\phi: \Fq[X] \to K\{\tau\}` such that: + `K` and Frobenius variable `\tau: x \mapsto x^q`. A Drinfeld + `\mathbb{F}_q[X]`-module over the base `\gamma` is an + `\mathbb{F}_q`-algebra morphism `\phi: \mathbb{F}_q[X] \to + K\{\tau\}` such that: - 1. The image of `\phi` contains non-constant Ore polynomials. - 2. For every element `a` in the `\Fq[X]`, the constant - coefficient `\phi(a)` is `\gamma(a)`. + 1. The image of `\phi` contains non-constant Ore polynomials. + 2. For every element `a` in the `\mathbb{F}_q[X]`, the constant + coefficient `\phi(a)` is `\gamma(a)`. For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. - The Drinfeld `\Fq[X]`-module `\phi` is uniquely determined by the - image `\phi_X` of `X`. This serves as input of the class. + The Drinfeld `\mathbb{F}_q[X]`-module `\phi` is uniquely determined + by the image `\phi_X` of `X`. This serves as input of the class. Despite an emphasis on the finite case, the base codomain can be any - extension of the field `\Fq`:: + extension of the field `\mathbb{F}_q`:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -94,28 +97,29 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. NOTE:: - In the first case, the Drinfeld module is said to be *finite*. See + In the first case, the Drinfeld module is said to be finite. See :class:`sage.rings.function_field.drinfeld_modules.finite_drinfeld_module`. - We say that `\Fq[X]` is the *function ring of `\phi`*; *K\{\tau\}* - is the *Ore polynomial ring of `\phi`*. Further, the *generator of - `\phi`* is `\phi_X` and its *constant coefficient* is the constant - coefficient of `\phi_X`. The `\Fq[X]`-characteristic of the - `\Fq[X]`-field `K` can also be referred to as its *function - ring-characteristic*. Finally, `K` is just referred to as the - codomain base. + We say that `\mathbb{F}_q[X]` is the function ring of `\phi`; + `K\{\tau\}` is the Ore polynomial ring of `\phi`. Further, the + generator of `\phi` is `\phi_X` and its constant coefficient is the + constant coefficient of `\phi_X`. The + `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field `K` + can also be referred to as its function ring-characteristic. + Finally, `K` is just referred to as the codomain base. Classical references on Drinfeld modules include [Gos1998]_, - [Rosen2002]_, [VS06]_ and [Gek1998]_. + [Rosen2002]_, [VS06]_ and [Gek1991]_. .. NOTE:: Drinfeld modules are defined in a larger setting, in which the - polynomial ring `\Fq[X]` is replaced by a more general function - ring: the ring of functions in `k` that are regular outside - `\infty`, where `k` is a function field over `\Fq` with - transcendence degree `1` and `\infty` is a fixed place of `k`. - This is out of the scope of this implementation. + polynomial ring `\mathbb{F}_q[X]` is replaced by a more general + function ring: the ring of functions in `k` that are regular + outside `\infty`, where `k` is a function field over + `\mathbb{F}_q` with transcendence degree `1` and `\infty` is a + fixed place of `k`. This is out of the scope of this + implementation. INPUT: @@ -123,7 +127,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring + - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring generator .. RUBRIC:: Construction @@ -185,8 +189,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): ... ValueError: base must be a non zero morphism - The coefficients of the generator must lie in an `\Fq[X]`-field, - where `\Fq[X]` is the function ring of the Drinfeld module:: + The coefficients of the generator must lie in an + `\mathbb{F}_q[X]`-field, where `\mathbb{F}_q[X]` is the function + ring of the Drinfeld module:: sage: DrinfeldModule(FqX, [z, QQ(1)]) Traceback (most recent call last): @@ -259,14 +264,14 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi(X) # phi_X t^2 + t + z - sage: phi(X^3 + X + 1) # phi_X^3 +X + 1 + sage: phi(X^3 + X + 1) # phi_(X^3 +X + 1) t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 sage: phi(1) # phi_1 1 This is useful to quickly retrieve the generator of the Drinfeld - module. Furthermore, a Drinfeld `\Fq[X]`-module can be seen as an - Ore polynomial with positive degree and constant coefficient + module. Furthermore, a Drinfeld `\mathbb{F}_q[X]`-module can be seen + as an Ore polynomial with positive degree and constant coefficient `\gamma(X)`, where `\gamma` is the base. This analogy is the motivation for the following methods:: @@ -320,10 +325,10 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: Morphisms, isogenies - A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore - polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for - every `a` in the function ring. In our case, this is equivalent to - `f \phi_X = \psi_X f`. An *isogeny* is a non-zero morphism. + A morphism of Drinfeld modules `\phi \to \psi` is an Ore polynomial + `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a` in + the function ring. In our case, this is equivalent to `f \phi_X = + \psi_X f`. An isogeny is a non-zero morphism. Use the ``in`` syntax to test if an Ore polynomial defines a morphism:: @@ -419,22 +424,22 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: The action of a Drinfeld module - The `\Fq[X]`-Drinfeld module `\phi` induces a special left - `\Fq[X]`-module structure on any field extension `L/K`. Let `x \in - L` and `a` be in the function ring; the action is defined as `(a, - x) \mapsto \phi_a(x)`. The method :meth:`action` returns an + The `\mathbb{F}_q[X]`-Drinfeld module `\phi` induces a special left + `\mathbb{F}_q[X]`-module structure on any field extension `L/K`. Let + `x \in L` and `a` be in the function ring; the action is defined as + `(a, x) \mapsto \phi_a(x)`. The method :meth:`action` returns an ``Action`` object representing the Drinfeld module action. .. NOTE:: - In this implementation, `L` is `K`. + In this implementation, `L` is `K`:: - sage: action = phi.action() - sage: action - Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by X |--> t^2 + t + z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z + sage: action = phi.action() + sage: action + Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by X |--> t^2 + t + z over base Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 + Defn: X |--> z The action on elements is computed by calling the action object:: @@ -477,8 +482,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): @staticmethod def __classcall_private__(cls, function_ring, gen, name='t'): """ - Check input validity and return a `DrinfeldModule` or - `FiniteDrinfeldModule` object accordingly. + Check input validity and return a ``DrinfeldModule`` or + ``FiniteDrinfeldModule`` object accordingly. INPUT: @@ -486,7 +491,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): is a finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` (default: `'t'`) -- the name of the Ore polynomial + - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring gen OUTPUT: a DrinfeldModule or FiniteDrinfeldModule @@ -561,10 +566,10 @@ def __classcall_private__(cls, function_ring, gen, name='t'): def __init__(self, gen, category): """ - Initialize `self`. + Initialize ``self``. - Validity of the input is checked in `__classcall_private__`. The - `__init__` just saves attributes. + Validity of the input is checked in ``__classcall_private__``. + The ``__init__`` just saves attributes. INPUT: @@ -572,7 +577,8 @@ def __init__(self, gen, category): is a finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring gen + - ``name`` (default: ``'t'``) -- the name of the Ore polynomial + ring gen TESTS:: @@ -628,7 +634,7 @@ def base_ring(self): def __call__(self, a): r""" - Return the image of ``a`` by the morphism that defines the + Return the image of input ``a`` by the morphism that defines the Drinfeld module; i.e. `\phi_a` if the Drinfeld module is denoted `phi`. @@ -677,7 +683,7 @@ def _Hom_(self, other, category): - ``other`` -- the codomain of the homset - ``category`` -- the category in which we consider the - morphisms, usually `self.category()` + morphisms, usually ``self.category()`` OUTPUT: an homset @@ -911,8 +917,8 @@ def height(self): field characteristic is a prime ideal. In our case, this ideal is even generated by a monic polynomial `\mathfrak{p}` in the function field. Write `\phi_\mathfrak{p} = a_s \tau^s + \dots + - \tau^{r*\deg(\mathfrak{p})}`. The *height* of the Drinfeld - module is the well-defined positive integer `h = + \tau^{r*\deg(\mathfrak{p})}`. The height of the Drinfeld module + is the well-defined positive integer `h = \frac{s}{\deg(\mathfrak{p})}`. .. NOTE:: @@ -1143,7 +1149,7 @@ def morphism(self): sage: isinstance(phi.morphism(), RingHomomorphism) True - Actually, the ``DrinfeldModule`` method ``__call__`` simply + Actually, the ``DrinfeldModule`` method :meth:`__call__` simply class the ``__call__`` method of this morphism:: sage: phi.morphism()(X) == phi(X) @@ -1208,12 +1214,13 @@ def velu(self, isog): ALGORITHM: The input defines an isogeny if only if: - 1. The degree of the characteristic divides the height - of the input. (The height of an Ore polynomial - `P(t)` is the maximum `n` such that `t^n` right-divides - `P(t)`.) - 2. The input right-divides the generator, which can - be tested with Euclidean division. + + 1. The degree of the characteristic divides the height of + the input. (The height of an Ore polynomial `P(\tau)` is the + maximum `n` such that `\tau^n` right-divides `P(\tau)`.) + + 2. The input right-divides the generator, which can + be tested with Euclidean division. We test if the input is an isogeny, and, if it is, we return the quotient of the Euclidean division. @@ -1223,7 +1230,7 @@ def velu(self, isog): :class:`sage.rings.polynomial.ore_polynomial_element.OrePolynomial`. Another possible algorithm is to recursively solve a system, - see :arxiv:`2203.06970`, eq. 1.1. + see :arxiv:`2203.06970`, Eq. 1.1. EXAMPLES:: @@ -1251,7 +1258,7 @@ def velu(self, isog): True The following inputs do not define isogenies, and the method - returns None:: + returns ``None``:: sage: phi.velu(0) Traceback (most recent call last): diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 1cfd2af886f..2e8fa854a7e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -176,7 +176,7 @@ def frobenius_charpoly(self, var='T'): endomorphism, if the rank is two; raise a NotImplementedError otherwise. - Let `\Fq` be the base ring of the function ring. The + Let `\mathbb{F}_q` be the base ring of the function ring. The *characteristic polynomial `\chi` of the Frobenius endomorphism* is defined in [Gek1991]_. An important feature of this polynomial is that it is a monic univariate polynomial with @@ -188,14 +188,14 @@ def frobenius_charpoly(self, var='T'): Let `\chi = T^2 - A(X)T + B(X)` be the characteristic polynomial of the Frobenius endomorphism, let `t^n` be the Ore polynomial that defines the Frobenius endomorphism of `\phi`; by - definition, `n` is the degree over `\Fq` of the base codomain. We - have `\chi(t^n)(\phi(X)) = t^{2n} - \phi_A t^n + \phi_B = 0`, - with `\deg(A) \leq \frac{n}{2}` and `\deg(B) = n`. + definition, `n` is the degree over `\mathbb{F}_q` of the base + codomain. We have `\chi(t^n)(\phi(X)) = t^{2n} - \phi_A t^n + + \phi_B = 0`, with `\deg(A) \leq \frac{n}{2}` and `\deg(B) = n`. Note that the *Frobenius trace* is defined as `A(X)` and the *Frobenius norm* is defined as `B(X)`. - INPUT: (default: `'T'`) the name of the second variable + INPUT: (default: ``'T'``) the name of the second variable OUTPUT: an univariate polynomial with coefficients in the function ring @@ -231,7 +231,7 @@ def frobenius_charpoly(self, var='T'): We compute the Frobenius norm, and with it the Frobenius trace. This gives the Frobenius characteristic polynomial. - See [SM2019]_, Section 4. + See [MS2019]_, Section 4. See docstrings of methods :meth:`frobenius_norm` and :meth:`frobenius_trace` for furthere details on the @@ -250,13 +250,13 @@ def frobenius_norm(self): Return Frobenius norm of the Drinfeld module, if the rank is two; raise a NotImplementedError otherwise. - Let `\Fq[X]` be the function ring, write `\chi = T^2 - A(X)T + - B(X) \in \Fq[X][T]` for the characteristic polynomial of the - Frobenius endomorphism. The *Frobenius norm* is defined as the - polynomial `B(X) \in \Fq[X]`. + Let `\mathbb{F}_q[X]` be the function ring, write `\chi = T^2 - + A(X)T + B(X) \in \mathbb{F}_q[X][T]` for the characteristic + polynomial of the Frobenius endomorphism. The *Frobenius norm* + is defined as the polynomial `B(X) \in \mathbb{F}_q[X]`. - Let `n` be the degree over `\Fq` of the base codomain. Then the - Frobenius norm has degree `n`. + Let `n` be the degree over `\mathbb{F}_q` of the base codomain. + Then the Frobenius norm has degree `n`. OUTPUT: an element in the function ring @@ -280,7 +280,7 @@ def frobenius_norm(self): ALGORITHM: The Frobenius norm is computed using the formula, by - Gekeler, given in [SM2019]_, Section 4, Proposition 3. + Gekeler, given in [MS2019]_, Section 4, Proposition 3. """ self._check_rank_two() L = self._base.codomain().over(self._Fq) @@ -300,13 +300,13 @@ def frobenius_trace(self): Return Frobenius norm of the Drinfeld module, if the rank is two; raise a NotImplementedError otherwise. - Let `\Fq[X]` be the function ring, write `\chi = T^2 - A(X)T + - B(X) \in \Fq[X][T]` for the characteristic polynomial of the - Frobenius endomorphism. The *Frobenius norm* is defined as the - polynomial `B(X) \in \Fq[X]`. + Let `\mathbb{F}_q[X]` be the function ring, write `\chi = T^2 - + A(X)T + B(X) \in \mathbb{F}_q[X][T]` for the characteristic + polynomial of the Frobenius endomorphism. The *Frobenius norm* + is defined as the polynomial `B(X) \in \mathbb{F}_q[X]`. - Let `n` be the degree over `\Fq` of the base codomain. Then the - Frobenius trace has degree `\leq \frac{n}{2}`. + Let `n` be the degree over `\mathbb{F}_q` of the base codomain. + Then the Frobenius trace has degree `\leq \frac{n}{2}`. OUTPUT: an element in the function ring @@ -378,12 +378,12 @@ def is_ordinary(self): ALGORITHM: - Compute the Frobenius trace and test if the `\Fq[X]` - characteristic divides it. + Compute the Frobenius trace and test if the + `\mathbb{F}_q[X]` characteristic divides it. We could also test if the image of the - `\Fq[X]`-characteristic under the Drinfeld module is purely - inseparable; see [Gek1991]_, Proposition 4.1. + `\mathbb{F}_q[X]`-characteristic under the Drinfeld module + is purely inseparable; see [Gek1991]_, Proposition 4.1. """ self._check_rank_two() return not self.is_supersingular() diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index dc3484c759a..f70bc0d9de5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -131,15 +131,15 @@ class DrinfeldModuleHomset(Homset): def __init__(self, X, Y, category=None, check=True): """ - Initialize `self`. + Initialize ``self``. INPUT: - ``X`` -- the domain of the homset - ``Y`` -- the codomain of the homset - - ``category`` (default: None) -- the Drinfeld modules category of + - ``category`` (default: ``None``) -- the Drinfeld modules category of the domain and codomain - - ``check`` (default: True) -- check the validity of the category + - ``check`` (default: ``True``) -- check the validity of the category TESTS:: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index bf511bc7df1..847ce0233bb 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -29,14 +29,14 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, r""" This class represents a Drinfeld module morphism. - Let `\phi, \psi` be two Drinfeld modules with base `\gamma: \Fq[X] + Let `\phi, \psi` be two Drinfeld modules with base `\gamma: \mathbb{F}_q[X] \to K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore - polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for - every `a \in \Fq[X]`. In our case, this is equivalent to `f \phi_X = - \psi_X f`. An *isogeny* is a non-zero morphism. + polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a + \in \mathbb{F}_q[X]`. In our case, this is equivalent to `f \phi_X = \psi_X + f`. An *isogeny* is a non-zero morphism. To create a morphism object, the user should never explicitly - instantiate `DrinfeldModuleMorphism`, but rather call the parent + instantiate :class:`DrinfeldModuleMorphism`, but rather call the parent homset with the defining Ore polynomial:: sage: Fq = GF(25) @@ -170,7 +170,7 @@ def __classcall_private__(cls, parent, x): def __init__(self, parent, ore_pol): r""" - Initialize `self`. + Initialize ``self``. INPUT: From c2691352c3322851cccefc089119cb69d0a60534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 18:42:03 +0200 Subject: [PATCH 146/197] (fix) Fix DrinfeldModule.base_ring docstring --- .../function_field/drinfeld_modules/drinfeld_module.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index b63b5aaef7c..cbcf0cc0a96 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -610,15 +610,15 @@ def __init__(self, gen, category): def base_ring(self): r""" - Raise an exception. + Raise exception ``AttributeError``. The base of a Drinfeld module is a ring morphism, not a ring. - This method is implemented in CategoryObjects, of which - Parent inherits. It returns the base of the category. I - overloaded it to avoid confusion. + This method is implemented in ``CategoryObject``, of which Parent + inherits. It returns the base of the category. I overloaded it + to avoid confusion. - TESTS:: + EXAMPLES:: sage: Fq = GF(25) sage: FqX. = Fq[] From b59d83624afeda23ed07c16115dd37f889b569b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 5 Oct 2022 10:46:57 +0200 Subject: [PATCH 147/197] (fix) Fix DrinfeldModules.Endsets It used to return Homsets(). Now it returns Homsets().Endsets(). --- src/sage/categories/drinfeld_modules.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 7feba60d0b0..01dc332fc27 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -347,11 +347,11 @@ def Endsets(self): sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() - sage: from sage.categories.homsets import Endsets - sage: cat.Endsets() is Endsets() + sage: from sage.categories.homsets import Homsets + sage: cat.Endsets() is Homsets().Endsets() True """ - return Homsets() + return Homsets().Endsets() def characteristic(self): r""" From b7638276a45bbefb066b5da8ee3d58eb37303c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 4 Oct 2022 19:06:56 +0200 Subject: [PATCH 148/197] Split some code blocks using :: Change sage: foo sage: bar to sage: foo :: sage: bar --- src/sage/categories/drinfeld_modules.py | 5 +++- .../drinfeld_modules/drinfeld_module.py | 27 +++++++++++++++-- .../finite_drinfeld_module.py | 14 +++++++++ .../function_field/drinfeld_modules/homset.py | 13 ++++++++- .../drinfeld_modules/morphism.py | 29 +++++++++++++++++-- 5 files changed, 82 insertions(+), 6 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 01dc332fc27..7f88954ef96 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -115,7 +115,6 @@ class DrinfeldModules(Category_over_base): sage: cat.base() is phi.base() True - sage: cat.function_ring() is phi.function_ring() True sage: cat.function_ring() @@ -370,6 +369,8 @@ def characteristic(self): sage: cat.characteristic() X^2 + 7*X + 2 + :: + sage: L = Frac(FqX) sage: psi = DrinfeldModule(FqX, [L.gen(), 1]) sage: psi @@ -592,6 +593,8 @@ def base(self): sage: psi.ore_polring().twisting_morphism().is_identity() True + :: + sage: psi.base().codomain() is psi.function_ring().base_ring() True diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index cbcf0cc0a96..df68ab80502 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -85,6 +85,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 5^12 Defn: X |--> z + :: + sage: Fq = GF(49) sage: FqX. = Fq[] sage: K. = Frac(FqX) @@ -198,6 +200,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): ... ValueError: function ring base must coerce into base codomain + :: + sage: DrinfeldModule(FqX, [1, QQ(1)]) Traceback (most recent call last): ... @@ -211,6 +215,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): ... NotImplementedError: function ring must be a polynomial ring + :: + sage: FqXY. = FqX[] sage: DrinfeldModule(FqXY, [z, 1, 1]) Traceback (most recent call last): @@ -278,8 +284,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.coefficients() [z, 1, 1] - sage: phi.coefficient(1) - 1 + :: + sage: phi.coefficient(1) 1 @@ -291,20 +297,30 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 Defn: X |--> z + :: + sage: phi.ore_polring() # K{t} Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) + :: + sage: phi.function_ring() # Fq[X] Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + :: + sage: phi.gen() # phi_X t^2 + t + z sage: phi.gen() == phi(X) True + :: + sage: phi.constant_coefficient() # Constant coefficient of phi_X z + :: + sage: phi.morphism() # The Drinfeld module as a morphism Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 @@ -507,6 +523,8 @@ def __classcall_private__(cls, function_ring, gen, name='t'): sage: isinstance(phi, FiniteDrinfeldModule) True + :: + sage: K = Frac(FqX) sage: phi = DrinfeldModule(FqX, [K(X), 1]) sage: isinstance(psi, FiniteDrinfeldModule) @@ -652,12 +670,15 @@ def __call__(self, a): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + :: sage: a = X^3 + 4*X + 2 sage: phi(a) == phi(X)^3 + 4*phi(X) + 2 True sage: phi(a)[0] == p_root^3 + 4*p_root + 2 True + :: + sage: phi(0) 0 sage: phi(1) @@ -665,6 +686,8 @@ def __call__(self, a): sage: phi(X) == phi._gen True + :: + sage: a = FqX.random_element(5) sage: phi(a)[0] == phi.category().base()(a) True diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 2e8fa854a7e..0991a343b35 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -210,10 +210,14 @@ def frobenius_charpoly(self, var='T'): sage: chi T^2 + ((3*z3^2 + z3 + 4)*X + 4*z3^2 + 6*z3 + 3)*T + (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + :: + sage: frob_pol = phi.frobenius_endomorphism().ore_polynomial() sage: chi(frob_pol, phi(X)) 0 + :: + sage: A = phi.frobenius_trace() sage: A (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 @@ -221,6 +225,8 @@ def frobenius_charpoly(self, var='T'): sage: B (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + :: + sage: n = 2 # Degree over Fq of the base codomain sage: A.degree() <= n/2 True @@ -270,10 +276,14 @@ def frobenius_norm(self): sage: B (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + :: + sage: n = 2 # Degree over Fq of the base codomain sage: B.degree() == n True + :: + sage: B == phi.frobenius_charpoly()[0] True @@ -333,10 +343,14 @@ def frobenius_trace(self): sage: A (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 + :: + sage: n = 2 # Degree over Fq of the base codomain sage: A.degree() <= n/2 True + :: + sage: A == -phi.frobenius_charpoly()[1] True """ diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index f70bc0d9de5..53cd6719b79 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -47,6 +47,8 @@ class DrinfeldModuleHomset(Homset): sage: hom Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 + :: + sage: from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset sage: isinstance(hom, DrinfeldModuleHomset) True @@ -68,6 +70,8 @@ class DrinfeldModuleHomset(Homset): ... ValueError: Drinfeld modules must be in the same category + :: + sage: sigma = DrinfeldModule(FqX, [1, z6, 2]) sage: Hom(phi, sigma) Traceback (most recent call last): @@ -84,6 +88,8 @@ class DrinfeldModuleHomset(Homset): To (gen): 2*t^2 + z6*t + z6 Defn: 1 + :: + sage: frobenius_endomorphism = end(t^6) sage: frobenius_endomorphism Drinfeld Module morphism: @@ -91,6 +97,8 @@ class DrinfeldModuleHomset(Homset): To (gen): 2*t^2 + z6*t + z6 Defn: t^6 + :: + sage: isogeny = hom(t + 1) sage: isogeny Drinfeld Module morphism: @@ -231,7 +239,6 @@ def __contains__(self, x): sage: hom = Hom(phi, psi) sage: end = End(phi) sage: t = phi.ore_polring().gen() - sage: 1 in hom False sage: t^6 in hom @@ -291,6 +298,8 @@ def _element_constructor_(self, *args, **kwds): To (gen): 2*t^2 + z6*t + z6 Defn: 1 + :: + sage: frobenius_endomorphism = end(t^6) sage: frobenius_endomorphism Drinfeld Module morphism: @@ -298,6 +307,8 @@ def _element_constructor_(self, *args, **kwds): To (gen): 2*t^2 + z6*t + z6 Defn: t^6 + :: + sage: isogeny = hom(t + 1) sage: isogeny Drinfeld Module morphism: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 847ce0233bb..459b2fdf7b3 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -61,7 +61,7 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, ... ValueError: Ore polynomial does not define a morphism - We can get basic data on the morphism:: + One can get basic data on the morphism:: sage: morphism.domain() Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: @@ -71,6 +71,8 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: morphism.domain() is phi True + :: + sage: morphism.codomain() Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 @@ -79,12 +81,17 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: morphism.codomain() is psi True + :: + sage: morphism.ore_polynomial() t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + + :: + sage: morphism.ore_polynomial() is ore_pol True - We can check various properties:: + One can check various properties:: sage: morphism.is_zero() False @@ -146,6 +153,8 @@ def __classcall_private__(cls, parent, x): sage: morphism is Hom(phi, psi)(morphism) True + :: + sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism sage: morphism = DrinfeldModuleMorphism(Sets(), t + 1) Traceback (most recent call last): @@ -274,6 +283,8 @@ def is_zero(self): sage: morphism.is_zero() False + :: + sage: zero_morphism = End(phi)(0) sage: zero_morphism.is_zero() True @@ -296,14 +307,20 @@ def is_isogeny(self): sage: morphism.is_isogeny() True + :: + sage: zero_morphism = End(phi)(0) sage: zero_morphism.is_isogeny() False + :: + sage: identity_morphism = End(phi)(1) sage: identity_morphism.is_isogeny() True + :: + sage: frobenius_endomorphism = phi.frobenius_endomorphism() sage: frobenius_endomorphism.is_isogeny() True @@ -326,14 +343,20 @@ def is_isomorphism(self): sage: morphism.is_isomorphism() False + :: + sage: zero_morphism = End(phi)(0) sage: zero_morphism.is_isomorphism() False + :: + sage: identity_morphism = End(phi)(1) sage: identity_morphism.is_isomorphism() True + :: + sage: frobenius_endomorphism = phi.frobenius_endomorphism() sage: frobenius_endomorphism.is_isomorphism() False @@ -357,6 +380,8 @@ def ore_polynomial(self): sage: ore_pol t + z6^5 + z6^2 + 1 + :: + sage: ore_pol * phi(X) == psi(X) * ore_pol True """ From 7d8e358cde69671bee9978ea4793fc32cfae7965 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Wed, 5 Oct 2022 17:25:40 +0200 Subject: [PATCH 149/197] (fix) Fix lint check Github job See https://github.com/sagemath/sagetrac-mirror/actions/runs/3190135097/jobs/5204830911#step:5:23. --- src/sage/rings/function_field/drinfeld_modules/homset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 53cd6719b79..690d35653b8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -227,7 +227,7 @@ def __contains__(self, x): OUTPUT: a boolean - EXAMPLES:: + EXAMPLES: In the next examples, the input is an Ore polynomial:: From 26fe42659d8bed43835721f8a9d40303f4dca6da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 10 Oct 2022 18:21:04 +0200 Subject: [PATCH 150/197] (fix) Adress typos from comment 264 Thanks David. I changed the first typo and the nonXYZ typos. --- src/sage/categories/drinfeld_modules.py | 8 ++++---- .../drinfeld_modules/drinfeld_module.py | 16 ++++++++-------- .../function_field/drinfeld_modules/morphism.py | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 7f88954ef96..d6c4ca17c6a 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -52,7 +52,7 @@ class DrinfeldModules(Category_over_base): These notations will be used throughout this documentation. We say that `\mathbb{F}_q[X]` is the function ring of the category; - `K\{\tau\}` is the re polynomial ring of the category. The constant + `K\{\tau\}` is the polynomial ring of the category. The constant coefficient of the category is the image of `X` under the base. The `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field `K` can also be referred to as its function ring-characteristic. @@ -170,7 +170,7 @@ class DrinfeldModules(Category_over_base): sage: cat = DrinfeldModules(base) Traceback (most recent call last): ... - ValueError: base must be a non zero morphism + ValueError: base must be a nonzero morphism sage: base = Hom(FqX, FqX)(1) sage: cat = DrinfeldModules(base) @@ -245,9 +245,9 @@ def __init__(self, base, name='t'): K = base.codomain() if not K.is_field(): raise TypeError('base codomain must be a field') - # Check base is a non zero morphism + # Check base is a nonzero morphism if base(X).is_zero(): - raise ValueError('base must be a non zero morphism') + raise ValueError('base must be a nonzero morphism') # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index df68ab80502..9b4830488e6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -63,7 +63,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): `\mathbb{F}_q`-algebra morphism `\phi: \mathbb{F}_q[X] \to K\{\tau\}` such that: - 1. The image of `\phi` contains non-constant Ore polynomials. + 1. The image of `\phi` contains nonconstant Ore polynomials. 2. For every element `a` in the `\mathbb{F}_q[X]`, the constant coefficient `\phi(a)` is `\gamma(a)`. @@ -184,12 +184,12 @@ class DrinfeldModule(Parent, UniqueRepresentation): ... ValueError: generator must have positive degree - The constant coefficient must be non zero:: + The constant coefficient must be nonzero:: sage: DrinfeldModule(FqX, [K(0), K(1)]) Traceback (most recent call last): ... - ValueError: base must be a non zero morphism + ValueError: base must be a nonzero morphism The coefficients of the generator must lie in an `\mathbb{F}_q[X]`-field, where `\mathbb{F}_q[X]` is the function @@ -344,7 +344,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): A morphism of Drinfeld modules `\phi \to \psi` is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a` in the function ring. In our case, this is equivalent to `f \phi_X = - \psi_X f`. An isogeny is a non-zero morphism. + \psi_X f`. An isogeny is a nonzero morphism. Use the ``in`` syntax to test if an Ore polynomial defines a morphism:: @@ -411,7 +411,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: The Vélu formula - Let ``ore_pol`` be a non-zero Ore polynomial. We can decide if there + Let ``ore_pol`` be a nonzero Ore polynomial. We can decide if there exists a Drinfeld module ``psi`` such that ``ore_pol`` is an isogeny from ``self`` to ``psi``. If so, we find ``psi``:: @@ -842,7 +842,7 @@ def coefficient(self, n): INPUT: - - ``n`` -- a non-negative integer + - ``n`` -- a nonnegative integer OUTPUT: an element in the base codomain @@ -877,7 +877,7 @@ def coefficients(self, sparse=True): Return the coefficients of the generator, as a list. If the flag ``sparse`` is ``True`` (default), only return the - non-zero coefficients; otherwise, return all of them. + nonzero coefficients; otherwise, return all of them. INPUT: @@ -897,7 +897,7 @@ def coefficients(self, sparse=True): z12^3, z12^5] - Careful, the method only returns the non-zero coefficients, + Careful, the method only returns the nonzero coefficients, unless otherwise specified:: sage: rho = DrinfeldModule(FqX, [p_root, 0, 0, 0, 1]) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 459b2fdf7b3..9f36cdeca13 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -33,7 +33,7 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, \to K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a \in \mathbb{F}_q[X]`. In our case, this is equivalent to `f \phi_X = \psi_X - f`. An *isogeny* is a non-zero morphism. + f`. An *isogeny* is a nonzero morphism. To create a morphism object, the user should never explicitly instantiate :class:`DrinfeldModuleMorphism`, but rather call the parent From bee20a07453c5b18ac51a36835e6d20c1e33a1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 11 Oct 2022 18:48:18 +0200 Subject: [PATCH 151/197] Add details on the base definition in DrinfeldModule doc See comments 264 and 265 for context. --- .../drinfeld_modules/drinfeld_module.py | 62 ++++++++++++++++++- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 9b4830488e6..efac5f6cdb5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -72,8 +72,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): The Drinfeld `\mathbb{F}_q[X]`-module `\phi` is uniquely determined by the image `\phi_X` of `X`. This serves as input of the class. - Despite an emphasis on the finite case, the base codomain can be any - extension of the field `\mathbb{F}_q`:: + A Drinfeld module is saif to be finite if the base ring codomain is + a finite field. Despite an emphasis on this case, the base codomain + can be any extension of the field `\mathbb{F}_q`:: sage: Fq = GF(25) sage: FqX. = Fq[] @@ -99,7 +100,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. NOTE:: - In the first case, the Drinfeld module is said to be finite. See + Finite Drinfeld modules are implemented in the class :class:`sage.rings.function_field.drinfeld_modules.finite_drinfeld_module`. We say that `\mathbb{F}_q[X]` is the function ring of `\phi`; @@ -147,6 +148,19 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 Defn: X |--> z + Note that the definition of the base morphism is implicit; it is + defined as the `\mathbb{F}_q`-algebra morphism `\mathbb{F}_q[X] \to + K` which maps `X` to the constant coefficient of the generator, and + where `K` is the compositum of all the parents of the coefficients:: + + sage: phi.base().codomain() is K + True + + .. NOTE:: + + Formally, `K` is defined as `Sequence(gen).universe()`, where + `gen` is the generator of the Drinfeld module. + The above Drinfeld module is finite; it can also be infinite:: sage: L = Frac(FqX) @@ -493,6 +507,48 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi = DrinfeldModule(FqX, [K(1), 1]) sage: isinstance(phi.ore_polring(), OrePolynomialRing) True + + Test that the base morphism is correct:: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K = Frac(Fq) + sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(1)]) + sage: phi.base().codomain() is K + True + + :: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: k = Frac(Fq) + sage: kT. = k[] + sage: K = k.extension(T^3 + T + 1) + sage: phi = DrinfeldModule(FqX, [Fq.gen(), K.gen()]) + sage: phi.base().codomain() is K + True + + In particular, note that the field `K` may not be the smallest field + of definition of the coefficients:: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: k = Frac(Fq) + sage: kT. = k[] + sage: K = k.extension(T^3 + T + 1) + sage: phi = DrinfeldModule(FqX, [K(k.gen()), 1]) + sage: phi.base().codomain() is K + True + + :: + + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(Fq.gen())]) + sage: phi.base().codomain() is K + True + """ @staticmethod From 78c99e05e5b12568dcb2def70da21cfc0d8fa870 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 11 Oct 2022 18:54:43 +0200 Subject: [PATCH 152/197] Enhance "base ring comment" See comment 264 for context. --- src/sage/categories/drinfeld_modules.py | 10 ---------- .../function_field/drinfeld_modules/drinfeld_module.py | 7 +++++++ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index d6c4ca17c6a..dc11284e7fa 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -721,13 +721,3 @@ def ore_polring(self): True """ return self.category().ore_polring() - - # FIXME - # The parent method `base_ring` is defined not here, as it - # should be, but in `DrinfeldModule`. - # - # This is because calling `phi.base_ring()` calls the - # `base_ring` method of `CategoryObject` and not the one defined - # here. - # - # This works, but any better solution would be welcome. diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index efac5f6cdb5..cbc1c8ac8f2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -704,6 +704,13 @@ def base_ring(self): ... AttributeError: the base of a Drinfeld module is a morphism """ + # FIXME + # This method depends on the sole category of the Drinfeld + # module. It should therefore be implemented in the + # `ParentMethods` bloc of `DrinfeldModule`. This is not possible + # as the method `base_ring` from `CategoryObject` --- of which + # `Parent` and so `DrinfeldModule inherit --- is called instead. + # Any better way would be welcome. raise AttributeError('the base of a Drinfeld module is a morphism') def __call__(self, a): From 0374b6d900171ca54f946e71f52481e6dca2ae43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 15 Nov 2022 17:08:23 +0100 Subject: [PATCH 153/197] (breaking) Change base morphism to base ring for Drinfeld modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WARNING: THIS IS EXTREMELY BUGGY! The base of the category was a ring morphism, it is now a ring extension. This change was discussed in comment 268 of the ticket: https://trac.sagemath.org/ticket/33713#comment:268. I updated both the code, documentation, and doctests. Many tests are unfortunately tagued with `# todo: not tested`, as several bugs need be corrected. Those are the bugs. 1. The first bug is https://trac.sagemath.org/ticket/34752. 2. One cannot create ring extensions using 'exotic' morphisms:: sage: Fq = GF(11) sage: FqX. = Fq[] sage: K = Frac(FqX) sage: f = Hom(FqX, K)(K(1)) sage: K_over = K.over(f) # Exception This a problem to define Drinfeld modules with arbitrary constant coefficient. 3. One cannot see the base ring — which is a ring extension over Fq[X] — as a ring extension over Fq:: sage: Fq = GF(25) sage: FqX. = Fq[] sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z12, 0, 1]) sage: K_over = phi.base_ring() sage: K_over.over(Fq) # Exception This would be very helpful to compute the function-ring characteristic and the Frobenius endomorphism (it requires the degree of the base field over Fq). 4. This other coercion problem causes problem to the ``invert`` method:: Fq = GF(25) FqX. = Fq[] K. = Fq.extension(6) phi = DrinfeldModule(FqX, [z12, 0, 1]) K_over = phi.base_ring() Fq(K_over(1)) # Exception --- src/sage/categories/drinfeld_modules.py | 306 +++++----- .../function_field/drinfeld_modules/action.py | 29 +- .../drinfeld_modules/drinfeld_module.py | 535 +++++++++--------- .../finite_drinfeld_module.py | 83 +-- .../function_field/drinfeld_modules/homset.py | 8 +- .../drinfeld_modules/morphism.py | 20 +- 6 files changed, 484 insertions(+), 497 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index dc11284e7fa..5f78eab3a3f 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -18,45 +18,44 @@ # http://www.gnu.org/licenses/ # ****************************************************************************** -from sage.categories.category_types import Category_over_base +from sage.categories.category_types import Category_over_base_ring from sage.categories.homsets import Homsets from sage.misc.functional import log from sage.misc.latex import latex from sage.rings.integer import Integer -from sage.rings.morphism import RingHomomorphism from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing from sage.rings.polynomial.polynomial_ring import PolynomialRing_general +from sage.rings.ring_extension import RingExtension_generic -class DrinfeldModules(Category_over_base): +class DrinfeldModules(Category_over_base_ring): r""" This class represents the category of Drinfeld modules on a given base. Let `\mathbb{F}_q[X]` be a polynomial ring with coefficients in a - finite field `\mathbb{F}_q` and let `K` be a field. We fix a ring - morphism `\gamma: \mathbb{F}_q[X] \to K`, which we call the *base* - of the category (it is the base of the Drinfeld modules in the - category). Please note that the base is not a ring; in particular, - it is not the field `K`. We also call `K` an - `\mathbb{F}_q[X]`-field. - - The category is uniquely defined by its base. - - The monic polynomial that generates the kernel of the base is called - the `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field - `K`. + finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring + morphism `\gamma: \mathbb{F}_q[X] \to K`. We say that the field `K` + is an `\mathbb{F}_q[X]`-field, so that the *base of the category* is + defined as the `\mathbb{F}_q[X]`-field *K*. The base uniquely + defines the category, and we also refer to it as the *base ring* or + *base field*. The *base morphism* is the morphism `\gamma: + \mathbb{F}_q[X] \to K`. .. NOTE:: - These notations will be used throughout this documentation. + Equivalently, the base of the category could be defined as the + base morphism `\gamma: \mathbb{F}_q[X] \to K`. + + The monic polynomial that generates the kernel of the base morphism + is called the `\mathbb{F}_q[X]`-characteristic of the + `\mathbb{F}_q[X]`-field `K`. It cal also be referred to as the + function-field characteristic of `K`. We say that `\mathbb{F}_q[X]` is the function ring of the category; - `K\{\tau\}` is the polynomial ring of the category. The constant - coefficient of the category is the image of `X` under the base. The - `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field `K` - can also be referred to as its function ring-characteristic. - Finally, `K` is just refered to as the codomain base. + `K\{\tau\}` is the Ore polynomial ring of the category. The constant + coefficient of the category is the image of `X` under the base + morphism. INPUT: the base ring morphism @@ -73,32 +72,37 @@ class DrinfeldModules(Category_over_base): sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat - Category of Drinfeld modules defined over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^4 - Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + Category of Drinfeld modules defined over Finite Field in z of size 11^4 over its base The output tells the user that the category is only defined by its base. .. RUBRIC:: Properties of the category - The base, which is a morphism, is retrieved using the method - :meth:`morphism`:: + The base ring is retrieved using the method :meth:`base` or + :meth:`base_ring`:: sage: cat.base() + Finite Field in z of size 11^4 over its base + sage: cat.base_ring() + Finite Field in z of size 11^4 over its base + + Equivalently, one can use :meth:`base_morphism` to retrieve the base + morphism:: + + sage: cat.base_morphism() Ring morphism: From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^4 + To: Finite Field in z of size 11^4 over its base Defn: X |--> z^3 + 7*z^2 + 6*z + 10 The so-called constant coefficient --- which is the same for all Drinfeld modules in the category --- is simply the image of `X` by - this morphism: + the base morphism:: sage: cat.constant_coefficient() z^3 + 7*z^2 + 6*z + 10 - sage: cat.base()(X) == cat.constant_coefficient() + sage: cat.base_morphism()(X) == cat.constant_coefficient() True Similarly, the function ring-characteristic of the category is @@ -107,25 +111,24 @@ class DrinfeldModules(Category_over_base): sage: cat.characteristic() X^2 + 7*X + 2 - sage: cat.base()(cat.characteristic()) + sage: cat.base_morphism()(cat.characteristic()) 0 - The base, function ring and Ore polynomial ring are the - same for the category and its objects:: + The base ring, base morphism, function ring and Ore polynomial ring + are the same for the category and its objects:: sage: cat.base() is phi.base() True + sage: cat.base_morphism() is phi.base_morphism() + True sage: cat.function_ring() is phi.function_ring() True sage: cat.function_ring() Univariate Polynomial Ring in X over Finite Field of size 11 - sage: cat.function_ring() is cat.base().domain() - True - sage: cat.ore_polring() is phi.ore_polring() True sage: cat.ore_polring() - Ore Polynomial Ring in t over Finite Field in z of size 11^4 twisted by z |--> z^11 + Ore Polynomial Ring in t over Finite Field in z of size 11^4 over its base twisted by Frob .. RUBRIC:: Creating Drinfeld module objects from the category @@ -134,10 +137,7 @@ class DrinfeldModules(Category_over_base): sage: psi = cat.object([p_root, 1]) sage: psi - Drinfeld module defined by X |--> t + z^3 + 7*z^2 + 6*z + 10 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^4 - Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + Drinfeld module defined by X |--> t + z^3 + 7*z^2 + 6*z + 10 over base Finite Field in z of size 11^4 over its base sage: psi.category() is cat True @@ -147,7 +147,7 @@ class DrinfeldModules(Category_over_base): sage: cat.object([z, 1]) Traceback (most recent call last): ... - ValueError: constant coefficient must be the generator of the morphism that defines the category + ValueError: constant coefficient must equal that of the category It is also possible to create a random object in the category. The input is the desired rank:: @@ -170,13 +170,23 @@ class DrinfeldModules(Category_over_base): sage: cat = DrinfeldModules(base) Traceback (most recent call last): ... - ValueError: base must be a nonzero morphism + TypeError: base field must be a ring extension + + :: + + sage: cat.base().defining_morphism() == cat.base_morphism() + True + + :: sage: base = Hom(FqX, FqX)(1) sage: cat = DrinfeldModules(base) Traceback (most recent call last): ... - TypeError: base codomain must be a field + TypeError: base field must be a ring extension + + :: + sage: base = 'I hate Rostropovitch' sage: cat = DrinfeldModules(base) # known bug (blankline) @@ -185,6 +195,8 @@ class DrinfeldModules(Category_over_base): ... TypeError: input must be a ring morphism + :: + sage: ZZT. = ZZ[] sage: base = Hom(ZZT, K)(1) sage: cat = DrinfeldModules(base) # known bug (blankline) @@ -193,15 +205,17 @@ class DrinfeldModules(Category_over_base): ... TypeError: function ring base must be a finite field """ - def __init__(self, base, name='t'): + + def __init__(self, base_field, name='t'): r""" Initialize `self`. INPUT: - - ``base`` -- the base ring morphism + - ``base_ring`` -- the base field, which is a ring extension + over a base - ``name`` (default: `'t'`) -- the name of the Ore polynomial - ring generator + variable TESTS:: @@ -211,25 +225,33 @@ def __init__(self, base, name='t'): sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() - sage: ore_polring. = OrePolynomialRing(K, K.frobenius_endomorphism()) + sage: ore_polring. = OrePolynomialRing(phi.base(), phi.base().frobenius_endomorphism()) sage: cat._ore_polring is ore_polring True - sage: base = Hom(FqX, K)(p_root) - sage: cat._base == base + sage: i = phi.base().coerce_map_from(K) + sage: base_morphism = Hom(FqX, K)(p_root) + sage: cat.base() == K.over(base_morphism) + True + sage: cat._base_morphism == i * base_morphism True sage: cat._function_ring is FqX True - sage: cat._constant_coefficient == base(X) + sage: cat._constant_coefficient == base_morphism(X) True sage: cat._characteristic(cat._constant_coefficient) 0 """ - # Check input is a ring Morphism - if not isinstance(base, RingHomomorphism): - raise TypeError('input must be a Ring morphism') - self._base = base - self._function_ring = base.domain() - # Check domain of base is Fq[X] + # Check input is a ring extension + if not isinstance(base_field, RingExtension_generic): + raise TypeError('base field must be a ring extension') + base_morphism = base_field.defining_morphism() + self._base_morphism = base_morphism + # Check input is a field + if not base_field.is_field(): + raise TypeError('input must be a field') + self._base_field = base_field + self._function_ring = base_morphism.domain() + # Check domain of base morphism is Fq[X] function_ring = self._function_ring if not isinstance(function_ring, PolynomialRing_general): raise NotImplementedError('function ring must be a polynomial ' @@ -238,32 +260,26 @@ def __init__(self, base, name='t'): if not function_ring_base.is_field() \ or not function_ring_base.is_finite(): raise TypeError('function ring base must be a finite field') + # Shortcuts Fq = function_ring_base FqX = function_ring X = FqX.gen() - # Check codomain of base is a field - K = base.codomain() - if not K.is_field(): - raise TypeError('base codomain must be a field') - # Check base is a nonzero morphism - if base(X).is_zero(): - raise ValueError('base must be a nonzero morphism') + K = base_field # A ring extension # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) tau = K.frobenius_endomorphism(d) self._ore_polring = OrePolynomialRing(K, tau, names=name, polcast=False) # Create constant coefficient - self._constant_coefficient = base(X) + self._constant_coefficient = base_morphism(X) # Create characteristic self._characteristic = None if K.is_finite(): - f = base * FqX.coerce_map_from(Fq) # Fq -> K - E = K.over(f) - self._characteristic = FqX(E(base(X)).minpoly()) + #FIXME: This minpoly is over Fp, not Fq + self._characteristic = FqX(K(base_morphism(X)).minpoly()) elif FqX.is_subring(K): self._characteristic = Integer(0) - super().__init__(base=base) + super().__init__(base=base_field) def _latex_(self): r""" @@ -280,15 +296,10 @@ def _latex_(self): sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: latex(cat) - \text{Category{ }of{ }Drinfeld{ }modules{ }defined{ }over{ }base{ }\begin{array}{l} - \text{\texttt{Ring{ }morphism:}}\\ - \text{\texttt{{ }{ }From:{ }Univariate{ }Polynomial{ }Ring{ }in{ }X{ }over{ }Finite{ }Field{ }of{ }size{ }11}}\\ - \text{\texttt{{ }{ }To:{ }{ }{ }Finite{ }Field{ }in{ }z{ }of{ }size{ }11{\char`\^}4}}\\ - \text{\texttt{{ }{ }Defn:{ }X{ }|{-}{-}>{ }z{\char`\^}3{ }+{ }7*z{\char`\^}2{ }+{ }6*z{ }+{ }10}} - \end{array} + \text{Category{ }of{ }Drinfeld{ }modules{ }defined{ }over{ }\Bold{F}_{11^{4}} """ return f'\\text{{Category{{ }}of{{ }}Drinfeld{{ }}modules{{ }}' \ - f'defined{{ }}over{{ }}base{{ }}{latex(self._base)}' + f'defined{{ }}over{{ }}{latex(self._base_field)}' def _repr_(self): r""" @@ -305,12 +316,9 @@ def _repr_(self): sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat - Category of Drinfeld modules defined over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^4 - Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + Category of Drinfeld modules defined over Finite Field in z of size 11^4 over its base """ - return f'Category of Drinfeld modules defined over base {self._base}' + return f'Category of Drinfeld modules defined over {self._base_field}' def Homsets(self): r""" @@ -352,6 +360,30 @@ def Endsets(self): """ return Homsets().Endsets() + def base_morphism(self): + r""" + Return the base morphism of the category + + OUTPUT: a ring morphism + + EXAMPLES:: + + sage: Fq = GF(11) + sage: FqX. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.base_morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field of size 11 + To: Finite Field in z of size 11^4 over its base + Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + sage: cat.constant_coefficient() == cat.base_morphism()(X) + True + """ + return self._base_morphism + def characteristic(self): r""" Return the function ring-characteristic. @@ -371,15 +403,8 @@ def characteristic(self): :: - sage: L = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [L.gen(), 1]) - sage: psi - Drinfeld module defined by X |--> t + X over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Fraction Field of Univariate Polynomial Ring in X over Finite Field of size 11 - Defn: X |--> X - sage: fox = psi.category() - sage: fox.characteristic() + sage: psi = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) # todo: not tested + sage: psi.category().characteristic() # todo: not tested 0 """ if self._characteristic is None: @@ -449,10 +474,7 @@ def object(self, gen): sage: cat = phi.category() sage: psi = cat.object([p_root, 0, 1]) sage: psi - Drinfeld module defined by X |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^4 - Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + Drinfeld module defined by X |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over base Finite Field in z of size 11^4 over its base sage: t = phi.ore_polring().gen() sage: cat.object(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi True @@ -461,10 +483,9 @@ def object(self, gen): # If gen is not in the Ore polring, an exception is raised gen = self._ore_polring(gen) X = self._function_ring.gen() - base = self._base - if gen[0] != base(X): - raise ValueError('constant coefficient must be the generator ' - 'of the morphism that defines the category') + if gen[0] != self._base_morphism(X): + raise ValueError('constant coefficient must equal that of the ' \ + 'category') return DrinfeldModule(self._function_ring, gen) def ore_polring(self): @@ -482,7 +503,7 @@ def ore_polring(self): sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat.ore_polring() - Ore Polynomial Ring in t over Finite Field in z of size 11^4 twisted by z |--> z^11 + Ore Polynomial Ring in t over Finite Field in z of size 11^4 over its base twisted by Frob sage: cat.ore_polring() is phi.ore_polring() True """ @@ -515,7 +536,7 @@ def random_object(self, rank): if rank <= 0: raise ValueError('rank must be a positive integer') - K = self._base.codomain() + K = self._base_field coeffs = [self._constant_coefficient] for _ in range(rank-1): coeffs.append(K.random_element()) @@ -545,9 +566,9 @@ class ParentMethods: def base(self): r""" - Return the base of the Drinfeld module. + Return the base field of the Drinfeld module. - OUTPUT: a ring morphism + OUTPUT: a field, which is a ring extension over a base EXAMPLES:: @@ -557,49 +578,41 @@ def base(self): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: phi.base() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Finite Field in z12 of size 5^12 over its base - The base codomain can be infinite:: + The base can be infinite:: - sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) - sage: sigma.base() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - Defn: X |--> X - - And it can also be the base field of the function ring:: - - sage: psi = DrinfeldModule(FqX, [Fq(1), Fq.gen()]) - sage: psi.base() - Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z2 of size 5^2 - Defn: X |--> 1 - - In this case the Ore polynomial ring is isomorphic to a regular - polynomial ring:: + sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) # todo: not tested + sage: sigma.base() # todo: not tested + Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 over its base + """ + return self.category().base() - sage: psi.ore_polring() - Ore Polynomial Ring in t over Finite Field in z2 of size 5^2 twisted by Identity - sage: psi.ore_polring().twisting_morphism() - Identity endomorphism of Finite Field in z2 of size 5^2 + def base_morphism(self): + r""" + Return the base morphism of the Drinfeld module. - TESTS:: + OUTPUT: a ring morphism - sage: psi.ore_polring().twisting_morphism().is_identity() - True + EXAMPLES:: - :: + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi.base_morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + To: Finite Field in z12 of size 5^12 over its base + Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: psi.base().codomain() is psi.function_ring().base_ring() - True + The base codomain can be infinite:: + sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) # todo: not tested + sage: sigma.base_morphism() # todo: not tested """ - return self.category().base() + return self.category().base_morphism() def characteristic(self): r""" @@ -614,14 +627,16 @@ def characteristic(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.characteristic() + sage: phi.characteristic() # todo: not tested X^2 + (4*z2 + 2)*X + 2 - sage: phi.characteristic()(phi.constant_coefficient()) + sage: phi.base_morphism()(phi.characteristic()) 0 - sage: L = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [L(1), 0, 0, L(1)]) - sage: psi.characteristic() + :: + + sage: L = Frac(FqX) # todo: not tested + sage: psi = DrinfeldModule(FqX, [L(1), 0, 0, L(1)]) # todo: not tested + sage: psi.characteristic() # todo: not tested 0 """ return self.category().characteristic() @@ -675,10 +690,7 @@ def constant_coefficient(self): sage: t = phi.ore_polring().gen() sage: psi = cat.object(phi.constant_coefficient() + t^3) sage: psi - Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base Reciprocally, it is impossible to create two Drinfeld modules in this category if they do not share the same constant @@ -687,7 +699,7 @@ def constant_coefficient(self): sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) Traceback (most recent call last): ... - ValueError: constant coefficient must be the generator of the morphism that defines the category + ValueError: constant coefficient must equal that of the category """ return self.category().constant_coefficient() @@ -706,7 +718,7 @@ def ore_polring(self): sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: ore_polring = phi.ore_polring() sage: ore_polring - Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 The Ore polynomial ring can also be retrieved from the category of the Drinfeld module:: diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 096b5428059..e8319a1f308 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -51,10 +51,7 @@ class DrinfeldModuleAction(Action): sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^2 - Defn: X |--> z + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by X |--> t^3 + z over base Finite Field in z of size 11^2 over its base The action on elements is computed as follows:: @@ -90,14 +87,14 @@ def __init__(self, drinfeld_module): sage: action = phi.action() sage: action._drinfeld_module is phi True - sage: action._field is phi.base().codomain() + sage: action._base_ring is phi.base() True """ if not isinstance(drinfeld_module, DrinfeldModule): raise TypeError('input must be a DrinfeldModule') self._drinfeld_module = drinfeld_module - self._field = drinfeld_module.base().codomain() - super().__init__(drinfeld_module.function_ring(), self._field) + self._base_ring = drinfeld_module.base() + super().__init__(drinfeld_module.function_ring(), self._base_ring) def _act_(self, pol, x): r""" @@ -128,7 +125,7 @@ def _act_(self, pol, x): """ if pol not in self._drinfeld_module.function_ring(): raise TypeError('first input must be in the function ring') - if x not in self._field: + if x not in self._base_ring: raise TypeError('second input must be in the field acted upon') return self._drinfeld_module(pol)(x) @@ -146,15 +143,10 @@ def _latex_(self): sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) sage: action = phi.action() sage: latex(action) - \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto t^{3} + z\text{{ }over{ }base{ }}\begin{array}{l} - \text{\texttt{Ring{ }morphism:}}\\ - \text{\texttt{{ }{ }From:{ }Univariate{ }Polynomial{ }Ring{ }in{ }X{ }over{ }Finite{ }Field{ }of{ }size{ }11}}\\ - \text{\texttt{{ }{ }To:{ }{ }{ }Finite{ }Field{ }in{ }z{ }of{ }size{ }11{\char`\^}2}}\\ - \text{\texttt{{ }{ }Defn:{ }X{ }|{-}{-}>{ }z}} - \end{array} + \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto t^{3} + z\text{{ }over{ }base{ }}\Bold{F}_{11^{2}} """ return f'\\text{{Action{{ }}on{{ }}}}' \ - f'{latex(self._field)}\\text{{{{ }}' \ + f'{latex(self._base_ring)}\\text{{{{ }}' \ f'induced{{ }}by{{ }}}}{latex(self._drinfeld_module)}' def _repr_(self): @@ -171,12 +163,9 @@ def _repr_(self): sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by X |--> t^3 + z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 - To: Finite Field in z of size 11^2 - Defn: X |--> z + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by X |--> t^3 + z over base Finite Field in z of size 11^2 over its base """ - return f'Action on {self._field} induced by ' \ + return f'Action on {self._base_ring} induced by ' \ f'{self._drinfeld_module}' def drinfeld_module(self): diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index cbc1c8ac8f2..d82632c6db2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -25,12 +25,14 @@ # ***************************************************************************** from sage.categories.drinfeld_modules import DrinfeldModules +from sage.categories.homset import Hom from sage.matrix.constructor import Matrix from sage.misc.latex import latex from sage.modules.free_module_element import vector from sage.rings.integer import Integer from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.polynomial_ring import PolynomialRing_general +from sage.rings.ring_extension import RingExtension_generic from sage.structure.parent import Parent from sage.structure.sequence import Sequence from sage.structure.unique_representation import UniqueRepresentation @@ -41,25 +43,32 @@ class DrinfeldModule(Parent, UniqueRepresentation): This class represents a Drinfeld `\mathbb{F}_q[X]`-module. Let `\mathbb{F}_q[X]` be a polynomial ring with coefficients in a - finite field `\mathbb{F}_q` and let `K` be a field. We fix a ring - morphism `\gamma: \mathbb{F}_q[X] \to K`, which we call the base of - the Drinfeld module. + finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring + morphism `\gamma: \mathbb{F}_q[X] \to K`. We say that the field `K` + is an `\mathbb{F}_q[X]`-field, so that the *base of the Drinfeld + module* is defined as the `\mathbb{F}_q[X]`-field *K*. The base + uniquely defines the category, and we also refer to it as the *base + ring* or *base field*. The *base morphism* is the morphism `\gamma: + \mathbb{F}_q[X] \to K`. .. NOTE:: - The base is not a ring. Specifically, it is not the field `K`. - We say however that `K` is an `\mathbb{F}_q[X]`-field. + Equivalently, the base of the Drinfeld module could be defined as the + base morphism `\gamma: \mathbb{F}_q[X] \to K`. - The base of the Drinfeld module is the base of the category of - the Drinfeld module. - The monic polynomial that generates the kernel of the base is called - the `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field - `K`. + .. NOTE:: + + See also :class:`sage.categories.drinfeld_modules`. + + The monic polynomial that generates the kernel of the base morphism + is called the `\mathbb{F}_q[X]`-characteristic of the + `\mathbb{F}_q[X]`-field `K`. It cal also be referred to as the + function-field characteristic of `K`. Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in `K` and Frobenius variable `\tau: x \mapsto x^q`. A Drinfeld - `\mathbb{F}_q[X]`-module over the base `\gamma` is an + `\mathbb{F}_q[X]`-module over the `\mathbb{F}_q[X]`-field `K` is an `\mathbb{F}_q`-algebra morphism `\phi: \mathbb{F}_q[X] \to K\{\tau\}` such that: @@ -70,33 +79,26 @@ class DrinfeldModule(Parent, UniqueRepresentation): For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. The Drinfeld `\mathbb{F}_q[X]`-module `\phi` is uniquely determined - by the image `\phi_X` of `X`. This serves as input of the class. + by the image `\phi_X` of `X` — this serves as input of the class. - A Drinfeld module is saif to be finite if the base ring codomain is - a finite field. Despite an emphasis on this case, the base codomain - can be any extension of the field `\mathbb{F}_q`:: + A Drinfeld module is said to be finite if the field `K` is. Despite + an emphasis on this case, the base field can be any extension of + `\mathbb{F}_q`:: sage: Fq = GF(25) sage: FqX. = Fq[] sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z, 4, 1]) sage: phi - Drinfeld module defined by X |--> t^2 + 4*t + z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z of size 5^12 - Defn: X |--> z + Drinfeld module defined by X |--> t^2 + 4*t + z over base Finite Field in z of size 5^12 over its base :: sage: Fq = GF(49) sage: FqX. = Fq[] sage: K. = Frac(FqX) - sage: phi = DrinfeldModule(FqX, [z, X+1]) - sage: phi - Drinfeld module defined by X |--> (X + 1)*t + X over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 - To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 - Defn: X |--> X + sage: psi = DrinfeldModule(FqX, [z, X+1]) # todo: not tested + sage: psi # todo: not tested .. NOTE:: @@ -143,36 +145,29 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(FqX, [z, 1, 1]) sage: phi - Drinfeld module defined by X |--> t^2 + t + z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z + Drinfeld module defined by X |--> t^2 + t + z over base Finite Field in z of size 3^12 over its base Note that the definition of the base morphism is implicit; it is - defined as the `\mathbb{F}_q`-algebra morphism `\mathbb{F}_q[X] \to - K` which maps `X` to the constant coefficient of the generator, and - where `K` is the compositum of all the parents of the coefficients:: - - sage: phi.base().codomain() is K - True - - .. NOTE:: - - Formally, `K` is defined as `Sequence(gen).universe()`, where - `gen` is the generator of the Drinfeld module. + defined as the `\mathbb{F}_q`-algebra morphism defined from + `\mathbb{F}_q[X]` to the base ring, and the base ring is a ring + extension over the base morphism whose underlying ring is the + compositum of all the parents of the coefficients. The above Drinfeld module is finite; it can also be infinite:: - sage: L = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [L(X), 1, X^3 + X + 1]) - sage: psi + sage: L = Frac(FqX) # todo: not tested + sage: psi = DrinfeldModule(FqX, [L(X), 1, X^3 + X + 1]) # todo: not tested + sage: psi # todo: not tested Drinfeld module defined by X |--> (X^3 + X + 1)*t^2 + t + X over base Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 Defn: X |--> X + + :: + sage: phi.is_finite() True - sage: psi.is_finite() + sage: psi.is_finite() # todo: not tested False In those examples, we used a list of coefficients (``[z, 1, 1]``) to @@ -184,76 +179,18 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: rho_X = z + t^3 sage: rho = DrinfeldModule(FqX, rho_X) sage: rho - Drinfeld module defined by X |--> t^3 + z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z + Drinfeld module defined by X |--> t^3 + z over base Finite Field in z of size 3^12 over its base sage: rho(X) == rho_X True - The generator must have positive degree:: - - sage: DrinfeldModule(FqX, [z]) - Traceback (most recent call last): - ... - ValueError: generator must have positive degree - - The constant coefficient must be nonzero:: - - sage: DrinfeldModule(FqX, [K(0), K(1)]) - Traceback (most recent call last): - ... - ValueError: base must be a nonzero morphism - - The coefficients of the generator must lie in an - `\mathbb{F}_q[X]`-field, where `\mathbb{F}_q[X]` is the function - ring of the Drinfeld module:: - - sage: DrinfeldModule(FqX, [z, QQ(1)]) - Traceback (most recent call last): - ... - ValueError: function ring base must coerce into base codomain - - :: - - sage: DrinfeldModule(FqX, [1, QQ(1)]) - Traceback (most recent call last): - ... - ValueError: function ring base must coerce into base codomain - - The function ring must be an univariate polynomial ring whose - base is a finite field:: - - sage: DrinfeldModule(K, [z, 1, 1]) - Traceback (most recent call last): - ... - NotImplementedError: function ring must be a polynomial ring - - :: - - sage: FqXY. = FqX[] - sage: DrinfeldModule(FqXY, [z, 1, 1]) - Traceback (most recent call last): - ... - TypeError: function ring base must be a finite field - - If you already defined a category of Drinfeld modules, and you - create a Drinfeld module through this category, you must - ensure that the constant coefficient is the generator of the algebra - morphism that defines the category:: - - sage: cat = phi.category() - sage: cat.object([1, 1, K(1)]) - Traceback (most recent call last): - ... - ValueError: constant coefficient must be the generator of the morphism that defines the category - - .. NOTE:: + Images under the Drinfeld module are computed by calling the object:: - The reader may think that it is odd to build a Drinfeld module - without explicitly specifying the base. However, the base can be - deduced from the generator, and we omit the base in the input - of the class for conciseness. + sage: phi(X) # phi_X, the generator of the Drinfeld module + t^2 + t + z + sage: phi(X^3 + X + 1) # phi_(X^3 +X + 1) + t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 + sage: phi(1) # phi_1 + 1 .. RUBRIC:: The category of Drinfeld modules @@ -261,60 +198,57 @@ class DrinfeldModule(Parent, UniqueRepresentation): :class:`sage.categories.drinfeld_modules.DrinfeldModules`):: sage: phi.category() - Category of Drinfeld modules defined over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z - sage: phi.category() is psi.category() + Category of Drinfeld modules defined over Finite Field in z of size 3^12 over its base + sage: phi.category() is psi.category() # todo: not tested False sage: phi.category() is rho.category() True - This category holds crucial information, like the - function ring-characteristic of the base:: + One can use the category to directly create new objects:: - sage: char = phi.category().characteristic() + sage: cat = phi.category() + sage: cat.object([z, 0, 0, 1]) + Drinfeld module defined by X |--> t^3 + z over base Finite Field in z of size 3^12 over its base - As the output of :meth:`category` suggests, the base uniquely - determines the category. + .. RUBRIC:: The base ring of a Drinfeld module - .. RUBRIC:: Basics + The base ring of the Drinfeld module is retrieved using + :meth:`base_ring` or :meth:`base`:: - Images under the Drinfeld module are computed by calling the object:: + sage: phi.base_ring() + Finite Field in z of size 3^12 over its base - sage: phi(X) # phi_X - t^2 + t + z - sage: phi(X^3 + X + 1) # phi_(X^3 +X + 1) - t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 - sage: phi(1) # phi_1 - 1 + The base morphism is retrieved using :meth:`base_morphism`:: - This is useful to quickly retrieve the generator of the Drinfeld - module. Furthermore, a Drinfeld `\mathbb{F}_q[X]`-module can be seen - as an Ore polynomial with positive degree and constant coefficient - `\gamma(X)`, where `\gamma` is the base. This analogy is the - motivation for the following methods:: + sage: phi.base_morphism() + Ring morphism: + From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + To: Finite Field in z of size 3^12 over its base + Defn: X |--> z - sage: phi.coefficients() - [z, 1, 1] + Note that the base ring is *not* the field `K`. Rather, it is a ring + extension (see :class:`sage.rings.ring_extension.RingExtension`) + whose underlying ring is `K` and whose base is the base morphism:: - :: + sage: phi.base_ring() is K + False - sage: phi.coefficient(1) - 1 + .. RUBRIC:: Getters One can retrieve basic properties:: - sage: phi.base() + :: + + sage: phi.base_morphism() Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 + To: Finite Field in z of size 3^12 over its base Defn: X |--> z :: sage: phi.ore_polring() # K{t} - Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) + Ore Polynomial Ring in t over Finite Field in z of size 3^12 over its base twisted by Frob^2 :: @@ -338,14 +272,14 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.morphism() # The Drinfeld module as a morphism Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) + To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 over its base twisted by Frob^2 Defn: X |--> t^2 + t + z One can compute the rank and height:: sage: phi.rank() 2 - sage: phi.height() + sage: phi.height() # todo: not tested 1 As well as the j-invariant if the rank is two:: @@ -353,7 +287,21 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.j_invariant() # j-invariant 1 - .. RUBRIC:: Morphisms, isogenies + A Drinfeld `\mathbb{F}_q[X]`-module can be seen as an Ore + polynomial with positive degree and constant coefficient + `\gamma(X)`, where `\gamma` is the base morphism. This analogy is + the motivation for the following methods:: + + sage: phi.coefficients() + [z, 1, 1] + + :: + + sage: phi.coefficient(1) + 1 + + + .. RUBRIC:: Morphisms and isogenies A morphism of Drinfeld modules `\phi \to \psi` is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a` in @@ -432,10 +380,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z sage: psi = phi.velu(ore_pol) sage: psi - Drinfeld module defined by X |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z + Drinfeld module defined by X |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over base Finite Field in z of size 3^12 over its base sage: ore_pol in Hom(phi, psi) True sage: ore_pol * phi(X) == psi(X) * ore_pol @@ -466,10 +411,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: action = phi.action() sage: action - Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by X |--> t^2 + t + z over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Finite Field in z of size 3^12 - Defn: X |--> z + Action on Finite Field in z of size 3^12 over its base induced by Drinfeld module defined by X |--> t^2 + t + z over base Finite Field in z of size 3^12 over its base The action on elements is computed by calling the action object:: @@ -485,22 +427,105 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: Inverting the Drinfeld module - Given an Ore polynomial that equals `\phi_a` for some function ring - elelement `a`, one can retrieve `a` (as a morphism, a Drinfeld - module is injective, see [Gos1998]_, cor. 4.5.2.):: + The morphism that defines a Drinfeld module is injective. Given an + Ore polynomial that equals `\phi_a` for some function ring elelement + `a`, one can retrieve `a` (as a morphism, a Drinfeld module is + injective, see [Gos1998]_, cor. 4.5.2.):: sage: a = FqX.random_element() - sage: phi.invert(phi(a)) == a + sage: phi.invert(phi(a)) == a # todo: not tested True - TESTS:: + TESTS: + + The generator must have positive degree:: + + sage: Fq = GF(2) + sage: K. = Fq.extension(2) + sage: FqX. = Fq[] + sage: DrinfeldModule(FqX, [K(1)]) + Traceback (most recent call last): + ... + ValueError: generator must have positive degree + + The constant coefficient must be nonzero:: + + sage: Fq = GF(2) + sage: K. = Fq.extension(2) + sage: FqX. = Fq[] + sage: DrinfeldModule(FqX, [K(0), K(1)]) + Traceback (most recent call last): + ... + ValueError: constant coefficient must be nonzero + + The coefficients of the generator must lie in an + `\mathbb{F}_q[X]`-field, where `\mathbb{F}_q[X]` is the function + ring of the Drinfeld module:: + + sage: Fq = GF(2) + sage: K. = Fq.extension(2) + sage: FqX. = Fq[] + sage: DrinfeldModule(FqX, [z, QQ(1)]) + Traceback (most recent call last): + ... + ValueError: function ring base must coerce into base ring + + :: + + sage: Fq = GF(2) + sage: K. = Fq.extension(2) + sage: FqX. = Fq[] + sage: DrinfeldModule(FqX, [1, QQ(1)]) + Traceback (most recent call last): + ... + ValueError: function ring base must coerce into base ring + + The function ring must be an univariate polynomial ring whose + base is a finite field:: + + sage: Fq = GF(2) + sage: K. = Fq.extension(2) + sage: FqX. = Fq[] + sage: DrinfeldModule(K, [z, 1, 1]) + Traceback (most recent call last): + ... + NotImplementedError: function ring must be a polynomial ring + + :: + + sage: Fq = GF(2) + sage: K. = Fq.extension(2) + sage: FqX. = Fq[] + sage: FqXY. = FqX[] + sage: DrinfeldModule(FqXY, [z, 1, 1]) + Traceback (most recent call last): + ... + TypeError: function ring base must be a finite field + + If you already defined a category of Drinfeld modules, and you + create a Drinfeld module through this category, you must ensure that + the constant coefficient is that of the category:: + + sage: Fq = GF(2) + sage: K. = Fq.extension(2) + sage: FqX. = Fq[] + sage: phi = DrinfeldModule(FqX, [z, 1]) + sage: phi.category().object([1, 1, K(1)]) + Traceback (most recent call last): + ... + ValueError: constant coefficient must equal that of the category + + + :: sage: Fq = K = GF(2) sage: FqX. = Fq[] sage: phi = DrinfeldModule(FqX, [1, 1]) Traceback (most recent call last): ... - ValueError: function ring base must coerce into base codomain + ValueError: function ring base must coerce into base ring + + :: sage: Fq = K = GF(2) sage: FqX. = Fq[] @@ -510,45 +535,44 @@ class DrinfeldModule(Parent, UniqueRepresentation): Test that the base morphism is correct:: - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K = Frac(Fq) - sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(1)]) - sage: phi.base().codomain() is K - True - - :: + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K = Frac(Fq) + sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(1)]) + sage: phi.base().codomain() is K # todo: not tested + True - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: k = Frac(Fq) - sage: kT. = k[] - sage: K = k.extension(T^3 + T + 1) - sage: phi = DrinfeldModule(FqX, [Fq.gen(), K.gen()]) - sage: phi.base().codomain() is K - True + :: - In particular, note that the field `K` may not be the smallest field - of definition of the coefficients:: + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: k = Frac(Fq) + sage: kT. = k[] + sage: K = k.extension(T^3 + T + 1) + sage: phi = DrinfeldModule(FqX, [Fq.gen(), K.gen()]) + sage: phi.base().codomain() is K # todo: not tested + True - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: k = Frac(Fq) - sage: kT. = k[] - sage: K = k.extension(T^3 + T + 1) - sage: phi = DrinfeldModule(FqX, [K(k.gen()), 1]) - sage: phi.base().codomain() is K - True + In particular, note that the field `K` may not be the smallest field + of definition of the coefficients:: - :: + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: k = Frac(Fq) + sage: kT. = k[] + sage: K = k.extension(T^3 + T + 1) + sage: phi = DrinfeldModule(FqX, [K(k.gen()), 1]) + sage: phi.base().codomain() is K # todo: not tested + True - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(Fq.gen())]) - sage: phi.base().codomain() is K - True + :: + sage: Fq = GF(25) + sage: FqX. = Fq[] + sage: K = Fq.extension(2) + sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(Fq.gen())]) + sage: phi.base().codomain() is K # todo: not tested + True """ @staticmethod @@ -582,8 +606,8 @@ def __classcall_private__(cls, function_ring, gen, name='t'): :: sage: K = Frac(FqX) - sage: phi = DrinfeldModule(FqX, [K(X), 1]) - sage: isinstance(psi, FiniteDrinfeldModule) + sage: phi = DrinfeldModule(FqX, [K(X), 1]) # todo: not tested + sage: isinstance(psi, FiniteDrinfeldModule) # todo: not tested False """ @@ -605,26 +629,33 @@ def __classcall_private__(cls, function_ring, gen, name='t'): # `gen` is an Ore polynomial: if isinstance(gen, OrePolynomial): ore_polring = gen.parent() - ore_polring_base = ore_polring.base_ring() + # Base ring without morphism structure: + base_ring_noext = ore_polring.base() name = ore_polring.variable_name() # `gen` is a list of coefficients (function_ring = Fq[X]): elif isinstance(gen, (list, tuple)): ore_polring = None - ore_polring_base = Sequence(gen).universe() + # Base ring without morphism structure: + base_ring_noext = Sequence(gen).universe() else: raise TypeError('generator must be list of coefficients or Ore ' 'polynomial') + # Constant coefficient must be nonzero: + if gen[0].is_zero(): + raise ValueError('constant coefficient must be nonzero') # The coefficients are in a base ring that has coercion from Fq: - if not (hasattr(ore_polring_base, 'has_coerce_map_from') - and ore_polring_base.has_coerce_map_from( - function_ring.base_ring())): - raise ValueError('function ring base must coerce into base codomain') + if not (hasattr(base_ring_noext, 'has_coerce_map_from') and + base_ring_noext.has_coerce_map_from(function_ring.base_ring())): + raise ValueError('function ring base must coerce into base ring') - # Build the morphism that defines the category - base = function_ring.hom([ore_polring_base(gen[0])]) + # Build the category + if isinstance(base_ring_noext, RingExtension_generic): + base_ring = base_ring_noext + else: + base_morphism = Hom(function_ring, base_ring_noext)(gen[0]) + base_ring = base_ring_noext.over(base_morphism) - # Other checks in the category definition - category = DrinfeldModules(base, name=name) + category = DrinfeldModules(base_ring, name=name) # Check gen as Ore polynomial ore_polring = category.ore_polring() # Sanity cast @@ -633,7 +664,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): raise ValueError('generator must have positive degree') # Instantiate the appropriate class - if ore_polring_base.is_finite(): + if base_ring.is_finite(): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return FiniteDrinfeldModule(gen, category) return cls.__classcall__(cls, gen, category) @@ -682,37 +713,6 @@ def __init__(self, gen, category): self._Fq = self._function_ring.base_ring() # Must be last super().__init__(base=self._base, category=category) - def base_ring(self): - r""" - Raise exception ``AttributeError``. - - The base of a Drinfeld module is a ring morphism, not a ring. - - This method is implemented in ``CategoryObject``, of which Parent - inherits. It returns the base of the category. I overloaded it - to avoid confusion. - - EXAMPLES:: - - sage: Fq = GF(25) - sage: FqX. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.base_ring() - Traceback (most recent call last): - ... - AttributeError: the base of a Drinfeld module is a morphism - """ - # FIXME - # This method depends on the sole category of the Drinfeld - # module. It should therefore be implemented in the - # `ParentMethods` bloc of `DrinfeldModule`. This is not possible - # as the method `base_ring` from `CategoryObject` --- of which - # `Parent` and so `DrinfeldModule inherit --- is called instead. - # Any better way would be welcome. - raise AttributeError('the base of a Drinfeld module is a morphism') - def __call__(self, a): r""" Return the image of input ``a`` by the morphism that defines the @@ -828,12 +828,7 @@ def _latex_(self): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: latex(phi) - \text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }base{ }}\begin{array}{l} - \text{\texttt{Ring{ }morphism:}}\\ - \text{\texttt{{ }{ }From:{ }Univariate{ }Polynomial{ }Ring{ }in{ }X{ }over{ }Finite{ }Field{ }in{ }z2{ }of{ }size{ }5{\char`\^}2}}\\ - \text{\texttt{{ }{ }To:{ }{ }{ }Finite{ }Field{ }in{ }z12{ }of{ }size{ }5{\char`\^}12}}\\ - \text{\texttt{{ }{ }Defn:{ }X{ }|{-}{-}>{ }2*z12{\char`\^}11{ }+{ }2*z12{\char`\^}10{ }+{ }z12{\char`\^}9{ }+{ }3*z12{\char`\^}8{ }+{ }z12{\char`\^}7{ }+{ }2*z12{\char`\^}5{ }+{ }2*z12{\char`\^}4{ }+{ }3*z12{\char`\^}3{ }+{ }z12{\char`\^}2{ }+{ }2*z12}} - \end{array} + \text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }base{ }}\Bold{F}_{5^{12}} """ return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ f'{latex(self._function_ring.gen())} '\ @@ -854,10 +849,7 @@ def _repr_(self): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: phi - Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base """ return f'Drinfeld module defined by {self._function_ring.gen()} ' \ f'|--> {self._gen} over base {self._base}' @@ -880,10 +872,7 @@ def action(self): sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: action = phi.action() sage: action - Action on Finite Field in z12 of size 5^12 induced by Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base The action on elements is computed as follows:: @@ -1023,14 +1012,14 @@ def height(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.height() == 1 + sage: phi.height() == 1 # todo: not tested True - sage: phi.is_ordinary() + sage: phi.is_ordinary() # todo: not tested True sage: L = Frac(FqX) - sage: phi = DrinfeldModule(FqX, [L(2), L(1)]) - sage: phi.height() + sage: phi = DrinfeldModule(FqX, [L(2), L(1)]) # todo: not tested + sage: phi.height() # todo: not tested Traceback (most recent call last): ... ValueError: height is defined for prime function field characteristic @@ -1039,9 +1028,9 @@ def height(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: phi.height() + sage: phi.height() # todo: not tested 2 - sage: phi.is_supersingular() + sage: phi.is_supersingular() # todo: not tested True """ @@ -1076,22 +1065,22 @@ def invert(self, ore_pol): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: a = FqX.random_element() - sage: phi.invert(phi(a)) == a + sage: phi.invert(phi(a)) == a # todo: not tested True - sage: phi.invert(phi(X)) == X + sage: phi.invert(phi(X)) == X # todo: not tested True - sage: phi.invert(phi(Fq.gen())) == Fq.gen() + sage: phi.invert(phi(Fq.gen())) == Fq.gen() # todo: not tested True When the input is not in the image of the Drinfeld module, an exception is raised:: sage: t = phi.ore_polring().gen() - sage: phi.invert(t + 1) + sage: phi.invert(t + 1) # todo: not tested Traceback (most recent call last): ... ValueError: input must be in the image of the Drinfeld module - sage: phi.invert(t^3 + t^2 + 1) + sage: phi.invert(t^3 + t^2 + 1) # todo: not tested Traceback (most recent call last): ... ValueError: input must be in the image of the Drinfeld module @@ -1106,19 +1095,19 @@ def invert(self, ore_pol): sage: a = FqX.random_element() sage: cat = phi.category() sage: phi_r1 = cat.random_object(1) - sage: phi_r1.invert(phi_r1(a)) == a + sage: phi_r1.invert(phi_r1(a)) == a # todo: not tested True sage: phi_r2 = cat.random_object(2) - sage: phi_r2.invert(phi_r2(a)) == a + sage: phi_r2.invert(phi_r2(a)) == a # todo: not tested True sage: phi_r3 = cat.random_object(3) - sage: phi_r3.invert(phi_r3(a)) == a + sage: phi_r3.invert(phi_r3(a)) == a # todo: not tested True sage: phi_r4 = cat.random_object(4) - sage: phi_r4.invert(phi_r4(a)) == a + sage: phi_r4.invert(phi_r4(a)) == a # todo: not tested True sage: phi_r5 = cat.random_object(5) - sage: phi_r5.invert(phi_r5(a)) == a + sage: phi_r5.invert(phi_r5(a)) == a # todo: not tested True """ deg = ore_pol.degree() @@ -1145,7 +1134,8 @@ def invert(self, ore_pol): if self(pre_image) == ore_pol: return pre_image else: - return None + raise ValueError('input must be in the image of the Drinfeld ' + 'module') def is_finite(self): r""" @@ -1163,8 +1153,8 @@ def is_finite(self): sage: phi.is_finite() True sage: L = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [L(2), L(1)]) - sage: psi.is_finite() + sage: psi = DrinfeldModule(FqX, [L(2), L(1)]) # todo: not tested + sage: psi.is_finite() # todo: not tested False """ from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule @@ -1229,7 +1219,7 @@ def morphism(self): sage: phi.morphism() Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + To: Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 Defn: X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: from sage.rings.morphism import RingHomomorphism sage: isinstance(phi.morphism(), RingHomomorphism) @@ -1329,10 +1319,7 @@ def velu(self, isog): sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) sage: psi - Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base sage: isog in Hom(phi, psi) True @@ -1340,7 +1327,7 @@ def velu(self, isog): sage: phi.velu(phi(X)) is phi True - sage: phi.velu(t^6) is phi + sage: phi.velu(t^6) is phi # todo: not tested True The following inputs do not define isogenies, and the method diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 0991a343b35..46f6be20bd3 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -47,6 +47,11 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [z6, 0, 5]) + sage: phi + Drinfeld module defined by X |--> 5*t^2 + z6 over base Finite Field in z6 of size 7^6 over its base + + :: + sage: isinstance(phi, DrinfeldModule) True sage: from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule @@ -68,8 +73,8 @@ class FiniteDrinfeldModule(DrinfeldModule): First of all, it is easy to create the Frobenius endomorphism:: - sage: frobenius_endomorphism = phi.frobenius_endomorphism() - sage: frobenius_endomorphism + sage: frobenius_endomorphism = phi.frobenius_endomorphism() # todo: not tested + sage: frobenius_endomorphism # todo: not tested Drinfeld Module morphism: From (gen): 5*t^2 + z6 To (gen): 5*t^2 + z6 @@ -77,27 +82,27 @@ class FiniteDrinfeldModule(DrinfeldModule): Its characteristic polynomial can be computed:: - sage: chi = phi.frobenius_charpoly() - sage: chi + sage: chi = phi.frobenius_charpoly() # todo: not tested + sage: chi # todo: not tested T^2 + (X + 2*z3^2 + 2*z3 + 1)*T + 2*X^2 + (z3^2 + z3 + 4)*X + 2*z3 - sage: frob_pol = frobenius_endomorphism.ore_polynomial() - sage: chi(frob_pol, phi(X)) + sage: frob_pol = frobenius_endomorphism.ore_polynomial() # todo: not tested + sage: chi(frob_pol, phi(X)) # todo: not tested 0 This makes it possible to compute the Frobenius trace and norm:: - sage: phi.frobenius_trace() + sage: phi.frobenius_trace() # todo: not tested 6*X + 5*z3^2 + 5*z3 + 6 - sage: phi.frobenius_trace() == -chi[1] + sage: phi.frobenius_trace() == -chi[1] # todo: not tested True - sage: phi.frobenius_norm() + sage: phi.frobenius_norm() # todo: not tested 2*X^2 + (z3^2 + z3 + 4)*X + 2*z3 And to decide if a Drinfeld module is ordinary or supersingular:: - sage: phi.is_ordinary() + sage: phi.is_ordinary() # todo: not tested True - sage: phi.is_supersingular() + sage: phi.is_supersingular() # todo: not tested False """ @@ -155,19 +160,19 @@ def frobenius_endomorphism(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: phi.frobenius_endomorphism() + sage: phi.frobenius_endomorphism() # todo: not tested Drinfeld Module morphism: From (gen): z6*t^2 + 1 To (gen): z6*t^2 + 1 Defn: t^2 sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism - sage: isinstance(phi.frobenius_endomorphism(), DrinfeldModuleMorphism) + sage: isinstance(phi.frobenius_endomorphism(), DrinfeldModuleMorphism) # todo: not tested True """ t = self.ore_polring().gen() - L = self._base.codomain() - Fq = self._function_ring.base_ring() - deg = L.over(Fq).degree(Fq) + Fq = self._function_ring.base() + #FIXME + deg = self._base.over(Fq).degree(Fq) return self._Hom_(self, category=self.category())(t**deg) def frobenius_charpoly(self, var='T'): @@ -206,31 +211,31 @@ def frobenius_charpoly(self, var='T'): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: chi = phi.frobenius_charpoly() - sage: chi + sage: chi = phi.frobenius_charpoly() # todo: not tested + sage: chi # todo: not tested T^2 + ((3*z3^2 + z3 + 4)*X + 4*z3^2 + 6*z3 + 3)*T + (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 :: - sage: frob_pol = phi.frobenius_endomorphism().ore_polynomial() - sage: chi(frob_pol, phi(X)) + sage: frob_pol = phi.frobenius_endomorphism().ore_polynomial() # todo: not tested + sage: chi(frob_pol, phi(X)) # todo: not tested 0 :: - sage: A = phi.frobenius_trace() - sage: A + sage: A = phi.frobenius_trace() # todo: not tested + sage: A # todo: not tested (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 - sage: B = phi.frobenius_norm() - sage: B + sage: B = phi.frobenius_norm() # todo: not tested + sage: B # todo: not tested (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 :: sage: n = 2 # Degree over Fq of the base codomain - sage: A.degree() <= n/2 + sage: A.degree() <= n/2 # todo: not tested True - sage: B.degree() == n + sage: B.degree() == n # todo: not tested True ALGORITHM: @@ -272,19 +277,19 @@ def frobenius_norm(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: B = phi.frobenius_norm() - sage: B + sage: B = phi.frobenius_norm() # todo: not tested + sage: B # todo: not tested (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 :: sage: n = 2 # Degree over Fq of the base codomain - sage: B.degree() == n + sage: B.degree() == n # todo: not tested True :: - sage: B == phi.frobenius_charpoly()[0] + sage: B == phi.frobenius_charpoly()[0] # todo: not tested True ALGORITHM: @@ -339,19 +344,19 @@ def frobenius_trace(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: A = phi.frobenius_trace() - sage: A + sage: A = phi.frobenius_trace() # todo: not tested + sage: A # todo: not tested (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 :: sage: n = 2 # Degree over Fq of the base codomain - sage: A.degree() <= n/2 + sage: A.degree() <= n/2 # todo: not tested True :: - sage: A == -phi.frobenius_charpoly()[1] + sage: A == -phi.frobenius_charpoly()[1] # todo: not tested True """ self._check_rank_two() @@ -384,10 +389,10 @@ def is_ordinary(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: phi.is_ordinary() + sage: phi.is_ordinary() # todo: not tested False - sage: phi_p = phi(phi.characteristic()) - sage: phi_p # Purely inseparable + sage: phi_p = phi(phi.characteristic()) # todo: not tested + sage: phi_p # Purely inseparable # todo: not tested z6*t^2 ALGORITHM: @@ -420,9 +425,9 @@ def is_supersingular(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: phi.is_supersingular() + sage: phi.is_supersingular() # todo: not tested True - sage: phi(phi.characteristic()) # Purely inseparable + sage: phi(phi.characteristic()) # Purely inseparable # todo: not tested z6*t^2 ALGORITHM: diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 690d35653b8..71604593ab2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -64,8 +64,8 @@ class DrinfeldModuleHomset(Homset): The domain and codomain must have the same Drinfeld modules category:: - sage: rho = DrinfeldModule(FqX, [Frac(FqX)(X), 1]) - sage: Hom(phi, rho) + sage: rho = DrinfeldModule(FqX, [Frac(FqX)(X), 1]) # todo: not tested + sage: Hom(phi, rho) # todo: not tested Traceback (most recent call last): ... ValueError: Drinfeld modules must be in the same category @@ -262,8 +262,8 @@ def __contains__(self, x): sage: identity_morphism = end(1) sage: identity_morphism in hom False - sage: frobenius_endomorphism = phi.frobenius_endomorphism() - sage: frobenius_endomorphism in hom + sage: frobenius_endomorphism = phi.frobenius_endomorphism() # todo: not tested + sage: frobenius_endomorphism in hom # todo: not tested False """ try: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 9f36cdeca13..a752b3ddb1c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -64,20 +64,14 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, One can get basic data on the morphism:: sage: morphism.domain() - Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base sage: morphism.domain() is phi True :: sage: morphism.codomain() - Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 - To: Finite Field in z12 of size 5^12 - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base sage: morphism.codomain() is psi True @@ -106,7 +100,7 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: morphism.parent() == Hom(phi, psi) True - sage: phi.frobenius_endomorphism().parent() == End(phi) + sage: phi.frobenius_endomorphism().parent() == End(phi) # todo: not tested True sage: End(phi)(0).parent() == End(phi) True @@ -321,8 +315,8 @@ def is_isogeny(self): :: - sage: frobenius_endomorphism = phi.frobenius_endomorphism() - sage: frobenius_endomorphism.is_isogeny() + sage: frobenius_endomorphism = phi.frobenius_endomorphism() # todo: not tested + sage: frobenius_endomorphism.is_isogeny() # todo: not tested True """ return not self.is_zero() @@ -357,8 +351,8 @@ def is_isomorphism(self): :: - sage: frobenius_endomorphism = phi.frobenius_endomorphism() - sage: frobenius_endomorphism.is_isomorphism() + sage: frobenius_endomorphism = phi.frobenius_endomorphism() # todo: not tested + sage: frobenius_endomorphism.is_isomorphism() # todo: not tested False """ return self.is_isogeny() and self._ore_polynomial.degree() == 0 From 1ef141292c39c14b1f6c4e15f119b14241e39ad5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 6 Feb 2023 11:01:17 +0100 Subject: [PATCH 154/197] Remove `# todo: not tested` comments --- .../drinfeld_modules/drinfeld_module.py | 68 +++++++++--------- .../finite_drinfeld_module.py | 72 +++++++++---------- .../function_field/drinfeld_modules/homset.py | 8 +-- .../drinfeld_modules/morphism.py | 10 +-- 4 files changed, 79 insertions(+), 79 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index d82632c6db2..22d0f3c399f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -97,8 +97,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: Fq = GF(49) sage: FqX. = Fq[] sage: K. = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [z, X+1]) # todo: not tested - sage: psi # todo: not tested + sage: psi = DrinfeldModule(FqX, [z, X+1]) + sage: psi .. NOTE:: @@ -155,9 +155,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): The above Drinfeld module is finite; it can also be infinite:: - sage: L = Frac(FqX) # todo: not tested - sage: psi = DrinfeldModule(FqX, [L(X), 1, X^3 + X + 1]) # todo: not tested - sage: psi # todo: not tested + sage: L = Frac(FqX) + sage: psi = DrinfeldModule(FqX, [L(X), 1, X^3 + X + 1]) + sage: psi Drinfeld module defined by X |--> (X^3 + X + 1)*t^2 + t + X over base Ring morphism: From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 @@ -167,7 +167,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.is_finite() True - sage: psi.is_finite() # todo: not tested + sage: psi.is_finite() False In those examples, we used a list of coefficients (``[z, 1, 1]``) to @@ -199,7 +199,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.category() Category of Drinfeld modules defined over Finite Field in z of size 3^12 over its base - sage: phi.category() is psi.category() # todo: not tested + sage: phi.category() is psi.category() False sage: phi.category() is rho.category() True @@ -279,7 +279,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.rank() 2 - sage: phi.height() # todo: not tested + sage: phi.height() 1 As well as the j-invariant if the rank is two:: @@ -433,7 +433,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): injective, see [Gos1998]_, cor. 4.5.2.):: sage: a = FqX.random_element() - sage: phi.invert(phi(a)) == a # todo: not tested + sage: phi.invert(phi(a)) == a True TESTS: @@ -539,7 +539,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: FqX. = Fq[] sage: K = Frac(Fq) sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(1)]) - sage: phi.base().codomain() is K # todo: not tested + sage: phi.base().codomain() is K True :: @@ -550,7 +550,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: kT. = k[] sage: K = k.extension(T^3 + T + 1) sage: phi = DrinfeldModule(FqX, [Fq.gen(), K.gen()]) - sage: phi.base().codomain() is K # todo: not tested + sage: phi.base().codomain() is K True In particular, note that the field `K` may not be the smallest field @@ -562,7 +562,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: kT. = k[] sage: K = k.extension(T^3 + T + 1) sage: phi = DrinfeldModule(FqX, [K(k.gen()), 1]) - sage: phi.base().codomain() is K # todo: not tested + sage: phi.base().codomain() is K True :: @@ -571,7 +571,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: FqX. = Fq[] sage: K = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(Fq.gen())]) - sage: phi.base().codomain() is K # todo: not tested + sage: phi.base().codomain() is K True """ @@ -606,8 +606,8 @@ def __classcall_private__(cls, function_ring, gen, name='t'): :: sage: K = Frac(FqX) - sage: phi = DrinfeldModule(FqX, [K(X), 1]) # todo: not tested - sage: isinstance(psi, FiniteDrinfeldModule) # todo: not tested + sage: phi = DrinfeldModule(FqX, [K(X), 1]) + sage: isinstance(psi, FiniteDrinfeldModule) False """ @@ -1012,14 +1012,14 @@ def height(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.height() == 1 # todo: not tested + sage: phi.height() == 1 True - sage: phi.is_ordinary() # todo: not tested + sage: phi.is_ordinary() True sage: L = Frac(FqX) - sage: phi = DrinfeldModule(FqX, [L(2), L(1)]) # todo: not tested - sage: phi.height() # todo: not tested + sage: phi = DrinfeldModule(FqX, [L(2), L(1)]) + sage: phi.height() Traceback (most recent call last): ... ValueError: height is defined for prime function field characteristic @@ -1028,9 +1028,9 @@ def height(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: phi.height() # todo: not tested + sage: phi.height() 2 - sage: phi.is_supersingular() # todo: not tested + sage: phi.is_supersingular() True """ @@ -1065,22 +1065,22 @@ def invert(self, ore_pol): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) sage: a = FqX.random_element() - sage: phi.invert(phi(a)) == a # todo: not tested + sage: phi.invert(phi(a)) == a True - sage: phi.invert(phi(X)) == X # todo: not tested + sage: phi.invert(phi(X)) == X True - sage: phi.invert(phi(Fq.gen())) == Fq.gen() # todo: not tested + sage: phi.invert(phi(Fq.gen())) == Fq.gen() True When the input is not in the image of the Drinfeld module, an exception is raised:: sage: t = phi.ore_polring().gen() - sage: phi.invert(t + 1) # todo: not tested + sage: phi.invert(t + 1) Traceback (most recent call last): ... ValueError: input must be in the image of the Drinfeld module - sage: phi.invert(t^3 + t^2 + 1) # todo: not tested + sage: phi.invert(t^3 + t^2 + 1) Traceback (most recent call last): ... ValueError: input must be in the image of the Drinfeld module @@ -1095,19 +1095,19 @@ def invert(self, ore_pol): sage: a = FqX.random_element() sage: cat = phi.category() sage: phi_r1 = cat.random_object(1) - sage: phi_r1.invert(phi_r1(a)) == a # todo: not tested + sage: phi_r1.invert(phi_r1(a)) == a True sage: phi_r2 = cat.random_object(2) - sage: phi_r2.invert(phi_r2(a)) == a # todo: not tested + sage: phi_r2.invert(phi_r2(a)) == a True sage: phi_r3 = cat.random_object(3) - sage: phi_r3.invert(phi_r3(a)) == a # todo: not tested + sage: phi_r3.invert(phi_r3(a)) == a True sage: phi_r4 = cat.random_object(4) - sage: phi_r4.invert(phi_r4(a)) == a # todo: not tested + sage: phi_r4.invert(phi_r4(a)) == a True sage: phi_r5 = cat.random_object(5) - sage: phi_r5.invert(phi_r5(a)) == a # todo: not tested + sage: phi_r5.invert(phi_r5(a)) == a True """ deg = ore_pol.degree() @@ -1153,8 +1153,8 @@ def is_finite(self): sage: phi.is_finite() True sage: L = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [L(2), L(1)]) # todo: not tested - sage: psi.is_finite() # todo: not tested + sage: psi = DrinfeldModule(FqX, [L(2), L(1)]) + sage: psi.is_finite() False """ from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule @@ -1327,7 +1327,7 @@ def velu(self, isog): sage: phi.velu(phi(X)) is phi True - sage: phi.velu(t^6) is phi # todo: not tested + sage: phi.velu(t^6) is phi True The following inputs do not define isogenies, and the method diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 46f6be20bd3..89fc6744209 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -73,8 +73,8 @@ class FiniteDrinfeldModule(DrinfeldModule): First of all, it is easy to create the Frobenius endomorphism:: - sage: frobenius_endomorphism = phi.frobenius_endomorphism() # todo: not tested - sage: frobenius_endomorphism # todo: not tested + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism Drinfeld Module morphism: From (gen): 5*t^2 + z6 To (gen): 5*t^2 + z6 @@ -82,27 +82,27 @@ class FiniteDrinfeldModule(DrinfeldModule): Its characteristic polynomial can be computed:: - sage: chi = phi.frobenius_charpoly() # todo: not tested - sage: chi # todo: not tested + sage: chi = phi.frobenius_charpoly() + sage: chi T^2 + (X + 2*z3^2 + 2*z3 + 1)*T + 2*X^2 + (z3^2 + z3 + 4)*X + 2*z3 - sage: frob_pol = frobenius_endomorphism.ore_polynomial() # todo: not tested - sage: chi(frob_pol, phi(X)) # todo: not tested + sage: frob_pol = frobenius_endomorphism.ore_polynomial() + sage: chi(frob_pol, phi(X)) 0 This makes it possible to compute the Frobenius trace and norm:: - sage: phi.frobenius_trace() # todo: not tested + sage: phi.frobenius_trace() 6*X + 5*z3^2 + 5*z3 + 6 - sage: phi.frobenius_trace() == -chi[1] # todo: not tested + sage: phi.frobenius_trace() == -chi[1] True - sage: phi.frobenius_norm() # todo: not tested + sage: phi.frobenius_norm() 2*X^2 + (z3^2 + z3 + 4)*X + 2*z3 And to decide if a Drinfeld module is ordinary or supersingular:: - sage: phi.is_ordinary() # todo: not tested + sage: phi.is_ordinary() True - sage: phi.is_supersingular() # todo: not tested + sage: phi.is_supersingular() False """ @@ -160,13 +160,13 @@ def frobenius_endomorphism(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: phi.frobenius_endomorphism() # todo: not tested + sage: phi.frobenius_endomorphism() Drinfeld Module morphism: From (gen): z6*t^2 + 1 To (gen): z6*t^2 + 1 Defn: t^2 sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism - sage: isinstance(phi.frobenius_endomorphism(), DrinfeldModuleMorphism) # todo: not tested + sage: isinstance(phi.frobenius_endomorphism(), DrinfeldModuleMorphism) True """ t = self.ore_polring().gen() @@ -211,31 +211,31 @@ def frobenius_charpoly(self, var='T'): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: chi = phi.frobenius_charpoly() # todo: not tested - sage: chi # todo: not tested + sage: chi = phi.frobenius_charpoly() + sage: chi T^2 + ((3*z3^2 + z3 + 4)*X + 4*z3^2 + 6*z3 + 3)*T + (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 :: - sage: frob_pol = phi.frobenius_endomorphism().ore_polynomial() # todo: not tested - sage: chi(frob_pol, phi(X)) # todo: not tested + sage: frob_pol = phi.frobenius_endomorphism().ore_polynomial() + sage: chi(frob_pol, phi(X)) 0 :: - sage: A = phi.frobenius_trace() # todo: not tested - sage: A # todo: not tested + sage: A = phi.frobenius_trace() + sage: A (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 - sage: B = phi.frobenius_norm() # todo: not tested - sage: B # todo: not tested + sage: B = phi.frobenius_norm() + sage: B (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 :: sage: n = 2 # Degree over Fq of the base codomain - sage: A.degree() <= n/2 # todo: not tested + sage: A.degree() <= n/2 True - sage: B.degree() == n # todo: not tested + sage: B.degree() == n True ALGORITHM: @@ -277,19 +277,19 @@ def frobenius_norm(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: B = phi.frobenius_norm() # todo: not tested - sage: B # todo: not tested + sage: B = phi.frobenius_norm() + sage: B (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 :: sage: n = 2 # Degree over Fq of the base codomain - sage: B.degree() == n # todo: not tested + sage: B.degree() == n True :: - sage: B == phi.frobenius_charpoly()[0] # todo: not tested + sage: B == phi.frobenius_charpoly()[0] True ALGORITHM: @@ -344,19 +344,19 @@ def frobenius_trace(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: A = phi.frobenius_trace() # todo: not tested - sage: A # todo: not tested + sage: A = phi.frobenius_trace() + sage: A (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 :: sage: n = 2 # Degree over Fq of the base codomain - sage: A.degree() <= n/2 # todo: not tested + sage: A.degree() <= n/2 True :: - sage: A == -phi.frobenius_charpoly()[1] # todo: not tested + sage: A == -phi.frobenius_charpoly()[1] True """ self._check_rank_two() @@ -389,10 +389,10 @@ def is_ordinary(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: phi.is_ordinary() # todo: not tested + sage: phi.is_ordinary() False - sage: phi_p = phi(phi.characteristic()) # todo: not tested - sage: phi_p # Purely inseparable # todo: not tested + sage: phi_p = phi(phi.characteristic()) + sage: phi_p # Purely inseparable z6*t^2 ALGORITHM: @@ -425,9 +425,9 @@ def is_supersingular(self): sage: FqX. = Fq[] sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(FqX, [1, 0, z6]) - sage: phi.is_supersingular() # todo: not tested + sage: phi.is_supersingular() True - sage: phi(phi.characteristic()) # Purely inseparable # todo: not tested + sage: phi(phi.characteristic()) # Purely inseparable z6*t^2 ALGORITHM: diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 71604593ab2..690d35653b8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -64,8 +64,8 @@ class DrinfeldModuleHomset(Homset): The domain and codomain must have the same Drinfeld modules category:: - sage: rho = DrinfeldModule(FqX, [Frac(FqX)(X), 1]) # todo: not tested - sage: Hom(phi, rho) # todo: not tested + sage: rho = DrinfeldModule(FqX, [Frac(FqX)(X), 1]) + sage: Hom(phi, rho) Traceback (most recent call last): ... ValueError: Drinfeld modules must be in the same category @@ -262,8 +262,8 @@ def __contains__(self, x): sage: identity_morphism = end(1) sage: identity_morphism in hom False - sage: frobenius_endomorphism = phi.frobenius_endomorphism() # todo: not tested - sage: frobenius_endomorphism in hom # todo: not tested + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism in hom False """ try: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index a752b3ddb1c..6383a9b2aad 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -100,7 +100,7 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: morphism.parent() == Hom(phi, psi) True - sage: phi.frobenius_endomorphism().parent() == End(phi) # todo: not tested + sage: phi.frobenius_endomorphism().parent() == End(phi) True sage: End(phi)(0).parent() == End(phi) True @@ -315,8 +315,8 @@ def is_isogeny(self): :: - sage: frobenius_endomorphism = phi.frobenius_endomorphism() # todo: not tested - sage: frobenius_endomorphism.is_isogeny() # todo: not tested + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism.is_isogeny() True """ return not self.is_zero() @@ -351,8 +351,8 @@ def is_isomorphism(self): :: - sage: frobenius_endomorphism = phi.frobenius_endomorphism() # todo: not tested - sage: frobenius_endomorphism.is_isomorphism() # todo: not tested + sage: frobenius_endomorphism = phi.frobenius_endomorphism() + sage: frobenius_endomorphism.is_isomorphism() False """ return self.is_isogeny() and self._ore_polynomial.degree() == 0 From 050c2f65c74a85dc74f361d755de8066699b51de Mon Sep 17 00:00:00 2001 From: Kryzar Date: Mon, 6 Feb 2023 14:57:36 +0100 Subject: [PATCH 155/197] (minor) Typo --- src/sage/categories/drinfeld_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 5f78eab3a3f..0d364cf5b51 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -49,7 +49,7 @@ class DrinfeldModules(Category_over_base_ring): The monic polynomial that generates the kernel of the base morphism is called the `\mathbb{F}_q[X]`-characteristic of the - `\mathbb{F}_q[X]`-field `K`. It cal also be referred to as the + `\mathbb{F}_q[X]`-field `K`. It can also be referred to as the function-field characteristic of `K`. We say that `\mathbb{F}_q[X]` is the function ring of the category; From e24c36681ae0d4630b5dbebe395752175d4fb24a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Mon, 6 Feb 2023 15:27:46 +0100 Subject: [PATCH 156/197] Change X to T --- src/sage/categories/drinfeld_modules.py | 194 +++++----- .../function_field/drinfeld_modules/action.py | 40 +- .../drinfeld_modules/drinfeld_module.py | 352 +++++++++--------- .../finite_drinfeld_module.py | 98 ++--- .../function_field/drinfeld_modules/homset.py | 40 +- .../drinfeld_modules/morphism.py | 62 +-- 6 files changed, 393 insertions(+), 393 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 0d364cf5b51..142300e0971 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -33,28 +33,28 @@ class DrinfeldModules(Category_over_base_ring): This class represents the category of Drinfeld modules on a given base. - Let `\mathbb{F}_q[X]` be a polynomial ring with coefficients in a + Let `\mathbb{F}_q[T]` be a polynomial ring with coefficients in a finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring - morphism `\gamma: \mathbb{F}_q[X] \to K`. We say that the field `K` - is an `\mathbb{F}_q[X]`-field, so that the *base of the category* is - defined as the `\mathbb{F}_q[X]`-field *K*. The base uniquely + morphism `\gamma: \mathbb{F}_q[T] \to K`. We say that the field `K` + is an `\mathbb{F}_q[T]`-field, so that the *base of the category* is + defined as the `\mathbb{F}_q[T]`-field *K*. The base uniquely defines the category, and we also refer to it as the *base ring* or *base field*. The *base morphism* is the morphism `\gamma: - \mathbb{F}_q[X] \to K`. + \mathbb{F}_q[T] \to K`. .. NOTE:: Equivalently, the base of the category could be defined as the - base morphism `\gamma: \mathbb{F}_q[X] \to K`. + base morphism `\gamma: \mathbb{F}_q[T] \to K`. The monic polynomial that generates the kernel of the base morphism - is called the `\mathbb{F}_q[X]`-characteristic of the - `\mathbb{F}_q[X]`-field `K`. It can also be referred to as the + is called the `\mathbb{F}_q[T]`-characteristic of the + `\mathbb{F}_q[T]`-field `K`. It can also be referred to as the function-field characteristic of `K`. - We say that `\mathbb{F}_q[X]` is the function ring of the category; + We say that `\mathbb{F}_q[T]` is the function ring of the category; `K\{\tau\}` is the Ore polynomial ring of the category. The constant - coefficient of the category is the image of `X` under the base + coefficient of the category is the image of `T` under the base morphism. INPUT: the base ring morphism @@ -66,10 +66,10 @@ class DrinfeldModules(Category_over_base_ring): Drinfeld module:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat Category of Drinfeld modules defined over Finite Field in z of size 11^4 over its base @@ -92,25 +92,25 @@ class DrinfeldModules(Category_over_base_ring): sage: cat.base_morphism() Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 + From: Univariate Polynomial Ring in T over Finite Field of size 11 To: Finite Field in z of size 11^4 over its base - Defn: X |--> z^3 + 7*z^2 + 6*z + 10 + Defn: T |--> z^3 + 7*z^2 + 6*z + 10 The so-called constant coefficient --- which is the same for all - Drinfeld modules in the category --- is simply the image of `X` by + Drinfeld modules in the category --- is simply the image of `T` by the base morphism:: sage: cat.constant_coefficient() z^3 + 7*z^2 + 6*z + 10 - sage: cat.base_morphism()(X) == cat.constant_coefficient() + sage: cat.base_morphism()(T) == cat.constant_coefficient() True Similarly, the function ring-characteristic of the category is - either `0` or the unique monic polynomial in `\mathbb{F}_q[X]` that + either `0` or the unique monic polynomial in `\mathbb{F}_q[T]` that generates the kernel of the base:: sage: cat.characteristic() - X^2 + 7*X + 2 + T^2 + 7*T + 2 sage: cat.base_morphism()(cat.characteristic()) 0 @@ -124,7 +124,7 @@ class DrinfeldModules(Category_over_base_ring): sage: cat.function_ring() is phi.function_ring() True sage: cat.function_ring() - Univariate Polynomial Ring in X over Finite Field of size 11 + Univariate Polynomial Ring in T over Finite Field of size 11 sage: cat.ore_polring() is phi.ore_polring() True sage: cat.ore_polring() @@ -137,7 +137,7 @@ class DrinfeldModules(Category_over_base_ring): sage: psi = cat.object([p_root, 1]) sage: psi - Drinfeld module defined by X |--> t + z^3 + 7*z^2 + 6*z + 10 over base Finite Field in z of size 11^4 over its base + Drinfeld module defined by T |--> t + z^3 + 7*z^2 + 6*z + 10 over base Finite Field in z of size 11^4 over its base sage: psi.category() is cat True @@ -154,7 +154,7 @@ class DrinfeldModules(Category_over_base_ring): sage: rho = cat.random_object(2) sage: rho # random - Drinfeld module defined by X |--> (7*z^3 + 7*z^2 + 10*z + 2)*t^2 + (9*z^3 + 5*z^2 + 2*z + 7)*t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + Drinfeld module defined by T |--> (7*z^3 + 7*z^2 + 10*z + 2)*t^2 + (9*z^3 + 5*z^2 + 2*z + 7)*t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 sage: rho.rank() == 2 True sage: rho.category() is cat @@ -163,10 +163,10 @@ class DrinfeldModules(Category_over_base_ring): TESTS:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: from sage.categories.drinfeld_modules import DrinfeldModules - sage: base = Hom(FqX, K)(0) + sage: base = Hom(A, K)(0) sage: cat = DrinfeldModules(base) Traceback (most recent call last): ... @@ -179,7 +179,7 @@ class DrinfeldModules(Category_over_base_ring): :: - sage: base = Hom(FqX, FqX)(1) + sage: base = Hom(A, A)(1) sage: cat = DrinfeldModules(base) Traceback (most recent call last): ... @@ -220,23 +220,23 @@ def __init__(self, base_field, name='t'): TESTS:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: ore_polring. = OrePolynomialRing(phi.base(), phi.base().frobenius_endomorphism()) sage: cat._ore_polring is ore_polring True sage: i = phi.base().coerce_map_from(K) - sage: base_morphism = Hom(FqX, K)(p_root) + sage: base_morphism = Hom(A, K)(p_root) sage: cat.base() == K.over(base_morphism) True sage: cat._base_morphism == i * base_morphism True - sage: cat._function_ring is FqX + sage: cat._function_ring is A True - sage: cat._constant_coefficient == base_morphism(X) + sage: cat._constant_coefficient == base_morphism(T) True sage: cat._characteristic(cat._constant_coefficient) 0 @@ -251,7 +251,7 @@ def __init__(self, base_field, name='t'): raise TypeError('input must be a field') self._base_field = base_field self._function_ring = base_morphism.domain() - # Check domain of base morphism is Fq[X] + # Check domain of base morphism is Fq[T] function_ring = self._function_ring if not isinstance(function_ring, PolynomialRing_general): raise NotImplementedError('function ring must be a polynomial ' @@ -262,8 +262,8 @@ def __init__(self, base_field, name='t'): raise TypeError('function ring base must be a finite field') # Shortcuts Fq = function_ring_base - FqX = function_ring - X = FqX.gen() + A = function_ring + T = A.gen() K = base_field # A ring extension # Build K{t} d = log(Fq.cardinality(), Fq.characteristic()) @@ -271,13 +271,13 @@ def __init__(self, base_field, name='t'): self._ore_polring = OrePolynomialRing(K, tau, names=name, polcast=False) # Create constant coefficient - self._constant_coefficient = base_morphism(X) + self._constant_coefficient = base_morphism(T) # Create characteristic self._characteristic = None if K.is_finite(): #FIXME: This minpoly is over Fp, not Fq - self._characteristic = FqX(K(base_morphism(X)).minpoly()) - elif FqX.is_subring(K): + self._characteristic = A(K(base_morphism(T)).minpoly()) + elif A.is_subring(K): self._characteristic = Integer(0) super().__init__(base=base_field) @@ -290,10 +290,10 @@ def _latex_(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: latex(cat) \text{Category{ }of{ }Drinfeld{ }modules{ }defined{ }over{ }\Bold{F}_{11^{4}} @@ -310,10 +310,10 @@ def _repr_(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat Category of Drinfeld modules defined over Finite Field in z of size 11^4 over its base @@ -329,10 +329,10 @@ def Homsets(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: from sage.categories.homsets import Homsets sage: cat.Homsets() is Homsets() @@ -349,10 +349,10 @@ def Endsets(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: from sage.categories.homsets import Homsets sage: cat.Endsets() is Homsets().Endsets() @@ -369,17 +369,17 @@ def base_morphism(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat.base_morphism() Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field of size 11 + From: Univariate Polynomial Ring in T over Finite Field of size 11 To: Finite Field in z of size 11^4 over its base - Defn: X |--> z^3 + 7*z^2 + 6*z + 10 - sage: cat.constant_coefficient() == cat.base_morphism()(X) + Defn: T |--> z^3 + 7*z^2 + 6*z + 10 + sage: cat.constant_coefficient() == cat.base_morphism()(T) True """ return self._base_morphism @@ -393,17 +393,17 @@ def characteristic(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat.characteristic() - X^2 + 7*X + 2 + T^2 + 7*T + 2 :: - sage: psi = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) # todo: not tested + sage: psi = DrinfeldModule(A, [Frac(A).gen(), 1]) # todo: not tested sage: psi.category().characteristic() # todo: not tested 0 """ @@ -421,14 +421,14 @@ def constant_coefficient(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat.constant_coefficient() z^3 + 7*z^2 + 6*z + 10 - sage: cat.constant_coefficient() == cat.base()(X) + sage: cat.constant_coefficient() == cat.base()(T) True """ return self._constant_coefficient @@ -442,14 +442,14 @@ def function_ring(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat.function_ring() - Univariate Polynomial Ring in X over Finite Field of size 11 - sage: cat.function_ring() is FqX + Univariate Polynomial Ring in T over Finite Field of size 11 + sage: cat.function_ring() is A True """ return self._function_ring @@ -467,14 +467,14 @@ def object(self, gen): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: psi = cat.object([p_root, 0, 1]) sage: psi - Drinfeld module defined by X |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over base Finite Field in z of size 11^4 over its base + Drinfeld module defined by T |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over base Finite Field in z of size 11^4 over its base sage: t = phi.ore_polring().gen() sage: cat.object(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi True @@ -482,8 +482,8 @@ def object(self, gen): from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule # If gen is not in the Ore polring, an exception is raised gen = self._ore_polring(gen) - X = self._function_ring.gen() - if gen[0] != self._base_morphism(X): + T = self._function_ring.gen() + if gen[0] != self._base_morphism(T): raise ValueError('constant coefficient must equal that of the ' \ 'category') return DrinfeldModule(self._function_ring, gen) @@ -497,10 +497,10 @@ def ore_polring(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat.ore_polring() Ore Polynomial Ring in t over Finite Field in z of size 11^4 over its base twisted by Frob @@ -521,13 +521,13 @@ def random_object(self, rank): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: psi = cat.random_object(3) # random - Drinfeld module defined by X |--> (6*z^3 + 4*z^2 + 10*z + 9)*t^3 + (4*z^3 + 8*z^2 + 8*z)*t^2 + (10*z^3 + 3*z^2 + 6*z)*t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + Drinfeld module defined by T |--> (6*z^3 + 4*z^2 + 10*z + 9)*t^3 + (4*z^3 + 8*z^2 + 8*z)*t^2 + (10*z^3 + 3*z^2 + 6*z)*t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 sage: psi.rank() == 3 True """ @@ -552,10 +552,10 @@ def super_categories(self): EXAMPLES:: sage: Fq = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(FqX, [p_root, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: cat.super_categories() [] @@ -573,18 +573,18 @@ def base(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.base() Finite Field in z12 of size 5^12 over its base The base can be infinite:: - sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) # todo: not tested + sage: sigma = DrinfeldModule(A, [Frac(A).gen(), 1]) # todo: not tested sage: sigma.base() # todo: not tested - Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 over its base + Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 over its base """ return self.category().base() @@ -597,19 +597,19 @@ def base_morphism(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.base_morphism() Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + From: Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 To: Finite Field in z12 of size 5^12 over its base - Defn: X |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Defn: T |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 The base codomain can be infinite:: - sage: sigma = DrinfeldModule(FqX, [Frac(FqX).gen(), 1]) # todo: not tested + sage: sigma = DrinfeldModule(A, [Frac(A).gen(), 1]) # todo: not tested sage: sigma.base_morphism() # todo: not tested """ return self.category().base_morphism() @@ -623,19 +623,19 @@ def characteristic(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.characteristic() # todo: not tested - X^2 + (4*z2 + 2)*X + 2 + T^2 + (4*z2 + 2)*T + 2 sage: phi.base_morphism()(phi.characteristic()) 0 :: - sage: L = Frac(FqX) # todo: not tested - sage: psi = DrinfeldModule(FqX, [L(1), 0, 0, L(1)]) # todo: not tested + sage: L = Frac(A) # todo: not tested + sage: psi = DrinfeldModule(A, [L(1), 0, 0, L(1)]) # todo: not tested sage: psi.characteristic() # todo: not tested 0 """ @@ -650,11 +650,11 @@ def function_ring(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.function_ring() is FqX + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.function_ring() is A True """ return self.category().function_ring() @@ -668,20 +668,20 @@ def constant_coefficient(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.constant_coefficient() == p_root True - Let `\mathbb{F}_q[X]` be the function ring, and let `\gamma` + Let `\mathbb{F}_q[T]` be the function ring, and let `\gamma` the base of the Drinfeld module. The constant coefficient - equals `\gamma(X)`:: + equals `\gamma(T)`:: sage: cat = phi.category() sage: base = cat.base() - sage: base(X) == phi.constant_coefficient() + sage: base(T) == phi.constant_coefficient() True Naturally, two Drinfeld modules in the same category have the @@ -690,7 +690,7 @@ def constant_coefficient(self): sage: t = phi.ore_polring().gen() sage: psi = cat.object(phi.constant_coefficient() + t^3) sage: psi - Drinfeld module defined by X |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base Reciprocally, it is impossible to create two Drinfeld modules in this category if they do not share the same constant @@ -712,10 +712,10 @@ def ore_polring(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: ore_polring = phi.ore_polring() sage: ore_polring Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 @@ -729,7 +729,7 @@ def ore_polring(self): The generator of the Drinfeld module is in the Ore polynomial ring:: - sage: phi(X) in ore_polring + sage: phi(T) in ore_polring True """ return self.category().ore_polring() diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index e8319a1f308..5bdf666218c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -29,7 +29,7 @@ class DrinfeldModuleAction(Action): This class represents the module action induced by a Drinfeld module. - Let `\phi` be a Drinfeld module with base `\gamma: \mathbb{F}_q[X] + Let `\phi` be a Drinfeld module with base `\gamma: \mathbb{F}_q[T] \to K`. Let `L/K` be a field extension, let `x \in L`, let `a` be a function ring element; the action is defined as `(a, x) \mapsto \phi_a(x)`. @@ -46,23 +46,23 @@ class DrinfeldModuleAction(Action): EXAMPLES:: sage: Fq. = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by X |--> t^3 + z over base Finite Field in z of size 11^2 over its base + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over base Finite Field in z of size 11^2 over its base The action on elements is computed as follows:: - sage: P = X + 1 + sage: P = T + 1 sage: a = z sage: action(P, a) ... 4*z + 2 sage: action(0, K.random_element()) 0 - sage: action(FqX.random_element(), 0) + sage: action(A.random_element(), 0) 0 Finally, given a Drinfeld module action, it is easy to recover the @@ -81,9 +81,9 @@ def __init__(self, drinfeld_module): TESTS:: sage: Fq. = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: action._drinfeld_module is phi True @@ -110,17 +110,17 @@ def _act_(self, pol, x): EXAMPLES:: sage: Fq. = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() - sage: P = X + 1 + sage: P = T + 1 sage: a = z sage: action(P, a) 4*z + 2 sage: action(0, K.random_element()) 0 - sage: action(FqX.random_element(), 0) + sage: action(A.random_element(), 0) 0 """ if pol not in self._drinfeld_module.function_ring(): @@ -138,12 +138,12 @@ def _latex_(self): EXAMPLES:: sage: Fq. = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: latex(action) - \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto t^{3} + z\text{{ }over{ }base{ }}\Bold{F}_{11^{2}} + \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} T \mapsto t^{3} + z\text{{ }over{ }base{ }}\Bold{F}_{11^{2}} """ return f'\\text{{Action{{ }}on{{ }}}}' \ f'{latex(self._base_ring)}\\text{{{{ }}' \ @@ -158,12 +158,12 @@ def _repr_(self): EXAMPLES:: sage: Fq. = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by X |--> t^3 + z over base Finite Field in z of size 11^2 over its base + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over base Finite Field in z of size 11^2 over its base """ return f'Action on {self._base_ring} induced by ' \ f'{self._drinfeld_module}' @@ -177,9 +177,9 @@ def drinfeld_module(self): EXAMPLES:: sage: Fq. = GF(11) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z, 0, 0, 1]) + sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: action.drinfeld_module() is phi True diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 22d0f3c399f..9f3784bbbc3 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -40,21 +40,21 @@ class DrinfeldModule(Parent, UniqueRepresentation): r""" - This class represents a Drinfeld `\mathbb{F}_q[X]`-module. + This class represents a Drinfeld `\mathbb{F}_q[T]`-module. - Let `\mathbb{F}_q[X]` be a polynomial ring with coefficients in a + Let `\mathbb{F}_q[T]` be a polynomial ring with coefficients in a finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring - morphism `\gamma: \mathbb{F}_q[X] \to K`. We say that the field `K` - is an `\mathbb{F}_q[X]`-field, so that the *base of the Drinfeld - module* is defined as the `\mathbb{F}_q[X]`-field *K*. The base + morphism `\gamma: \mathbb{F}_q[T] \to K`. We say that the field `K` + is an `\mathbb{F}_q[T]`-field, so that the *base of the Drinfeld + module* is defined as the `\mathbb{F}_q[T]`-field *K*. The base uniquely defines the category, and we also refer to it as the *base ring* or *base field*. The *base morphism* is the morphism `\gamma: - \mathbb{F}_q[X] \to K`. + \mathbb{F}_q[T] \to K`. .. NOTE:: Equivalently, the base of the Drinfeld module could be defined as the - base morphism `\gamma: \mathbb{F}_q[X] \to K`. + base morphism `\gamma: \mathbb{F}_q[T] \to K`. .. NOTE:: @@ -62,42 +62,42 @@ class DrinfeldModule(Parent, UniqueRepresentation): See also :class:`sage.categories.drinfeld_modules`. The monic polynomial that generates the kernel of the base morphism - is called the `\mathbb{F}_q[X]`-characteristic of the - `\mathbb{F}_q[X]`-field `K`. It cal also be referred to as the + is called the `\mathbb{F}_q[T]`-characteristic of the + `\mathbb{F}_q[T]`-field `K`. It cal also be referred to as the function-field characteristic of `K`. Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in `K` and Frobenius variable `\tau: x \mapsto x^q`. A Drinfeld - `\mathbb{F}_q[X]`-module over the `\mathbb{F}_q[X]`-field `K` is an - `\mathbb{F}_q`-algebra morphism `\phi: \mathbb{F}_q[X] \to + `\mathbb{F}_q[T]`-module over the `\mathbb{F}_q[T]`-field `K` is an + `\mathbb{F}_q`-algebra morphism `\phi: \mathbb{F}_q[T] \to K\{\tau\}` such that: 1. The image of `\phi` contains nonconstant Ore polynomials. - 2. For every element `a` in the `\mathbb{F}_q[X]`, the constant + 2. For every element `a` in the `\mathbb{F}_q[T]`, the constant coefficient `\phi(a)` is `\gamma(a)`. For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. - The Drinfeld `\mathbb{F}_q[X]`-module `\phi` is uniquely determined - by the image `\phi_X` of `X` — this serves as input of the class. + The Drinfeld `\mathbb{F}_q[T]`-module `\phi` is uniquely determined + by the image `\phi_T` of `T` — this serves as input of the class. A Drinfeld module is said to be finite if the field `K` is. Despite an emphasis on this case, the base field can be any extension of `\mathbb{F}_q`:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z, 4, 1]) + sage: phi = DrinfeldModule(A, [z, 4, 1]) sage: phi - Drinfeld module defined by X |--> t^2 + 4*t + z over base Finite Field in z of size 5^12 over its base + Drinfeld module defined by T |--> t^2 + 4*t + z over base Finite Field in z of size 5^12 over its base :: sage: Fq = GF(49) - sage: FqX. = Fq[] - sage: K. = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [z, X+1]) + sage: A. = Fq[] + sage: K. = Frac(A) + sage: psi = DrinfeldModule(A, [z, T+1]) sage: psi .. NOTE:: @@ -105,11 +105,11 @@ class DrinfeldModule(Parent, UniqueRepresentation): Finite Drinfeld modules are implemented in the class :class:`sage.rings.function_field.drinfeld_modules.finite_drinfeld_module`. - We say that `\mathbb{F}_q[X]` is the function ring of `\phi`; + We say that `\mathbb{F}_q[T]` is the function ring of `\phi`; `K\{\tau\}` is the Ore polynomial ring of `\phi`. Further, the - generator of `\phi` is `\phi_X` and its constant coefficient is the - constant coefficient of `\phi_X`. The - `\mathbb{F}_q[X]`-characteristic of the `\mathbb{F}_q[X]`-field `K` + generator of `\phi` is `\phi_T` and its constant coefficient is the + constant coefficient of `\phi_T`. The + `\mathbb{F}_q[T]`-characteristic of the `\mathbb{F}_q[T]`-field `K` can also be referred to as its function ring-characteristic. Finally, `K` is just referred to as the codomain base. @@ -119,7 +119,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. NOTE:: Drinfeld modules are defined in a larger setting, in which the - polynomial ring `\mathbb{F}_q[X]` is replaced by a more general + polynomial ring `\mathbb{F}_q[T]` is replaced by a more general function ring: the ring of functions in `k` that are regular outside `\infty`, where `k` is a function field over `\mathbb{F}_q` with transcendence degree `1` and `\infty` is a @@ -141,27 +141,27 @@ class DrinfeldModule(Parent, UniqueRepresentation): ring and the generator:: sage: Fq. = GF(3^2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z, 1, 1]) + sage: phi = DrinfeldModule(A, [z, 1, 1]) sage: phi - Drinfeld module defined by X |--> t^2 + t + z over base Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> t^2 + t + z over base Finite Field in z of size 3^12 over its base Note that the definition of the base morphism is implicit; it is defined as the `\mathbb{F}_q`-algebra morphism defined from - `\mathbb{F}_q[X]` to the base ring, and the base ring is a ring + `\mathbb{F}_q[T]` to the base ring, and the base ring is a ring extension over the base morphism whose underlying ring is the compositum of all the parents of the coefficients. The above Drinfeld module is finite; it can also be infinite:: - sage: L = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [L(X), 1, X^3 + X + 1]) + sage: L = Frac(A) + sage: psi = DrinfeldModule(A, [L(T), 1, T^3 + T + 1]) sage: psi - Drinfeld module defined by X |--> (X^3 + X + 1)*t^2 + t + X over base Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - To: Fraction Field of Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 - Defn: X |--> X + Drinfeld module defined by T |--> (T^3 + T + 1)*t^2 + t + T over base Ring morphism: + From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 + To: Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 + Defn: T |--> T :: @@ -171,23 +171,23 @@ class DrinfeldModule(Parent, UniqueRepresentation): False In those examples, we used a list of coefficients (``[z, 1, 1]``) to - represent the generator `\phi_X = z + t + t^2`. One can also use + represent the generator `\phi_T = z + t + t^2`. One can also use regular Ore polynomials:: sage: ore_polring = phi.ore_polring() sage: t = phi.ore_polring().gen() - sage: rho_X = z + t^3 - sage: rho = DrinfeldModule(FqX, rho_X) + sage: rho_T = z + t^3 + sage: rho = DrinfeldModule(A, rho_T) sage: rho - Drinfeld module defined by X |--> t^3 + z over base Finite Field in z of size 3^12 over its base - sage: rho(X) == rho_X + Drinfeld module defined by T |--> t^3 + z over base Finite Field in z of size 3^12 over its base + sage: rho(T) == rho_T True Images under the Drinfeld module are computed by calling the object:: - sage: phi(X) # phi_X, the generator of the Drinfeld module + sage: phi(T) # phi_T, the generator of the Drinfeld module t^2 + t + z - sage: phi(X^3 + X + 1) # phi_(X^3 +X + 1) + sage: phi(T^3 + T + 1) # phi_(T^3 +T + 1) t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 sage: phi(1) # phi_1 1 @@ -208,7 +208,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: cat = phi.category() sage: cat.object([z, 0, 0, 1]) - Drinfeld module defined by X |--> t^3 + z over base Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> t^3 + z over base Finite Field in z of size 3^12 over its base .. RUBRIC:: The base ring of a Drinfeld module @@ -222,9 +222,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.base_morphism() Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 over its base - Defn: X |--> z + Defn: T |--> z Note that the base ring is *not* the field `K`. Rather, it is a ring extension (see :class:`sage.rings.ring_extension.RingExtension`) @@ -241,9 +241,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.base_morphism() Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 over its base - Defn: X |--> z + Defn: T |--> z :: @@ -252,28 +252,28 @@ class DrinfeldModule(Parent, UniqueRepresentation): :: - sage: phi.function_ring() # Fq[X] - Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + sage: phi.function_ring() # Fq[T] + Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 :: - sage: phi.gen() # phi_X + sage: phi.gen() # phi_T t^2 + t + z - sage: phi.gen() == phi(X) + sage: phi.gen() == phi(T) True :: - sage: phi.constant_coefficient() # Constant coefficient of phi_X + sage: phi.constant_coefficient() # Constant coefficient of phi_T z :: sage: phi.morphism() # The Drinfeld module as a morphism Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 over its base twisted by Frob^2 - Defn: X |--> t^2 + t + z + Defn: T |--> t^2 + t + z One can compute the rank and height:: @@ -287,9 +287,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.j_invariant() # j-invariant 1 - A Drinfeld `\mathbb{F}_q[X]`-module can be seen as an Ore + A Drinfeld `\mathbb{F}_q[T]`-module can be seen as an Ore polynomial with positive degree and constant coefficient - `\gamma(X)`, where `\gamma` is the base morphism. This analogy is + `\gamma(T)`, where `\gamma` is the base morphism. This analogy is the motivation for the following methods:: sage: phi.coefficients() @@ -305,13 +305,13 @@ class DrinfeldModule(Parent, UniqueRepresentation): A morphism of Drinfeld modules `\phi \to \psi` is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a` in - the function ring. In our case, this is equivalent to `f \phi_X = - \psi_X f`. An isogeny is a nonzero morphism. + the function ring. In our case, this is equivalent to `f \phi_T = + \psi_T f`. An isogeny is a nonzero morphism. Use the ``in`` syntax to test if an Ore polynomial defines a morphism:: - sage: phi(X) in Hom(phi, phi) + sage: phi(T) in Hom(phi, phi) True sage: t^6 in Hom(phi, phi) True @@ -380,10 +380,10 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z sage: psi = phi.velu(ore_pol) sage: psi - Drinfeld module defined by X |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over base Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over base Finite Field in z of size 3^12 over its base sage: ore_pol in Hom(phi, psi) True - sage: ore_pol * phi(X) == psi(X) * ore_pol + sage: ore_pol * phi(T) == psi(T) * ore_pol True If the input does not define an isogeny, an exception is raised: @@ -399,8 +399,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: The action of a Drinfeld module - The `\mathbb{F}_q[X]`-Drinfeld module `\phi` induces a special left - `\mathbb{F}_q[X]`-module structure on any field extension `L/K`. Let + The `\mathbb{F}_q[T]`-Drinfeld module `\phi` induces a special left + `\mathbb{F}_q[T]`-module structure on any field extension `L/K`. Let `x \in L` and `a` be in the function ring; the action is defined as `(a, x) \mapsto \phi_a(x)`. The method :meth:`action` returns an ``Action`` object representing the Drinfeld module action. @@ -411,18 +411,18 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: action = phi.action() sage: action - Action on Finite Field in z of size 3^12 over its base induced by Drinfeld module defined by X |--> t^2 + t + z over base Finite Field in z of size 3^12 over its base + Action on Finite Field in z of size 3^12 over its base induced by Drinfeld module defined by T |--> t^2 + t + z over base Finite Field in z of size 3^12 over its base The action on elements is computed by calling the action object:: - sage: P = X + 1 + sage: P = T + 1 sage: a = z sage: action(P, a) ... z^9 + 2*z^8 + 2*z^7 + 2*z^6 + 2*z^3 + z^2 sage: action(0, K.random_element()) 0 - sage: action(FqX.random_element(), 0) + sage: action(A.random_element(), 0) 0 .. RUBRIC:: Inverting the Drinfeld module @@ -432,7 +432,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): `a`, one can retrieve `a` (as a morphism, a Drinfeld module is injective, see [Gos1998]_, cor. 4.5.2.):: - sage: a = FqX.random_element() + sage: a = A.random_element() sage: phi.invert(phi(a)) == a True @@ -442,8 +442,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: Fq = GF(2) sage: K. = Fq.extension(2) - sage: FqX. = Fq[] - sage: DrinfeldModule(FqX, [K(1)]) + sage: A. = Fq[] + sage: DrinfeldModule(A, [K(1)]) Traceback (most recent call last): ... ValueError: generator must have positive degree @@ -452,20 +452,20 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: Fq = GF(2) sage: K. = Fq.extension(2) - sage: FqX. = Fq[] - sage: DrinfeldModule(FqX, [K(0), K(1)]) + sage: A. = Fq[] + sage: DrinfeldModule(A, [K(0), K(1)]) Traceback (most recent call last): ... ValueError: constant coefficient must be nonzero The coefficients of the generator must lie in an - `\mathbb{F}_q[X]`-field, where `\mathbb{F}_q[X]` is the function + `\mathbb{F}_q[T]`-field, where `\mathbb{F}_q[T]` is the function ring of the Drinfeld module:: sage: Fq = GF(2) sage: K. = Fq.extension(2) - sage: FqX. = Fq[] - sage: DrinfeldModule(FqX, [z, QQ(1)]) + sage: A. = Fq[] + sage: DrinfeldModule(A, [z, QQ(1)]) Traceback (most recent call last): ... ValueError: function ring base must coerce into base ring @@ -474,8 +474,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: Fq = GF(2) sage: K. = Fq.extension(2) - sage: FqX. = Fq[] - sage: DrinfeldModule(FqX, [1, QQ(1)]) + sage: A. = Fq[] + sage: DrinfeldModule(A, [1, QQ(1)]) Traceback (most recent call last): ... ValueError: function ring base must coerce into base ring @@ -485,7 +485,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: Fq = GF(2) sage: K. = Fq.extension(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: DrinfeldModule(K, [z, 1, 1]) Traceback (most recent call last): ... @@ -495,9 +495,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: Fq = GF(2) sage: K. = Fq.extension(2) - sage: FqX. = Fq[] - sage: FqXY. = FqX[] - sage: DrinfeldModule(FqXY, [z, 1, 1]) + sage: A. = Fq[] + sage: AY. = A[] + sage: DrinfeldModule(AY, [z, 1, 1]) Traceback (most recent call last): ... TypeError: function ring base must be a finite field @@ -508,8 +508,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: Fq = GF(2) sage: K. = Fq.extension(2) - sage: FqX. = Fq[] - sage: phi = DrinfeldModule(FqX, [z, 1]) + sage: A. = Fq[] + sage: phi = DrinfeldModule(A, [z, 1]) sage: phi.category().object([1, 1, K(1)]) Traceback (most recent call last): ... @@ -519,8 +519,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): :: sage: Fq = K = GF(2) - sage: FqX. = Fq[] - sage: phi = DrinfeldModule(FqX, [1, 1]) + sage: A. = Fq[] + sage: phi = DrinfeldModule(A, [1, 1]) Traceback (most recent call last): ... ValueError: function ring base must coerce into base ring @@ -528,28 +528,28 @@ class DrinfeldModule(Parent, UniqueRepresentation): :: sage: Fq = K = GF(2) - sage: FqX. = Fq[] - sage: phi = DrinfeldModule(FqX, [K(1), 1]) + sage: A. = Fq[] + sage: phi = DrinfeldModule(A, [K(1), 1]) sage: isinstance(phi.ore_polring(), OrePolynomialRing) True Test that the base morphism is correct:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K = Frac(Fq) - sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(1)]) + sage: phi = DrinfeldModule(A, [Fq.gen(), K(1)]) sage: phi.base().codomain() is K True :: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: k = Frac(Fq) - sage: kT. = k[] - sage: K = k.extension(T^3 + T + 1) - sage: phi = DrinfeldModule(FqX, [Fq.gen(), K.gen()]) + sage: kS. = k[] + sage: K = k.extension(S^3 + S + 1) + sage: phi = DrinfeldModule(A, [Fq.gen(), K.gen()]) sage: phi.base().codomain() is K True @@ -557,20 +557,20 @@ class DrinfeldModule(Parent, UniqueRepresentation): of definition of the coefficients:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: k = Frac(Fq) - sage: kT. = k[] - sage: K = k.extension(T^3 + T + 1) - sage: phi = DrinfeldModule(FqX, [K(k.gen()), 1]) + sage: kS. = k[] + sage: K = k.extension(S^3 + S + 1) + sage: phi = DrinfeldModule(A, [K(k.gen()), 1]) sage: phi.base().codomain() is K True :: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [Fq.gen(), K(Fq.gen())]) + sage: phi = DrinfeldModule(A, [Fq.gen(), K(Fq.gen())]) sage: phi.base().codomain() is K True """ @@ -596,17 +596,17 @@ def __classcall_private__(cls, function_ring, gen, name='t'): sage: from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: isinstance(phi, FiniteDrinfeldModule) True :: - sage: K = Frac(FqX) - sage: phi = DrinfeldModule(FqX, [K(X), 1]) + sage: K = Frac(A) + sage: phi = DrinfeldModule(A, [K(T), 1]) sage: isinstance(psi, FiniteDrinfeldModule) False """ @@ -616,7 +616,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): # the category. Another problem is that those lines are # duplicate. As a general comment, there are sanity checks both # here and in the category constructor, which is not ideal. - # Check domain is Fq[X] + # Check domain is Fq[T] if not isinstance(function_ring, PolynomialRing_general): raise NotImplementedError('function ring must be a polynomial ' 'ring') @@ -632,7 +632,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): # Base ring without morphism structure: base_ring_noext = ore_polring.base() name = ore_polring.variable_name() - # `gen` is a list of coefficients (function_ring = Fq[X]): + # `gen` is a list of coefficients (function_ring = Fq[T]): elif isinstance(gen, (list, tuple)): ore_polring = None # Base ring without morphism structure: @@ -688,21 +688,21 @@ def __init__(self, gen, category): TESTS:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: gen = [p_root, z12^3, z12^5] - sage: phi = DrinfeldModule(FqX, gen) + sage: phi = DrinfeldModule(A, gen) sage: ore_polring = phi.ore_polring() sage: phi._base == phi.category().base() True - sage: phi._function_ring == FqX + sage: phi._function_ring == A True sage: phi._gen == ore_polring(gen) True sage: phi._ore_polring == ore_polring True - sage: phi._morphism == Hom(FqX, ore_polring)(phi._gen) + sage: phi._morphism == Hom(A, ore_polring)(phi._gen) True """ self._base = category.base() @@ -728,14 +728,14 @@ def __call__(self, a): TESTS:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) :: - sage: a = X^3 + 4*X + 2 - sage: phi(a) == phi(X)^3 + 4*phi(X) + 2 + sage: a = T^3 + 4*T + 2 + sage: phi(a) == phi(T)^3 + 4*phi(T) + 2 True sage: phi(a)[0] == p_root^3 + 4*p_root + 2 True @@ -746,12 +746,12 @@ def __call__(self, a): 0 sage: phi(1) 1 - sage: phi(X) == phi._gen + sage: phi(T) == phi._gen True :: - sage: a = FqX.random_element(5) + sage: a = A.random_element(5) sage: phi(a)[0] == phi.category().base()(a) True """ @@ -776,10 +776,10 @@ def _Hom_(self, other, category): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: t = phi.ore_polring().gen() sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) @@ -800,12 +800,12 @@ def _check_rank_two(self): TESTS:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi._check_rank_two() - sage: phi = DrinfeldModule(FqX, [p_root, 1]) + sage: phi = DrinfeldModule(A, [p_root, 1]) sage: phi._check_rank_two() Traceback (most recent call last): ... @@ -823,12 +823,12 @@ def _latex_(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: latex(phi) - \text{Drinfeld{ }module{ }defined{ }by{ }} X \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }base{ }}\Bold{F}_{5^{12}} + \text{Drinfeld{ }module{ }defined{ }by{ }} T \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }base{ }}\Bold{F}_{5^{12}} """ return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ f'{latex(self._function_ring.gen())} '\ @@ -844,12 +844,12 @@ def _repr_(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi - Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base """ return f'Drinfeld module defined by {self._function_ring.gen()} ' \ f'|--> {self._gen} over base {self._base}' @@ -866,17 +866,17 @@ def action(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: action = phi.action() sage: action - Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base The action on elements is computed as follows:: - sage: P = X^2 + X + 1 + sage: P = T^2 + T + 1 sage: a = z12 + 1 sage: action(P, a) 3*z12^11 + 2*z12^10 + 3*z12^9 + 3*z12^7 + 4*z12^5 + z12^4 + z12^3 + 2*z12 + 1 @@ -901,10 +901,10 @@ def coefficient(self, n): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.coefficient(0) 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi.coefficient(0) == p_root @@ -940,10 +940,10 @@ def coefficients(self, sparse=True): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.coefficients() [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, z12^3, @@ -952,7 +952,7 @@ def coefficients(self, sparse=True): Careful, the method only returns the nonzero coefficients, unless otherwise specified:: - sage: rho = DrinfeldModule(FqX, [p_root, 0, 0, 0, 1]) + sage: rho = DrinfeldModule(A, [p_root, 0, 0, 0, 1]) sage: rho.coefficients() [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, 1] @@ -974,11 +974,11 @@ def gen(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: phi.gen() == phi(X) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.gen() == phi(T) True """ return self._gen @@ -1008,26 +1008,26 @@ def height(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.height() == 1 True sage: phi.is_ordinary() True - sage: L = Frac(FqX) - sage: phi = DrinfeldModule(FqX, [L(2), L(1)]) + sage: L = Frac(A) + sage: phi = DrinfeldModule(A, [L(2), L(1)]) sage: phi.height() Traceback (most recent call last): ... ValueError: height is defined for prime function field characteristic sage: Fq = GF(343) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: phi.height() 2 sage: phi.is_supersingular() @@ -1060,14 +1060,14 @@ def invert(self, ore_pol): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) - sage: a = FqX.random_element() + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: a = A.random_element() sage: phi.invert(phi(a)) == a True - sage: phi.invert(phi(X)) == X + sage: phi.invert(phi(T)) == T True sage: phi.invert(phi(Fq.gen())) == Fq.gen() True @@ -1092,7 +1092,7 @@ def invert(self, ore_pol): TESTS:: - sage: a = FqX.random_element() + sage: a = A.random_element() sage: cat = phi.category() sage: phi_r1 = cat.random_object(1) sage: phi_r1.invert(phi_r1(a)) == a @@ -1121,12 +1121,12 @@ def invert(self, ore_pol): 'module') k = deg // r - X = self._function_ring.gen() + T = self._function_ring.gen() mat_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] for i in range(k+1): - phi_X_i = self(X**i) + phi_T_i = self(T**i) for j in range(i+1): - mat_lines[j][i] = phi_X_i[r*j] + mat_lines[j][i] = phi_T_i[r*j] mat = Matrix(mat_lines) vec = vector([list(ore_pol)[r*j] for j in range(k+1)]) pre_image = self._function_ring(list((mat**(-1)) * vec)) @@ -1146,14 +1146,14 @@ def is_finite(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.is_finite() True - sage: L = Frac(FqX) - sage: psi = DrinfeldModule(FqX, [L(2), L(1)]) + sage: L = Frac(A) + sage: psi = DrinfeldModule(A, [L(2), L(1)]) sage: psi.is_finite() False """ @@ -1165,7 +1165,7 @@ def j_invariant(self): Return the j-invariant of the Drinfeld module if the rank is two; raise a NotImplementedError otherwise. - Assume the rank is two. Write the generator `\phi_X = \omega + + Assume the rank is two. Write the generator `\phi_T = \omega + g\tau + \Delta\tau^2`. The j-invariant is defined by `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base ring of the function ring. In our case, this field is always finite. @@ -1175,22 +1175,22 @@ def j_invariant(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.j_invariant() z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2 - sage: psi = DrinfeldModule(FqX, [p_root, 1, 1]) + sage: psi = DrinfeldModule(A, [p_root, 1, 1]) sage: psi.j_invariant() 1 - sage: rho = DrinfeldModule(FqX, [p_root, 0, 1]) + sage: rho = DrinfeldModule(A, [p_root, 0, 1]) sage: rho.j_invariant() 0 The rank must be two:: - sage: theta = DrinfeldModule(FqX, [p_root, 1, 0]) + sage: theta = DrinfeldModule(A, [p_root, 1, 0]) sage: theta.j_invariant() Traceback (most recent call last): ... @@ -1212,15 +1212,15 @@ def morphism(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.morphism() Ring morphism: - From: Univariate Polynomial Ring in X over Finite Field in z2 of size 5^2 + From: Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 To: Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 - Defn: X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Defn: T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: from sage.rings.morphism import RingHomomorphism sage: isinstance(phi.morphism(), RingHomomorphism) True @@ -1228,9 +1228,9 @@ def morphism(self): Actually, the ``DrinfeldModule`` method :meth:`__call__` simply class the ``__call__`` method of this morphism:: - sage: phi.morphism()(X) == phi(X) + sage: phi.morphism()(T) == phi(T) True - sage: a = FqX.random_element() + sage: a = A.random_element() sage: phi.morphism()(a) == phi(a) True @@ -1244,7 +1244,7 @@ class the ``__call__`` method of this morphism:: True sage: m.im_gens() [z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12] - sage: phi(X) == m.im_gens()[0] + sage: phi(T) == m.im_gens()[0] True """ return self._morphism @@ -1260,16 +1260,16 @@ def rank(self): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.rank() 2 - sage: psi = DrinfeldModule(FqX, [p_root, 2]) + sage: psi = DrinfeldModule(A, [p_root, 2]) sage: psi.rank() 1 - sage: rho = DrinfeldModule(FqX, [p_root, 0, 0, 0, 1]) + sage: rho = DrinfeldModule(A, [p_root, 0, 0, 0, 1]) sage: rho.rank() 4 """ @@ -1311,21 +1311,21 @@ def velu(self, isog): EXAMPLES:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: t = phi.ore_polring().gen() sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) sage: psi - Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base sage: isog in Hom(phi, psi) True This method works for endomorphisms as well:: - sage: phi.velu(phi(X)) is phi + sage: phi.velu(phi(T)) is phi True sage: phi.velu(t^6) is phi True diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 89fc6744209..5722d04d175 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -44,11 +44,11 @@ class FiniteDrinfeldModule(DrinfeldModule): ``FiniteDrinfeldModule`` depending on the input:: sage: Fq = GF(343) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z6, 0, 5]) + sage: phi = DrinfeldModule(A, [z6, 0, 5]) sage: phi - Drinfeld module defined by X |--> 5*t^2 + z6 over base Finite Field in z6 of size 7^6 over its base + Drinfeld module defined by T |--> 5*t^2 + z6 over base Finite Field in z6 of size 7^6 over its base :: @@ -84,19 +84,19 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: chi = phi.frobenius_charpoly() sage: chi - T^2 + (X + 2*z3^2 + 2*z3 + 1)*T + 2*X^2 + (z3^2 + z3 + 4)*X + 2*z3 + X^2 + (T + 2*z3^2 + 2*z3 + 1)*X + 2*T^2 + (z3^2 + z3 + 4)*T + 2*z3 sage: frob_pol = frobenius_endomorphism.ore_polynomial() - sage: chi(frob_pol, phi(X)) + sage: chi(frob_pol, phi(T)) 0 This makes it possible to compute the Frobenius trace and norm:: sage: phi.frobenius_trace() - 6*X + 5*z3^2 + 5*z3 + 6 + 6*T + 5*z3^2 + 5*z3 + 6 sage: phi.frobenius_trace() == -chi[1] True sage: phi.frobenius_norm() - 2*X^2 + (z3^2 + z3 + 4)*X + 2*z3 + 2*T^2 + (z3^2 + z3 + 4)*T + 2*z3 And to decide if a Drinfeld module is ordinary or supersingular:: @@ -125,11 +125,11 @@ def __init__(self, gen, category): TESTS:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: gen = [p_root, z12^3, z12^5] - sage: phi = DrinfeldModule(FqX, gen) + sage: phi = DrinfeldModule(A, gen) sage: ore_polring = phi.ore_polring() sage: phi._gen == ore_polring(gen) True @@ -157,9 +157,9 @@ def frobenius_endomorphism(self): EXAMPLES:: sage: Fq = GF(343) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: phi.frobenius_endomorphism() Drinfeld Module morphism: From (gen): z6*t^2 + 1 @@ -190,15 +190,15 @@ def frobenius_charpoly(self, var='T'): characteristic polynomial of the Frobenius endomorphism as a bivariate polynomial. - Let `\chi = T^2 - A(X)T + B(X)` be the characteristic polynomial + Let `\chi = X^2 - A(T)X + B(T)` be the characteristic polynomial of the Frobenius endomorphism, let `t^n` be the Ore polynomial that defines the Frobenius endomorphism of `\phi`; by definition, `n` is the degree over `\mathbb{F}_q` of the base - codomain. We have `\chi(t^n)(\phi(X)) = t^{2n} - \phi_A t^n + + codomain. We have `\chi(t^n)(\phi(T)) = t^{2n} - \phi_A t^n + \phi_B = 0`, with `\deg(A) \leq \frac{n}{2}` and `\deg(B) = n`. - Note that the *Frobenius trace* is defined as `A(X)` and the - *Frobenius norm* is defined as `B(X)`. + Note that the *Frobenius trace* is defined as `A(T)` and the + *Frobenius norm* is defined as `B(T)`. INPUT: (default: ``'T'``) the name of the second variable @@ -208,9 +208,9 @@ def frobenius_charpoly(self, var='T'): EXAMPLES:: sage: Fq = GF(343) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: chi = phi.frobenius_charpoly() sage: chi T^2 + ((3*z3^2 + z3 + 4)*X + 4*z3^2 + 6*z3 + 3)*T + (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 @@ -218,24 +218,24 @@ def frobenius_charpoly(self, var='T'): :: sage: frob_pol = phi.frobenius_endomorphism().ore_polynomial() - sage: chi(frob_pol, phi(X)) + sage: chi(frob_pol, phi(T)) 0 :: - sage: A = phi.frobenius_trace() - sage: A - (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 - sage: B = phi.frobenius_norm() - sage: B - (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + sage: trace = phi.frobenius_trace() + sage: trace + (4*z3^2 + 6*z3 + 3)*T + 3*z3^2 + z3 + 4 + sage: norm = phi.frobenius_norm() + sage: norm + (5*z3^2 + 2*z3)*T^2 + (4*z3^2 + 3*z3)*T + 5*z3^2 + 2*z3 :: sage: n = 2 # Degree over Fq of the base codomain - sage: A.degree() <= n/2 + sage: trace.degree() <= n/2 True - sage: B.degree() == n + sage: norm.degree() == n True ALGORITHM: @@ -249,8 +249,8 @@ def frobenius_charpoly(self, var='T'): computation of the norm and of the trace. """ self._check_rank_two() - A = self._function_ring # Fq[X] - S = PolynomialRing(A, name=var) # Fq[X][T] + A = self._function_ring # Fq[T] + S = PolynomialRing(A, name=var) # Fq[T][T] # Does not work when Fq is not a prime field... # chi = self._gen.reduced_charpoly() # return -chi(A.gen(), S.gen()) @@ -261,10 +261,10 @@ def frobenius_norm(self): Return Frobenius norm of the Drinfeld module, if the rank is two; raise a NotImplementedError otherwise. - Let `\mathbb{F}_q[X]` be the function ring, write `\chi = T^2 - - A(X)T + B(X) \in \mathbb{F}_q[X][T]` for the characteristic + Let `\mathbb{F}_q[T]` be the function ring, write `\chi = X^2 - + A(T)X + B(T) \in \mathbb{F}_q[T][X]` for the characteristic polynomial of the Frobenius endomorphism. The *Frobenius norm* - is defined as the polynomial `B(X) \in \mathbb{F}_q[X]`. + is defined as the polynomial `B(T) \in \mathbb{F}_q[T]`. Let `n` be the degree over `\mathbb{F}_q` of the base codomain. Then the Frobenius norm has degree `n`. @@ -274,12 +274,12 @@ def frobenius_norm(self): EXAMPLES:: sage: Fq = GF(343) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: B = phi.frobenius_norm() sage: B - (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + (5*z3^2 + 2*z3)*T^2 + (4*z3^2 + 3*z3)*T + 5*z3^2 + 2*z3 :: @@ -315,10 +315,10 @@ def frobenius_trace(self): Return Frobenius norm of the Drinfeld module, if the rank is two; raise a NotImplementedError otherwise. - Let `\mathbb{F}_q[X]` be the function ring, write `\chi = T^2 - - A(X)T + B(X) \in \mathbb{F}_q[X][T]` for the characteristic + Let `\mathbb{F}_q[T]` be the function ring, write `\chi = T^2 - + A(X)T + B(X) \in \mathbb{F}_q[T][T]` for the characteristic polynomial of the Frobenius endomorphism. The *Frobenius norm* - is defined as the polynomial `B(X) \in \mathbb{F}_q[X]`. + is defined as the polynomial `B(T) \in \mathbb{F}_q[T]`. Let `n` be the degree over `\mathbb{F}_q` of the base codomain. Then the Frobenius trace has degree `\leq \frac{n}{2}`. @@ -327,13 +327,13 @@ def frobenius_trace(self): ALGORITHM: - Let `A(X)` denote the Frobenius trace and `B(X)` denote the - Frobenius norm. We begin by computing `B(X)`, see docstring + Let `A(T)` denote the Frobenius trace and `B(T)` denote the + Frobenius norm. We begin by computing `B(T)`, see docstring of method :meth:`frobenius_norm` for details. The characteristic polynomial of the Frobenius yields `t^{2n} - \phi_A t^n + \phi_B = 0`, where `t^n` is the Frobenius endomorphism. As `\phi_B` is now known, we can compute - `\phi_A = (t^{2n} + \phi_B) / t^n`. We get `A(X)` by + `\phi_A = (t^{2n} + \phi_B) / t^n`. We get `A(T)` by inverting this quantity, using the method :meth:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule.invert`, see its docstring for details. @@ -341,12 +341,12 @@ def frobenius_trace(self): EXAMPLES:: sage: Fq = GF(343) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: A = phi.frobenius_trace() sage: A - (4*z3^2 + 6*z3 + 3)*X + 3*z3^2 + z3 + 4 + (4*z3^2 + 6*z3 + 3)*T + 3*z3^2 + z3 + 4 :: @@ -386,9 +386,9 @@ def is_ordinary(self): EXAMPLES:: sage: Fq = GF(343) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: phi.is_ordinary() False sage: phi_p = phi(phi.characteristic()) @@ -398,10 +398,10 @@ def is_ordinary(self): ALGORITHM: Compute the Frobenius trace and test if the - `\mathbb{F}_q[X]` characteristic divides it. + `\mathbb{F}_q[T]` characteristic divides it. We could also test if the image of the - `\mathbb{F}_q[X]`-characteristic under the Drinfeld module + `\mathbb{F}_q[T]`-characteristic under the Drinfeld module is purely inseparable; see [Gek1991]_, Proposition 4.1. """ self._check_rank_two() @@ -422,9 +422,9 @@ def is_supersingular(self): EXAMPLES:: sage: Fq = GF(343) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [1, 0, z6]) + sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: phi.is_supersingular() True sage: phi(phi.characteristic()) # Purely inseparable diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 690d35653b8..812301bdbdf 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -39,10 +39,10 @@ class DrinfeldModuleHomset(Homset): EXAMPLES:: sage: Fq = GF(27) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) - sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: phi = DrinfeldModule(A, [z6, z6, 2]) + sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: hom Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 @@ -64,7 +64,7 @@ class DrinfeldModuleHomset(Homset): The domain and codomain must have the same Drinfeld modules category:: - sage: rho = DrinfeldModule(FqX, [Frac(FqX)(X), 1]) + sage: rho = DrinfeldModule(A, [Frac(A)(T), 1]) sage: Hom(phi, rho) Traceback (most recent call last): ... @@ -72,7 +72,7 @@ class DrinfeldModuleHomset(Homset): :: - sage: sigma = DrinfeldModule(FqX, [1, z6, 2]) + sage: sigma = DrinfeldModule(A, [1, z6, 2]) sage: Hom(phi, sigma) Traceback (most recent call last): ... @@ -152,10 +152,10 @@ def __init__(self, X, Y, category=None, check=True): TESTS:: sage: Fq = GF(27) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) - sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: phi = DrinfeldModule(A, [z6, z6, 2]) + sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: hom.domain() is phi True @@ -183,10 +183,10 @@ def _latex_(self): EXAMPLES:: sage: Fq = GF(27) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) - sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: phi = DrinfeldModule(A, [z6, z6, 2]) + sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: latex(hom) \text{Set{ }of{ }Drinfeld{ }module{ }morphisms{ }from{ }(gen){ }}2 t^{2} + z_{6} t + z_{6}\text{{ }to{ }(gen){ }}2 t^{2} + \left(2 z_{6}^{5} + 2 z_{6}^{4} + 2 z_{6} + 1\right) t + z_{6} @@ -205,10 +205,10 @@ def _repr_(self): EXAMPLES:: sage: Fq = GF(27) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) - sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: phi = DrinfeldModule(A, [z6, z6, 2]) + sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: hom Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 @@ -232,10 +232,10 @@ def __contains__(self, x): In the next examples, the input is an Ore polynomial:: sage: Fq = GF(27) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) - sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: phi = DrinfeldModule(A, [z6, z6, 2]) + sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: end = End(phi) sage: t = phi.ore_polring().gen() @@ -284,10 +284,10 @@ def _element_constructor_(self, *args, **kwds): EXAMPLES:: sage: Fq = GF(27) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(2) - sage: phi = DrinfeldModule(FqX, [z6, z6, 2]) - sage: psi = DrinfeldModule(FqX, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) + sage: phi = DrinfeldModule(A, [z6, z6, 2]) + sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: hom = Hom(phi, psi) sage: end = End(phi) sage: t = phi.ore_polring().gen() diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 6383a9b2aad..a0ef455704e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -29,10 +29,10 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, r""" This class represents a Drinfeld module morphism. - Let `\phi, \psi` be two Drinfeld modules with base `\gamma: \mathbb{F}_q[X] + Let `\phi, \psi` be two Drinfeld modules with base `\gamma: \mathbb{F}_q[T] \to K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a - \in \mathbb{F}_q[X]`. In our case, this is equivalent to `f \phi_X = \psi_X + \in \mathbb{F}_q[T]`. In our case, this is equivalent to `f \phi_T = \psi_T f`. An *isogeny* is a nonzero morphism. To create a morphism object, the user should never explicitly @@ -40,10 +40,10 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, homset with the defining Ore polynomial:: sage: Fq = GF(25) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(FqX, [p_root, z12^3, z12^5]) + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: t = phi.ore_polring().gen() sage: ore_pol = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(ore_pol) @@ -64,14 +64,14 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, One can get basic data on the morphism:: sage: morphism.domain() - Drinfeld module defined by X |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base sage: morphism.domain() is phi True :: sage: morphism.codomain() - Drinfeld module defined by X |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base sage: morphism.codomain() is psi True @@ -138,10 +138,10 @@ def __classcall_private__(cls, parent, x): TESTS:: sage: Fq = GF(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism is Hom(phi, psi)(morphism) @@ -183,10 +183,10 @@ def __init__(self, parent, ore_pol): TESTS:: sage: Fq = GF(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism._domain is phi @@ -211,10 +211,10 @@ def _latex_(self): EXAMPLES:: sage: Fq = GF(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: latex(morphism) @@ -244,10 +244,10 @@ def _repr_(self): EXAMPLES:: sage: Fq = GF(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism @@ -268,10 +268,10 @@ def is_zero(self): EXAMPLES:: sage: Fq = GF(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism.is_zero() @@ -292,10 +292,10 @@ def is_isogeny(self): EXAMPLES:: sage: Fq = GF(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism.is_isogeny() @@ -328,10 +328,10 @@ def is_isomorphism(self): EXAMPLES:: sage: Fq = GF(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism.is_isomorphism() @@ -364,10 +364,10 @@ def ore_polynomial(self): EXAMPLES:: sage: Fq = GF(2) - sage: FqX. = Fq[] + sage: A. = Fq[] sage: K. = Fq.extension(6) - sage: phi = DrinfeldModule(FqX, [z6, 1, 1]) - sage: psi = DrinfeldModule(FqX, [z6, z6^4 + z6^2 + 1, 1]) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: ore_pol = morphism.ore_polynomial() @@ -376,7 +376,7 @@ def ore_polynomial(self): :: - sage: ore_pol * phi(X) == psi(X) * ore_pol + sage: ore_pol * phi(T) == psi(T) * ore_pol True """ return self._ore_polynomial From 774f670d75189f4ff2818f3130fc9761d4ceb541 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Mon, 6 Feb 2023 15:54:53 +0100 Subject: [PATCH 157/197] Adress David's comments - line 66: cal - line 79: function ring before definition - line 119: rewrite the note - line 140: other word for "precising" - line 190: pep8 --- .../function_field/drinfeld_modules/drinfeld_module.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 9f3784bbbc3..9066f5722a6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -76,7 +76,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): 2. For every element `a` in the `\mathbb{F}_q[T]`, the constant coefficient `\phi(a)` is `\gamma(a)`. - For `a` in the function ring, `\phi(a)` is denoted `\phi_a`. + For `a` in `\mathbb{F}_q[T]`, `\phi(a)` is denoted `\phi_a`. The Drinfeld `\mathbb{F}_q[T]`-module `\phi` is uniquely determined by the image `\phi_T` of `T` — this serves as input of the class. @@ -137,8 +137,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: Construction - A Drinfeld module object is constructed by precising the function - ring and the generator:: + A Drinfeld module object is constructed by giving the function ring + and the generator:: sage: Fq. = GF(3^2) sage: A. = Fq[] @@ -187,7 +187,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi(T) # phi_T, the generator of the Drinfeld module t^2 + t + z - sage: phi(T^3 + T + 1) # phi_(T^3 +T + 1) + sage: phi(T^3 + T + 1) # phi_(T^3 + T + 1) t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 sage: phi(1) # phi_1 1 From a70c2486b1cdee82305c5e1175353dcd0488d57c Mon Sep 17 00:00:00 2001 From: Kryzar Date: Mon, 6 Feb 2023 16:46:27 +0100 Subject: [PATCH 158/197] Add a LaTeX name for Drinfeld modules There is now an optional `latexname=None` argument given at init. --- src/sage/categories/drinfeld_modules.py | 6 +- .../function_field/drinfeld_modules/action.py | 4 +- .../drinfeld_modules/drinfeld_module.py | 74 ++++++++++++++----- .../finite_drinfeld_module.py | 2 +- .../drinfeld_modules/morphism.py | 4 +- 5 files changed, 63 insertions(+), 27 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 142300e0971..c8b6252ae29 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -137,7 +137,7 @@ class DrinfeldModules(Category_over_base_ring): sage: psi = cat.object([p_root, 1]) sage: psi - Drinfeld module defined by T |--> t + z^3 + 7*z^2 + 6*z + 10 over base Finite Field in z of size 11^4 over its base + Drinfeld module defined by T |--> t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 over its base sage: psi.category() is cat True @@ -474,7 +474,7 @@ def object(self, gen): sage: cat = phi.category() sage: psi = cat.object([p_root, 0, 1]) sage: psi - Drinfeld module defined by T |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over base Finite Field in z of size 11^4 over its base + Drinfeld module defined by T |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 over its base sage: t = phi.ore_polring().gen() sage: cat.object(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi True @@ -690,7 +690,7 @@ def constant_coefficient(self): sage: t = phi.ore_polring().gen() sage: psi = cat.object(phi.constant_coefficient() + t^3) sage: psi - Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base Reciprocally, it is impossible to create two Drinfeld modules in this category if they do not share the same constant diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 5bdf666218c..826ea735f77 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -51,7 +51,7 @@ class DrinfeldModuleAction(Action): sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over base Finite Field in z of size 11^2 over its base + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 11^2 over its base The action on elements is computed as follows:: @@ -163,7 +163,7 @@ def _repr_(self): sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over base Finite Field in z of size 11^2 over its base + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 11^2 over its base """ return f'Action on {self._base_ring} induced by ' \ f'{self._drinfeld_module}' diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 9066f5722a6..056e63aa145 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -90,7 +90,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(A, [z, 4, 1]) sage: phi - Drinfeld module defined by T |--> t^2 + 4*t + z over base Finite Field in z of size 5^12 over its base + Drinfeld module defined by T |--> t^2 + 4*t + z over Finite Field in z of size 5^12 over its base :: @@ -145,7 +145,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(A, [z, 1, 1]) sage: phi - Drinfeld module defined by T |--> t^2 + t + z over base Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> t^2 + t + z over Finite Field in z of size 3^12 over its base Note that the definition of the base morphism is implicit; it is defined as the `\mathbb{F}_q`-algebra morphism defined from @@ -158,7 +158,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: L = Frac(A) sage: psi = DrinfeldModule(A, [L(T), 1, T^3 + T + 1]) sage: psi - Drinfeld module defined by T |--> (T^3 + T + 1)*t^2 + t + T over base Ring morphism: + Drinfeld module defined by T |--> (T^3 + T + 1)*t^2 + t + T over Ring morphism: From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 Defn: T |--> T @@ -179,7 +179,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: rho_T = z + t^3 sage: rho = DrinfeldModule(A, rho_T) sage: rho - Drinfeld module defined by T |--> t^3 + z over base Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 3^12 over its base sage: rho(T) == rho_T True @@ -192,6 +192,12 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi(1) # phi_1 1 + One can give a LaTeX name to be used for LaTeX representation:: + + sage: sigma = DrinfeldModule(A, [z, 1, 1], latexname='\phi') + sage: latex(sigma) + \phi + .. RUBRIC:: The category of Drinfeld modules Drinfeld modules have their own category (see class @@ -208,7 +214,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: cat = phi.category() sage: cat.object([z, 0, 0, 1]) - Drinfeld module defined by T |--> t^3 + z over base Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 3^12 over its base .. RUBRIC:: The base ring of a Drinfeld module @@ -380,7 +386,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z sage: psi = phi.velu(ore_pol) sage: psi - Drinfeld module defined by T |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over base Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over Finite Field in z of size 3^12 over its base sage: ore_pol in Hom(phi, psi) True sage: ore_pol * phi(T) == psi(T) * ore_pol @@ -411,7 +417,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: action = phi.action() sage: action - Action on Finite Field in z of size 3^12 over its base induced by Drinfeld module defined by T |--> t^2 + t + z over base Finite Field in z of size 3^12 over its base + Action on Finite Field in z of size 3^12 over its base induced by Drinfeld module defined by T |--> t^2 + t + z over Finite Field in z of size 3^12 over its base The action on elements is computed by calling the action object:: @@ -576,7 +582,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): """ @staticmethod - def __classcall_private__(cls, function_ring, gen, name='t'): + def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): """ Check input validity and return a ``DrinfeldModule`` or ``FiniteDrinfeldModule`` object accordingly. @@ -589,6 +595,8 @@ def __classcall_private__(cls, function_ring, gen, name='t'): coefficients or an Ore polynomial - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring gen + - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld + module OUTPUT: a DrinfeldModule or FiniteDrinfeldModule @@ -648,6 +656,10 @@ def __classcall_private__(cls, function_ring, gen, name='t'): base_ring_noext.has_coerce_map_from(function_ring.base_ring())): raise ValueError('function ring base must coerce into base ring') + # Check LaTeX name + if latexname is not None and type(latexname) is not str: + raise ValueError('LaTeX name should be a string') + # Build the category if isinstance(base_ring_noext, RingExtension_generic): base_ring = base_ring_noext @@ -669,7 +681,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): return FiniteDrinfeldModule(gen, category) return cls.__classcall__(cls, gen, category) - def __init__(self, gen, category): + def __init__(self, gen, category, latexname=None): """ Initialize ``self``. @@ -684,6 +696,8 @@ def __init__(self, gen, category): coefficients or an Ore polynomial - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring gen + - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld + module TESTS:: @@ -704,10 +718,13 @@ def __init__(self, gen, category): True sage: phi._morphism == Hom(A, ore_polring)(phi._gen) True + sage: phi._latexname is None + True """ self._base = category.base() self._function_ring = category.function_ring() self._gen = gen + self._latexname = latexname self._morphism = category._function_ring.hom([gen]) self._ore_polring = gen.parent() self._Fq = self._function_ring.base_ring() # Must be last @@ -818,6 +835,9 @@ def _latex_(self): r""" Return a LaTeX representation of the Drinfeld module. + If a LaTeX name was given at init. using `latexname`, use the LaTeX + name. Otherwise, create a representation. + OUTPUT: a string EXAMPLES:: @@ -829,11 +849,27 @@ def _latex_(self): sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: latex(phi) \text{Drinfeld{ }module{ }defined{ }by{ }} T \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }base{ }}\Bold{F}_{5^{12}} + + :: + + sage: psi = DrinfeldModule(A, [p_root, z12^3, z12^5], latexname='\psi') + sage: latex(psi) + \psi + + :: + + sage: psi = DrinfeldModule(A, [p_root, z12^3, z12^5], latexname=1729) + Traceback (most recent call last): + ... + ValueError: LaTeX name should be a string """ - return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ - f'{latex(self._function_ring.gen())} '\ - f'\\mapsto {latex(self._gen)}' \ - f'\\text{{{{ }}over{{ }}base{{ }}}}{latex(self._base)}' + if self._latexname is not None: + return self._latexname + else: + return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ + f'{latex(self._function_ring.gen())} '\ + f'\\mapsto {latex(self._gen)}' \ + f'\\text{{{{ }}over{{ }}base{{ }}}}{latex(self._base)}' def _repr_(self): r""" @@ -849,10 +885,10 @@ def _repr_(self): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi - Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base """ return f'Drinfeld module defined by {self._function_ring.gen()} ' \ - f'|--> {self._gen} over base {self._base}' + f'|--> {self._gen} over {self._base}' def action(self): r""" @@ -872,7 +908,7 @@ def action(self): sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: action = phi.action() sage: action - Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base The action on elements is computed as follows:: @@ -1190,8 +1226,8 @@ def j_invariant(self): The rank must be two:: - sage: theta = DrinfeldModule(A, [p_root, 1, 0]) - sage: theta.j_invariant() + sage: sigma = DrinfeldModule(A, [p_root, 1, 0]) + sage: sigma.j_invariant() Traceback (most recent call last): ... NotImplementedError: rank must be 2 @@ -1319,7 +1355,7 @@ def velu(self, isog): sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) sage: psi - Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base sage: isog in Hom(phi, psi) True diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 5722d04d175..016a4ee6a12 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -48,7 +48,7 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, 0, 5]) sage: phi - Drinfeld module defined by T |--> 5*t^2 + z6 over base Finite Field in z6 of size 7^6 over its base + Drinfeld module defined by T |--> 5*t^2 + z6 over Finite Field in z6 of size 7^6 over its base :: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index a0ef455704e..3ff5d40f7b7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -64,14 +64,14 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, One can get basic data on the morphism:: sage: morphism.domain() - Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base sage: morphism.domain() is phi True :: sage: morphism.codomain() - Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over base Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base sage: morphism.codomain() is psi True From b48243cb6617064254041b0050c3effe44050cee Mon Sep 17 00:00:00 2001 From: Kryzar Date: Mon, 6 Feb 2023 17:00:09 +0100 Subject: [PATCH 159/197] (minor) Modify DrinfeldModule.invert docstring --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 056e63aa145..02a42cd224c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1082,9 +1082,8 @@ def height(self): def invert(self, ore_pol): r""" - Return the preimage of the input under the Drinfeld module; - raise an exception if the input is not in the image of the - Drinfeld module. + Return the preimage of the input under the Drinfeld module, if it + exists. INPUT: From b8e54956aff8e602e04c8723fcec2a441bccd687 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Mon, 6 Feb 2023 21:39:49 +0100 Subject: [PATCH 160/197] (minor) Typo --- src/sage/rings/function_field/drinfeld_modules/action.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 826ea735f77..8c1e68fcd6f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -170,7 +170,7 @@ def _repr_(self): def drinfeld_module(self): r""" - Return the Drinfeld module defining to the action. + Return the Drinfeld module defining the action. OUTPUT: a Drinfeld module From ef94f98219a71e9cfe209e06d06b7083ad51ceb7 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Mon, 6 Feb 2023 21:55:19 +0100 Subject: [PATCH 161/197] Proofread DrinfeldModuleAction --- .../function_field/drinfeld_modules/action.py | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 8c1e68fcd6f..74399316c92 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -26,20 +26,26 @@ class DrinfeldModuleAction(Action): r""" - This class represents the module action induced by a Drinfeld + This class implements the module action induced by a Drinfeld module. - Let `\phi` be a Drinfeld module with base `\gamma: \mathbb{F}_q[T] - \to K`. Let `L/K` be a field extension, let `x \in L`, let `a` be a - function ring element; the action is defined as `(a, x) \mapsto - \phi_a(x)`. + Let `\phi` be a Drinfeld module over a field `K` and let `L/K` be a + field extension. Let `x \in L` and let `a` be a function ring + element; the action is defined as `(a, x) \mapsto \phi_a(x)`. .. NOTE:: In this implementation, `L` is `K`. - The user should never explicitly instantiate the class - `DrinfeldModuleAction`. + .. NOTE:: + + The user should never explicitly instantiate the class + `DrinfeldModuleAction`. + + .. WARNING:: + + This class may be replaced later on. See issues #34833 and + #34834. INPUT: the Drinfeld module From 9c34fe1657d3ca40c19931130ce6b1e791c0675c Mon Sep 17 00:00:00 2001 From: ymusleh <44390016+ymusleh@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:11:41 -0500 Subject: [PATCH 162/197] Major typo fix --- .../function_field/drinfeld_modules/finite_drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 016a4ee6a12..d4b84500ef6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -378,7 +378,7 @@ def is_ordinary(self): trace. A *supersingular* rank two finite Drinfeld module is a Drinfeld module that is not ordinary. - A rnak two Drinfeld module is *ordinary* if and only if it is + A rank two Drinfeld module is *ordinary* if and only if it is note supersingular; see :meth:`is_supersingular`. OUTPUT: a boolean From 6601038cdf3369e5f3b1b4372b238b080a35f8c7 Mon Sep 17 00:00:00 2001 From: ymusleh <44390016+ymusleh@users.noreply.github.com> Date: Mon, 6 Feb 2023 16:27:43 -0500 Subject: [PATCH 163/197] More extremely critical changes --- .../drinfeld_modules/finite_drinfeld_module.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index d4b84500ef6..c36c0e23ad6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -117,7 +117,7 @@ def __init__(self, gen, category): - ``function_ring`` -- a univariate polynomial ring whose base is a finite field - - ``gen`` -- the generator of the Drinfeld module; as a list of + - ``gen`` -- the generator of the Drinfeld module as a list of coefficients or an Ore polynomial - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring gen @@ -178,7 +178,7 @@ def frobenius_endomorphism(self): def frobenius_charpoly(self, var='T'): r""" Return the characteristic polynomial of the Frobenius - endomorphism, if the rank is two; raise a NotImplementedError + endomorphism if the rank is two. Raise a NotImplementedError otherwise. Let `\mathbb{F}_q` be the base ring of the function ring. The @@ -191,7 +191,7 @@ def frobenius_charpoly(self, var='T'): bivariate polynomial. Let `\chi = X^2 - A(T)X + B(T)` be the characteristic polynomial - of the Frobenius endomorphism, let `t^n` be the Ore polynomial + of the Frobenius endomorphism, and let `t^n` be the Ore polynomial that defines the Frobenius endomorphism of `\phi`; by definition, `n` is the degree over `\mathbb{F}_q` of the base codomain. We have `\chi(t^n)(\phi(T)) = t^{2n} - \phi_A t^n + @@ -245,12 +245,12 @@ def frobenius_charpoly(self, var='T'): See [MS2019]_, Section 4. See docstrings of methods :meth:`frobenius_norm` and - :meth:`frobenius_trace` for furthere details on the + :meth:`frobenius_trace` for further details on the computation of the norm and of the trace. """ self._check_rank_two() A = self._function_ring # Fq[T] - S = PolynomialRing(A, name=var) # Fq[T][T] + S = PolynomialRing(A, name=var) # Fq[T][X] # Does not work when Fq is not a prime field... # chi = self._gen.reduced_charpoly() # return -chi(A.gen(), S.gen()) @@ -259,7 +259,7 @@ def frobenius_charpoly(self, var='T'): def frobenius_norm(self): r""" Return Frobenius norm of the Drinfeld module, if the rank is - two; raise a NotImplementedError otherwise. + two, raise a NotImplementedError otherwise. Let `\mathbb{F}_q[T]` be the function ring, write `\chi = X^2 - A(T)X + B(T) \in \mathbb{F}_q[T][X]` for the characteristic @@ -316,7 +316,7 @@ def frobenius_trace(self): two; raise a NotImplementedError otherwise. Let `\mathbb{F}_q[T]` be the function ring, write `\chi = T^2 - - A(X)T + B(X) \in \mathbb{F}_q[T][T]` for the characteristic + A(X)T + B(X) \in \mathbb{F}_q[T][X]` for the characteristic polynomial of the Frobenius endomorphism. The *Frobenius norm* is defined as the polynomial `B(T) \in \mathbb{F}_q[T]`. @@ -379,7 +379,7 @@ def is_ordinary(self): Drinfeld module that is not ordinary. A rank two Drinfeld module is *ordinary* if and only if it is - note supersingular; see :meth:`is_supersingular`. + not supersingular; see :meth:`is_supersingular`. OUTPUT: a boolean From b4371c84b2e5914c129ae39874bfaf27df1f2178 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Mon, 6 Feb 2023 22:40:05 +0100 Subject: [PATCH 164/197] Manage merge conflict --- src/sage/categories/drinfeld_modules.py | 21 ++-- .../function_field/drinfeld_modules/action.py | 19 +-- .../drinfeld_modules/drinfeld_module.py | 111 ++++++------------ .../finite_drinfeld_module.py | 29 ++--- .../drinfeld_modules/morphism.py | 10 +- 5 files changed, 73 insertions(+), 117 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index c8b6252ae29..e1f74107688 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -36,11 +36,11 @@ class DrinfeldModules(Category_over_base_ring): Let `\mathbb{F}_q[T]` be a polynomial ring with coefficients in a finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring morphism `\gamma: \mathbb{F}_q[T] \to K`. We say that the field `K` - is an `\mathbb{F}_q[T]`-field, so that the *base of the category* is - defined as the `\mathbb{F}_q[T]`-field *K*. The base uniquely - defines the category, and we also refer to it as the *base ring* or - *base field*. The *base morphism* is the morphism `\gamma: - \mathbb{F}_q[T] \to K`. + is an `\mathbb{F}_q[T]`-field, so that the *base field of the + category* is defined as the `\mathbb{F}_q[T]`-field *K*. The base + field uniquely defines the category, and we also refer to it as its + *base*. The *base morphism* is the morphism `\gamma: \mathbb{F}_q[T] + \to K`. .. NOTE:: @@ -57,7 +57,7 @@ class DrinfeldModules(Category_over_base_ring): coefficient of the category is the image of `T` under the base morphism. - INPUT: the base ring morphism + INPUT: the base field .. RUBRIC:: Construction @@ -79,13 +79,10 @@ class DrinfeldModules(Category_over_base_ring): .. RUBRIC:: Properties of the category - The base ring is retrieved using the method :meth:`base` or - :meth:`base_ring`:: + The base field is retrieved using the method :meth:`base`. sage: cat.base() Finite Field in z of size 11^4 over its base - sage: cat.base_ring() - Finite Field in z of size 11^4 over its base Equivalently, one can use :meth:`base_morphism` to retrieve the base morphism:: @@ -114,7 +111,7 @@ class DrinfeldModules(Category_over_base_ring): sage: cat.base_morphism()(cat.characteristic()) 0 - The base ring, base morphism, function ring and Ore polynomial ring + The base field, base morphism, function ring and Ore polynomial ring are the same for the category and its objects:: sage: cat.base() is phi.base() @@ -212,7 +209,7 @@ def __init__(self, base_field, name='t'): INPUT: - - ``base_ring`` -- the base field, which is a ring extension + - ``base_field`` -- the base field, which is a ring extension over a base - ``name`` (default: `'t'`) -- the name of the Ore polynomial variable diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 74399316c92..6fabd091805 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -29,9 +29,10 @@ class DrinfeldModuleAction(Action): This class implements the module action induced by a Drinfeld module. - Let `\phi` be a Drinfeld module over a field `K` and let `L/K` be a - field extension. Let `x \in L` and let `a` be a function ring - element; the action is defined as `(a, x) \mapsto \phi_a(x)`. + Let `\phi` be a Drinfeld `\mathbb{F}_q[T]`-module over a field `K` + and let `L/K` be a field extension. Let `x \in L` and let `a` be a + function ring element; the action is defined as `(a, x) \mapsto + \phi_a(x)`. .. NOTE:: @@ -93,14 +94,14 @@ def __init__(self, drinfeld_module): sage: action = phi.action() sage: action._drinfeld_module is phi True - sage: action._base_ring is phi.base() + sage: action._base is phi.base() True """ if not isinstance(drinfeld_module, DrinfeldModule): raise TypeError('input must be a DrinfeldModule') self._drinfeld_module = drinfeld_module - self._base_ring = drinfeld_module.base() - super().__init__(drinfeld_module.function_ring(), self._base_ring) + self._base = drinfeld_module.base() + super().__init__(drinfeld_module.function_ring(), self._base) def _act_(self, pol, x): r""" @@ -131,7 +132,7 @@ def _act_(self, pol, x): """ if pol not in self._drinfeld_module.function_ring(): raise TypeError('first input must be in the function ring') - if x not in self._base_ring: + if x not in self._base: raise TypeError('second input must be in the field acted upon') return self._drinfeld_module(pol)(x) @@ -152,7 +153,7 @@ def _latex_(self): \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} T \mapsto t^{3} + z\text{{ }over{ }base{ }}\Bold{F}_{11^{2}} """ return f'\\text{{Action{{ }}on{{ }}}}' \ - f'{latex(self._base_ring)}\\text{{{{ }}' \ + f'{latex(self._base)}\\text{{{{ }}' \ f'induced{{ }}by{{ }}}}{latex(self._drinfeld_module)}' def _repr_(self): @@ -171,7 +172,7 @@ def _repr_(self): sage: action Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 11^2 over its base """ - return f'Action on {self._base_ring} induced by ' \ + return f'Action on {self._base} induced by ' \ f'{self._drinfeld_module}' def drinfeld_module(self): diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 02a42cd224c..ef16fb1bd98 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -45,25 +45,19 @@ class DrinfeldModule(Parent, UniqueRepresentation): Let `\mathbb{F}_q[T]` be a polynomial ring with coefficients in a finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring morphism `\gamma: \mathbb{F}_q[T] \to K`. We say that the field `K` - is an `\mathbb{F}_q[T]`-field, so that the *base of the Drinfeld - module* is defined as the `\mathbb{F}_q[T]`-field *K*. The base - uniquely defines the category, and we also refer to it as the *base - ring* or *base field*. The *base morphism* is the morphism `\gamma: + is an `\mathbb{F}_q[T]`-field, so that the *base field of the + Drinfeld module* is defined as the `\mathbb{F}_q[T]`-field *K*. This + field is the `base` of the category of Drinfeld modules, which it + uniquely defines. The *base morphism* is the morphism `\gamma: \mathbb{F}_q[T] \to K`. - .. NOTE:: - - Equivalently, the base of the Drinfeld module could be defined as the - base morphism `\gamma: \mathbb{F}_q[T] \to K`. - - .. NOTE:: See also :class:`sage.categories.drinfeld_modules`. The monic polynomial that generates the kernel of the base morphism is called the `\mathbb{F}_q[T]`-characteristic of the - `\mathbb{F}_q[T]`-field `K`. It cal also be referred to as the + `\mathbb{F}_q[T]`-field `K`. It can also be referred to as the function-field characteristic of `K`. Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in @@ -111,7 +105,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): constant coefficient of `\phi_T`. The `\mathbb{F}_q[T]`-characteristic of the `\mathbb{F}_q[T]`-field `K` can also be referred to as its function ring-characteristic. - Finally, `K` is just referred to as the codomain base. Classical references on Drinfeld modules include [Gos1998]_, [Rosen2002]_, [VS06]_ and [Gek1991]_. @@ -128,8 +121,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): INPUT: - - ``function_ring`` -- a univariate polynomial ring whose base is a - finite field + - ``function_ring`` -- a univariate polynomial ring whose base field + is a finite field - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring @@ -147,11 +140,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi Drinfeld module defined by T |--> t^2 + t + z over Finite Field in z of size 3^12 over its base - Note that the definition of the base morphism is implicit; it is - defined as the `\mathbb{F}_q`-algebra morphism defined from - `\mathbb{F}_q[T]` to the base ring, and the base ring is a ring - extension over the base morphism whose underlying ring is the - compositum of all the parents of the coefficients. + Note that the definition of the base field is implicit; it is + automatically defined as the compositum of all the parents of the + coefficients. The above Drinfeld module is finite; it can also be infinite:: @@ -216,12 +207,11 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: cat.object([z, 0, 0, 1]) Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 3^12 over its base - .. RUBRIC:: The base ring of a Drinfeld module + .. RUBRIC:: The base field of a Drinfeld module - The base ring of the Drinfeld module is retrieved using - :meth:`base_ring` or :meth:`base`:: + The base field of the Drinfeld module is retrieved using :meth:`base`:: - sage: phi.base_ring() + sage: phi.base() Finite Field in z of size 3^12 over its base The base morphism is retrieved using :meth:`base_morphism`:: @@ -232,11 +222,11 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 over its base Defn: T |--> z - Note that the base ring is *not* the field `K`. Rather, it is a ring - extension (see :class:`sage.rings.ring_extension.RingExtension`) - whose underlying ring is `K` and whose base is the base morphism:: + Note that the base field is *not* the field `K`. Rather, it is a ring + extension (see :class:`sage.rings.ring_extension.RingExtension`) whose + underlying ring is `K` and whose base is the base morphism:: - sage: phi.base_ring() is K + sage: phi.base() is K False .. RUBRIC:: Getters @@ -474,7 +464,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: DrinfeldModule(A, [z, QQ(1)]) Traceback (most recent call last): ... - ValueError: function ring base must coerce into base ring + ValueError: function ring base must coerce into base field :: @@ -484,7 +474,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: DrinfeldModule(A, [1, QQ(1)]) Traceback (most recent call last): ... - ValueError: function ring base must coerce into base ring + ValueError: function ring base must coerce into base field The function ring must be an univariate polynomial ring whose base is a finite field:: @@ -529,7 +519,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi = DrinfeldModule(A, [1, 1]) Traceback (most recent call last): ... - ValueError: function ring base must coerce into base ring + ValueError: function ring base must coerce into base field :: @@ -545,40 +535,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: A. = Fq[] sage: K = Frac(Fq) sage: phi = DrinfeldModule(A, [Fq.gen(), K(1)]) - sage: phi.base().codomain() is K - True - - :: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: k = Frac(Fq) - sage: kS. = k[] - sage: K = k.extension(S^3 + S + 1) - sage: phi = DrinfeldModule(A, [Fq.gen(), K.gen()]) - sage: phi.base().codomain() is K - True - - In particular, note that the field `K` may not be the smallest field - of definition of the coefficients:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: k = Frac(Fq) - sage: kS. = k[] - sage: K = k.extension(S^3 + S + 1) - sage: phi = DrinfeldModule(A, [K(k.gen()), 1]) - sage: phi.base().codomain() is K - True - - :: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K = Fq.extension(2) - sage: phi = DrinfeldModule(A, [Fq.gen(), K(Fq.gen())]) - sage: phi.base().codomain() is K - True + sage: phi.base_morphism().codomain() is K """ @staticmethod @@ -638,36 +595,36 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): if isinstance(gen, OrePolynomial): ore_polring = gen.parent() # Base ring without morphism structure: - base_ring_noext = ore_polring.base() + base_field_noext = ore_polring.base() name = ore_polring.variable_name() # `gen` is a list of coefficients (function_ring = Fq[T]): elif isinstance(gen, (list, tuple)): ore_polring = None # Base ring without morphism structure: - base_ring_noext = Sequence(gen).universe() + base_field_noext = Sequence(gen).universe() else: raise TypeError('generator must be list of coefficients or Ore ' 'polynomial') # Constant coefficient must be nonzero: if gen[0].is_zero(): raise ValueError('constant coefficient must be nonzero') - # The coefficients are in a base ring that has coercion from Fq: - if not (hasattr(base_ring_noext, 'has_coerce_map_from') and - base_ring_noext.has_coerce_map_from(function_ring.base_ring())): - raise ValueError('function ring base must coerce into base ring') + # The coefficients are in a base field that has coercion from Fq: + if not (hasattr(base_field_noext, 'has_coerce_map_from') and + base_field_noext.has_coerce_map_from(function_ring.base_field())): + raise ValueError('function ring base must coerce into base field') # Check LaTeX name if latexname is not None and type(latexname) is not str: raise ValueError('LaTeX name should be a string') # Build the category - if isinstance(base_ring_noext, RingExtension_generic): - base_ring = base_ring_noext + if isinstance(base_field_noext, RingExtension_generic): + base_field = base_field_noext else: - base_morphism = Hom(function_ring, base_ring_noext)(gen[0]) - base_ring = base_ring_noext.over(base_morphism) + base_morphism = Hom(function_ring, base_field_noext)(gen[0]) + base_field = base_field_noext.over(base_morphism) - category = DrinfeldModules(base_ring, name=name) + category = DrinfeldModules(base_field, name=name) # Check gen as Ore polynomial ore_polring = category.ore_polring() # Sanity cast @@ -676,7 +633,7 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): raise ValueError('generator must have positive degree') # Instantiate the appropriate class - if base_ring.is_finite(): + if base_field.is_finite(): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return FiniteDrinfeldModule(gen, category) return cls.__classcall__(cls, gen, category) @@ -1202,7 +1159,7 @@ def j_invariant(self): Assume the rank is two. Write the generator `\phi_T = \omega + g\tau + \Delta\tau^2`. The j-invariant is defined by - `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base ring + `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base field of the function ring. In our case, this field is always finite. OUTPUT: an element in the base codomain diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index c36c0e23ad6..63e56791d29 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -29,9 +29,9 @@ class FiniteDrinfeldModule(DrinfeldModule): r""" This class represents a finite Drinfeld module. - A *finite Drinfeld module* is a Drinfeld module whose base codomain - is a finite field. In this case, the function field characteristic - is a prime ideal. + A *finite Drinfeld module* is a Drinfeld module whose base field is + finite. In this case, the function field characteristic is a prime + ideal. For general definitions and help on Drinfeld modules, see class :class:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule`. @@ -148,7 +148,7 @@ def frobenius_endomorphism(self): Return the Frobenius endomorphism of the Drinfeld module as a morphism object. - Let `q` be the order of the base ring of the function ring. The + Let `q` be the order of the base field of the function ring. The *Frobenius endomorphism* is defined as the endomorphism whose defining Ore polynomial is `t^q`. @@ -181,7 +181,7 @@ def frobenius_charpoly(self, var='T'): endomorphism if the rank is two. Raise a NotImplementedError otherwise. - Let `\mathbb{F}_q` be the base ring of the function ring. The + Let `\mathbb{F}_q` be the base field of the function ring. The *characteristic polynomial `\chi` of the Frobenius endomorphism* is defined in [Gek1991]_. An important feature of this polynomial is that it is a monic univariate polynomial with @@ -193,9 +193,10 @@ def frobenius_charpoly(self, var='T'): Let `\chi = X^2 - A(T)X + B(T)` be the characteristic polynomial of the Frobenius endomorphism, and let `t^n` be the Ore polynomial that defines the Frobenius endomorphism of `\phi`; by - definition, `n` is the degree over `\mathbb{F}_q` of the base - codomain. We have `\chi(t^n)(\phi(T)) = t^{2n} - \phi_A t^n + - \phi_B = 0`, with `\deg(A) \leq \frac{n}{2}` and `\deg(B) = n`. + definition, `n` is the degree over of the base field over + `\mathbb{F}_q`. We have `\chi(t^n)(\phi(T)) = t^{2n} - \phi_A + t^n + \phi_B = 0`, with `\deg(A) \leq \frac{n}{2}` and `\deg(B) + = n`. Note that the *Frobenius trace* is defined as `A(T)` and the *Frobenius norm* is defined as `B(T)`. @@ -232,7 +233,7 @@ def frobenius_charpoly(self, var='T'): :: - sage: n = 2 # Degree over Fq of the base codomain + sage: n = 2 # Degree of the base field over Fq sage: trace.degree() <= n/2 True sage: norm.degree() == n @@ -266,8 +267,8 @@ def frobenius_norm(self): polynomial of the Frobenius endomorphism. The *Frobenius norm* is defined as the polynomial `B(T) \in \mathbb{F}_q[T]`. - Let `n` be the degree over `\mathbb{F}_q` of the base codomain. - Then the Frobenius norm has degree `n`. + Let `n` be the degree of the base field over `\mathbb{F}_q` Then the + Frobenius norm has degree `n`. OUTPUT: an element in the function ring @@ -283,7 +284,7 @@ def frobenius_norm(self): :: - sage: n = 2 # Degree over Fq of the base codomain + sage: n = 2 # Degree of the base field over Fq sage: B.degree() == n True @@ -298,7 +299,7 @@ def frobenius_norm(self): Gekeler, given in [MS2019]_, Section 4, Proposition 3. """ self._check_rank_two() - L = self._base.codomain().over(self._Fq) + L = self._base.over(self._Fq) # Notations from Schost-Musleh: if self._frobenius_norm is None: n = L.degree_over(self._Fq) @@ -362,7 +363,7 @@ def frobenius_trace(self): self._check_rank_two() # Notations from Schost-Musleh: if self._frobenius_trace is None: - n = self._base.codomain().over(self._Fq).degree_over(self._Fq) + n = self._base.over(self._Fq).degree_over(self._Fq) B = self.frobenius_norm() t = self.ore_polring().gen() self._frobenius_trace = self.invert(t**n + (self(B) // t**n)) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 3ff5d40f7b7..18a41c548aa 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -29,11 +29,11 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, r""" This class represents a Drinfeld module morphism. - Let `\phi, \psi` be two Drinfeld modules with base `\gamma: \mathbb{F}_q[T] - \to K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore - polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a - \in \mathbb{F}_q[T]`. In our case, this is equivalent to `f \phi_T = \psi_T - f`. An *isogeny* is a nonzero morphism. + Let `\phi, \psi` be two Drinfeld `\mathbb{F}_q[T]`-modules over a + field `K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an + Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for + every `a \in \mathbb{F}_q[T]`. In our case, this is equivalent to `f + \phi_T = \psi_T f`. An *isogeny* is a nonzero morphism. To create a morphism object, the user should never explicitly instantiate :class:`DrinfeldModuleMorphism`, but rather call the parent From 5e3182eb1d5cc3255263d14188272eb30c4cf9b8 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Mon, 6 Feb 2023 22:44:03 +0100 Subject: [PATCH 165/197] Adress David's suggestions for DrinfeldModuleHomset --- src/sage/rings/function_field/drinfeld_modules/homset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 812301bdbdf..c90d5535340 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -218,7 +218,7 @@ def _repr_(self): def __contains__(self, x): r""" - Implement the ``in`` operator for the homset; return ``True`` if + Implement the ``in`` operator for the homset; return ``True`` whether the input defines a morphism in the homset. INPUT: @@ -274,7 +274,7 @@ def __contains__(self, x): def _element_constructor_(self, *args, **kwds): r""" - Return the Drinfeld module morphism defined by the input Ore + Return the Drinfeld module morphism defined by the given Ore polynomial. INPUT: an Ore polynomial From 09f27ab8c647af53528fdecee6d9650be09cd00c Mon Sep 17 00:00:00 2001 From: ymusleh <44390016+ymusleh@users.noreply.github.com> Date: Mon, 6 Feb 2023 17:31:09 -0500 Subject: [PATCH 166/197] Huge fix people from paris wouldn't understand Removed stray apostrophe and made punctuation a bit more consistent. --- src/sage/categories/drinfeld_modules.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index e1f74107688..735c4a37101 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -139,7 +139,7 @@ class DrinfeldModules(Category_over_base_ring): True Of course, the constant coefficient of the input must be the same as - the category':: + the category:: sage: cat.object([z, 1]) Traceback (most recent call last): @@ -280,7 +280,7 @@ def __init__(self, base_field, name='t'): def _latex_(self): r""" - Return a latex representation of the category + Return a latex representation of the category. OUTPUT: a string @@ -300,7 +300,7 @@ def _latex_(self): def _repr_(self): r""" - Return a string representation of the category + Return a string representation of the category. OUTPUT: a string @@ -359,7 +359,7 @@ def Endsets(self): def base_morphism(self): r""" - Return the base morphism of the category + Return the base morphism of the category. OUTPUT: a ring morphism From 1863590a8d7fb7307153669a17ba09ee4201cd49 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 7 Feb 2023 09:32:00 +0100 Subject: [PATCH 167/197] fix creation of the ring extension when the top ring is itself an extension --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- src/sage/rings/ring_extension.pyx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index ef16fb1bd98..1abe56fd306 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -610,7 +610,7 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): raise ValueError('constant coefficient must be nonzero') # The coefficients are in a base field that has coercion from Fq: if not (hasattr(base_field_noext, 'has_coerce_map_from') and - base_field_noext.has_coerce_map_from(function_ring.base_field())): + base_field_noext.has_coerce_map_from(function_ring.base_ring())): raise ValueError('function ring base must coerce into base field') # Check LaTeX name diff --git a/src/sage/rings/ring_extension.pyx b/src/sage/rings/ring_extension.pyx index b493f57bed9..791cd3d1de8 100644 --- a/src/sage/rings/ring_extension.pyx +++ b/src/sage/rings/ring_extension.pyx @@ -429,6 +429,7 @@ class RingExtensionFactory(UniqueFactory): else: use_generic_constructor = False is_backend_exposed = False + ring = (ring)._backend # We normalize other attributes if gens is not None: From 7cc1eb641a612008e688384a96b4f962a4511415 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 09:59:59 +0100 Subject: [PATCH 168/197] Refactor DrinfeldModule docstring (see details) - Use italics for definitions - Change the order of paragraphs in the begining --- .../drinfeld_modules/drinfeld_module.py | 85 +++++++++---------- 1 file changed, 41 insertions(+), 44 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index ef16fb1bd98..32a0aee4e31 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -40,32 +40,18 @@ class DrinfeldModule(Parent, UniqueRepresentation): r""" - This class represents a Drinfeld `\mathbb{F}_q[T]`-module. + This class implements Drinfeld `\mathbb{F}_q[T]`-modules. Let `\mathbb{F}_q[T]` be a polynomial ring with coefficients in a finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring - morphism `\gamma: \mathbb{F}_q[T] \to K`. We say that the field `K` - is an `\mathbb{F}_q[T]`-field, so that the *base field of the - Drinfeld module* is defined as the `\mathbb{F}_q[T]`-field *K*. This - field is the `base` of the category of Drinfeld modules, which it - uniquely defines. The *base morphism* is the morphism `\gamma: - \mathbb{F}_q[T] \to K`. - - .. NOTE:: - - See also :class:`sage.categories.drinfeld_modules`. - - The monic polynomial that generates the kernel of the base morphism - is called the `\mathbb{F}_q[T]`-characteristic of the - `\mathbb{F}_q[T]`-field `K`. It can also be referred to as the - function-field characteristic of `K`. - - Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in - `K` and Frobenius variable `\tau: x \mapsto x^q`. A Drinfeld - `\mathbb{F}_q[T]`-module over the `\mathbb{F}_q[T]`-field `K` is an - `\mathbb{F}_q`-algebra morphism `\phi: \mathbb{F}_q[T] \to - K\{\tau\}` such that: - + morphism `\gamma: \mathbb{F}_q[T] \to K`; we say that `K` is an + `\mathbb{F}_q[T]`*-field*. Let `K\{\tau\}` be the ring of Ore + polynomials with coefficients in `K`, whose multiplication is given + by the rule `\tau \lambda = \lambda^q \tau` for any `\lambda \in K`. + + A Drinfeld `\mathbb{F}_q[T]`-module over the `base + \mathbb{F}_q[T]`-field `K` is an `\mathbb{F}_q`-algebra morphism + `\phi: \mathbb{F}_q[T] \to K\{\tau\}` such that: 1. The image of `\phi` contains nonconstant Ore polynomials. 2. For every element `a` in the `\mathbb{F}_q[T]`, the constant coefficient `\phi(a)` is `\gamma(a)`. @@ -75,9 +61,21 @@ class DrinfeldModule(Parent, UniqueRepresentation): The Drinfeld `\mathbb{F}_q[T]`-module `\phi` is uniquely determined by the image `\phi_T` of `T` — this serves as input of the class. - A Drinfeld module is said to be finite if the field `K` is. Despite - an emphasis on this case, the base field can be any extension of - `\mathbb{F}_q`:: + .. NOTE:: + + See also :class:`sage.categories.drinfeld_modules`. + + The *base morphism* is the morphism `\gamma: \mathbb{F}_q[T] \to K`. + The monic polynomial that generates the kernel of `\gamma` is called + the `\mathbb{F}_q[T]`-*characteristic*, or *function-field + characteristic*, of the base field. We say that `\mathbb{F}_q[T]` is + the *function ring* of `\phi`; `K\{\tau\}` is the *Ore polynomial + ring* of `\phi`. Further, the *generator* of `\phi` is `\phi_T` and + its *constant coefficient* is the constant coefficient of `\phi_T`. + + A Drinfeld module is said to be *finite* if the field `K` is. + Despite an emphasis on this case, the base field can be any + extension of `\mathbb{F}_q`:: sage: Fq = GF(25) sage: A. = Fq[] @@ -99,13 +97,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): Finite Drinfeld modules are implemented in the class :class:`sage.rings.function_field.drinfeld_modules.finite_drinfeld_module`. - We say that `\mathbb{F}_q[T]` is the function ring of `\phi`; - `K\{\tau\}` is the Ore polynomial ring of `\phi`. Further, the - generator of `\phi` is `\phi_T` and its constant coefficient is the - constant coefficient of `\phi_T`. The - `\mathbb{F}_q[T]`-characteristic of the `\mathbb{F}_q[T]`-field `K` - can also be referred to as its function ring-characteristic. - Classical references on Drinfeld modules include [Gos1998]_, [Rosen2002]_, [VS06]_ and [Gek1991]_. @@ -140,9 +131,11 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi Drinfeld module defined by T |--> t^2 + t + z over Finite Field in z of size 3^12 over its base - Note that the definition of the base field is implicit; it is - automatically defined as the compositum of all the parents of the - coefficients. + .. NOTE:: + + Note that the definition of the base field is implicit; it is + automatically defined as the compositum of all the parents of + the coefficients. The above Drinfeld module is finite; it can also be infinite:: @@ -166,7 +159,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): regular Ore polynomials:: sage: ore_polring = phi.ore_polring() - sage: t = phi.ore_polring().gen() + sage: t = ore_polring.gen() sage: rho_T = z + t^3 sage: rho = DrinfeldModule(A, rho_T) sage: rho @@ -185,9 +178,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): One can give a LaTeX name to be used for LaTeX representation:: - sage: sigma = DrinfeldModule(A, [z, 1, 1], latexname='\phi') + sage: sigma = DrinfeldModule(A, [z, 1, 1], latexname='\sigma') sage: latex(sigma) - \phi + \sigma .. RUBRIC:: The category of Drinfeld modules @@ -299,10 +292,10 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: Morphisms and isogenies - A morphism of Drinfeld modules `\phi \to \psi` is an Ore polynomial - `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a` in - the function ring. In our case, this is equivalent to `f \phi_T = - \psi_T f`. An isogeny is a nonzero morphism. + A *morphism* of Drinfeld modules `\phi \to \psi` is an Ore + polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for + every `a` in the function ring. In our case, this is equivalent to + `f \phi_T = \psi_T f`. An *isogeny* is a nonzero morphism. Use the ``in`` syntax to test if an Ore polynomial defines a morphism:: @@ -421,6 +414,11 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: action(A.random_element(), 0) 0 + .. WARNING:: + + The class ``DrinfeldModuleAction`` may be replaced later on. See + issues #34833 and #34834. + .. RUBRIC:: Inverting the Drinfeld module The morphism that defines a Drinfeld module is injective. Given an @@ -511,7 +509,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): ... ValueError: constant coefficient must equal that of the category - :: sage: Fq = K = GF(2) From 63fc7d9ff4f5dffaf8e7c7e6a208bd516bbe0da0 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 10:15:43 +0100 Subject: [PATCH 169/197] Various changes to doc (see details) - Change first sentences of doctests - Refactor DrinfeldModules doctest according to DrinfeldModule --- src/sage/categories/drinfeld_modules.py | 32 +++++++++---------- .../function_field/drinfeld_modules/action.py | 2 +- .../drinfeld_modules/drinfeld_module.py | 4 +-- .../finite_drinfeld_module.py | 2 +- .../function_field/drinfeld_modules/homset.py | 4 +-- .../drinfeld_modules/morphism.py | 6 ++-- 6 files changed, 24 insertions(+), 26 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 735c4a37101..de0a4200b1c 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -30,32 +30,30 @@ class DrinfeldModules(Category_over_base_ring): r""" - This class represents the category of Drinfeld modules on a given - base. + This class implements the category of Drinfeld + `\mathbb{F}_q[T]`-modules on a given base field. Let `\mathbb{F}_q[T]` be a polynomial ring with coefficients in a finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring - morphism `\gamma: \mathbb{F}_q[T] \to K`. We say that the field `K` - is an `\mathbb{F}_q[T]`-field, so that the *base field of the - category* is defined as the `\mathbb{F}_q[T]`-field *K*. The base - field uniquely defines the category, and we also refer to it as its - *base*. The *base morphism* is the morphism `\gamma: \mathbb{F}_q[T] - \to K`. + morphism `\gamma: \mathbb{F}_q[T] \to K`; we say that `K` is an + `\mathbb{F}_q[T]`*-field*. Let `K\{\tau\}` be the ring of Ore + polynomials with coefficients in `K`, whose multiplication is given + by the rule `\tau \lambda = \lambda^q \tau` for any `\lambda \in K`. + + We call `K` the *base field* of the category, and `\gamma` its *base + morphism*. .. NOTE:: Equivalently, the base of the category could be defined as the base morphism `\gamma: \mathbb{F}_q[T] \to K`. - The monic polynomial that generates the kernel of the base morphism - is called the `\mathbb{F}_q[T]`-characteristic of the - `\mathbb{F}_q[T]`-field `K`. It can also be referred to as the - function-field characteristic of `K`. - - We say that `\mathbb{F}_q[T]` is the function ring of the category; - `K\{\tau\}` is the Ore polynomial ring of the category. The constant - coefficient of the category is the image of `T` under the base - morphism. + The monic polynomial that generates the kernel of `\gamma` is called + the `\mathbb{F}_q[T]`-*characteristic*, or *function-field + characteristic*, of the base field. We say that `\mathbb{F}_q[T]` is + the *function ring* of the category; `K\{\tau\}` is the *Ore + polynomial ring*. The constant coefficient of the category is the + image of `T` under the base morphism. INPUT: the base field diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 6fabd091805..be93b643526 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -27,7 +27,7 @@ class DrinfeldModuleAction(Action): r""" This class implements the module action induced by a Drinfeld - module. + `\mathbb{F}_q[T]`-module. Let `\phi` be a Drinfeld `\mathbb{F}_q[T]`-module over a field `K` and let `L/K` be a field extension. Let `x \in L` and let `a` be a diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 32a0aee4e31..9c66244e225 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -70,8 +70,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): the `\mathbb{F}_q[T]`-*characteristic*, or *function-field characteristic*, of the base field. We say that `\mathbb{F}_q[T]` is the *function ring* of `\phi`; `K\{\tau\}` is the *Ore polynomial - ring* of `\phi`. Further, the *generator* of `\phi` is `\phi_T` and - its *constant coefficient* is the constant coefficient of `\phi_T`. + ring*. Further, the *generator* is `\phi_T` and the *constant + coefficient* is the constant coefficient of `\phi_T`. A Drinfeld module is said to be *finite* if the field `K` is. Despite an emphasis on this case, the base field can be any diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 63e56791d29..9e6f7a3b8c2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -27,7 +27,7 @@ class FiniteDrinfeldModule(DrinfeldModule): r""" - This class represents a finite Drinfeld module. + This class implements finite Drinfeld `\mathbb{F}_q[T]`-modules. A *finite Drinfeld module* is a Drinfeld module whose base field is finite. In this case, the function field characteristic is a prime diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index c90d5535340..c5391095631 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -28,8 +28,8 @@ class DrinfeldModuleHomset(Homset): r""" - This class represents the set of morphisms between two Drinfeld - modules. + This class implements the set of morphisms between two Drinfeld + `\mathbb{F}_q[T]`-modules. INPUT: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 18a41c548aa..b1b4d8b895d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -27,7 +27,7 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, metaclass=InheritComparisonClasscallMetaclass): r""" - This class represents a Drinfeld module morphism. + This class represents Drinfeld `\mathbb{F}_q[T]`-module morphisms. Let `\phi, \psi` be two Drinfeld `\mathbb{F}_q[T]`-modules over a field `K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an @@ -36,8 +36,8 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, \phi_T = \psi_T f`. An *isogeny* is a nonzero morphism. To create a morphism object, the user should never explicitly - instantiate :class:`DrinfeldModuleMorphism`, but rather call the parent - homset with the defining Ore polynomial:: + instantiate :class:`DrinfeldModuleMorphism`, but rather call the + parent homset with the defining Ore polynomial:: sage: Fq = GF(25) sage: A. = Fq[] From 53fab7d5d3c6d273c7b01f59b004f5a376966ed0 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 10:53:25 +0100 Subject: [PATCH 170/197] Handle NotImplementedError for is_subring --- src/sage/categories/drinfeld_modules.py | 9 ++++++--- .../function_field/drinfeld_modules/drinfeld_module.py | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index de0a4200b1c..b282fcaca61 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -270,10 +270,13 @@ def __init__(self, base_field, name='t'): # Create characteristic self._characteristic = None if K.is_finite(): - #FIXME: This minpoly is over Fp, not Fq + # FIXME: This minpoly is over Fp, not Fq self._characteristic = A(K(base_morphism(T)).minpoly()) - elif A.is_subring(K): - self._characteristic = Integer(0) + try: + if A.is_subring(K): + self._characteristic = Integer(0) + except NotImplementedError: + pass super().__init__(base=base_field) def _latex_(self): diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 9c66244e225..e45af9dcfad 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -607,7 +607,7 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): raise ValueError('constant coefficient must be nonzero') # The coefficients are in a base field that has coercion from Fq: if not (hasattr(base_field_noext, 'has_coerce_map_from') and - base_field_noext.has_coerce_map_from(function_ring.base_field())): + base_field_noext.has_coerce_map_from(function_ring.base_ring())): raise ValueError('function ring base must coerce into base field') # Check LaTeX name From 09b63288432fee81e4cd5c85819231095d85c48a Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 11:15:47 +0100 Subject: [PATCH 171/197] Adress David's suggestions for Morphism --- src/sage/categories/drinfeld_modules.py | 1 - .../drinfeld_modules/drinfeld_module.py | 3 ++ .../drinfeld_modules/morphism.py | 30 +++++++++---------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index b282fcaca61..e26dad7323e 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -182,7 +182,6 @@ class DrinfeldModules(Category_over_base_ring): :: - sage: base = 'I hate Rostropovitch' sage: cat = DrinfeldModules(base) # known bug (blankline) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index e45af9dcfad..0e570a0159b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -619,6 +619,9 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): base_field = base_field_noext else: base_morphism = Hom(function_ring, base_field_noext)(gen[0]) + natural_map = Hom(function_ring, base_field_noext).natural_map() + if base_morphism == natural_map: + base_morphism = natural_map base_field = base_field_noext.over(base_morphism) category = DrinfeldModules(base_field, name=name) diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index b1b4d8b895d..f99be1e93e9 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -29,8 +29,8 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, r""" This class represents Drinfeld `\mathbb{F}_q[T]`-module morphisms. - Let `\phi, \psi` be two Drinfeld `\mathbb{F}_q[T]`-modules over a - field `K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an + Let `\phi` and `\psi` be two Drinfeld `\mathbb{F}_q[T]`-modules over + a field `K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a \in \mathbb{F}_q[T]`. In our case, this is equivalent to `f \phi_T = \psi_T f`. An *isogeny* is a nonzero morphism. @@ -54,7 +54,7 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, To (gen): (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 - The input Ore polynomial must indeed define a morphism:: + The given Ore polynomial must indeed define a morphism:: sage: morphism = Hom(phi, psi)(1) Traceback (most recent call last): @@ -105,20 +105,18 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: End(phi)(0).parent() == End(phi) True - .. NOTE:: + For the sake of completeness, we explain how the user can directly + instantiate the class, even though this should never be explicitly + done:: - For the sake of completeness, we explain how the user can - directly instantiate the class, even though this should never be - explicitly done:: - - sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism - sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) - Drinfeld Module morphism: - From (gen): z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - To (gen): (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 - sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) is morphism - True + sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism + sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) + Drinfeld Module morphism: + From (gen): z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + To (gen): (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) is morphism + True """ @staticmethod From fc9b281d786e01a8f3f2a17bc354525c240cbb31 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 11:21:51 +0100 Subject: [PATCH 172/197] Try to cast the base morphism to a natural map, when possible --- .../function_field/drinfeld_modules/drinfeld_module.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 0e570a0159b..525eded371b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -619,9 +619,12 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): base_field = base_field_noext else: base_morphism = Hom(function_ring, base_field_noext)(gen[0]) - natural_map = Hom(function_ring, base_field_noext).natural_map() - if base_morphism == natural_map: - base_morphism = natural_map + try: + natural_map = Hom(function_ring, base_field_noext).natural_map() + if base_morphism == natural_map: + base_morphism = natural_map + except TypeError: + pass base_field = base_field_noext.over(base_morphism) category = DrinfeldModules(base_field, name=name) From bd430de47d403e3c80acacf101b926776ecb1ea8 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 11:35:45 +0100 Subject: [PATCH 173/197] Fix LaTeX name init --- .../drinfeld_modules/drinfeld_module.py | 11 +++++------ .../drinfeld_modules/finite_drinfeld_module.py | 6 ++++-- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 525eded371b..66293a18638 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -91,6 +91,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: K. = Frac(A) sage: psi = DrinfeldModule(A, [z, T+1]) sage: psi + Drinfeld module defined by T |--> (T + 1)*t + T over Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 7^2 over its base .. NOTE:: @@ -142,10 +143,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: L = Frac(A) sage: psi = DrinfeldModule(A, [L(T), 1, T^3 + T + 1]) sage: psi - Drinfeld module defined by T |--> (T^3 + T + 1)*t^2 + t + T over Ring morphism: - From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 - To: Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 - Defn: T |--> T + Drinfeld module defined by T |--> (T^3 + T + 1)*t^2 + t + T over Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 over its base :: @@ -179,6 +177,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): One can give a LaTeX name to be used for LaTeX representation:: sage: sigma = DrinfeldModule(A, [z, 1, 1], latexname='\sigma') + ... sage: latex(sigma) \sigma @@ -638,8 +637,8 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): # Instantiate the appropriate class if base_field.is_finite(): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule - return FiniteDrinfeldModule(gen, category) - return cls.__classcall__(cls, gen, category) + return FiniteDrinfeldModule(gen, category, latexname) + return cls.__classcall__(cls, gen, category, latexname) def __init__(self, gen, category, latexname=None): """ diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 9e6f7a3b8c2..5a9c7f77099 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -106,7 +106,7 @@ class FiniteDrinfeldModule(DrinfeldModule): False """ - def __init__(self, gen, category): + def __init__(self, gen, category, latexname=None): """ Initialize `self`. @@ -121,6 +121,8 @@ def __init__(self, gen, category): coefficients or an Ore polynomial - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring gen + - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld + module TESTS:: @@ -139,7 +141,7 @@ def __init__(self, gen, category): # added one to ensure that FiniteDrinfeldModule would always # have _frobenius_norm and _frobenius_trace attributes. - super().__init__(gen, category) + super().__init__(gen, category, latexname) self._frobenius_norm = None self._frobenius_trace = None From d8441f51685a7ff34db06ed9ff791bdcde523d7e Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 11:39:44 +0100 Subject: [PATCH 174/197] (fix) Stop using base.codomain --- src/sage/categories/drinfeld_modules.py | 6 +++--- .../function_field/drinfeld_modules/drinfeld_module.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index e26dad7323e..52eec420b13 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -413,7 +413,7 @@ def constant_coefficient(self): r""" Return the constant coefficient of the category. - OUTPUT: an element in the base codomain + OUTPUT: an element in the base field EXAMPLES:: @@ -604,7 +604,7 @@ def base_morphism(self): To: Finite Field in z12 of size 5^12 over its base Defn: T |--> 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - The base codomain can be infinite:: + The base field can be infinite:: sage: sigma = DrinfeldModule(A, [Frac(A).gen(), 1]) # todo: not tested sage: sigma.base_morphism() # todo: not tested @@ -660,7 +660,7 @@ def constant_coefficient(self): r""" Return the constant coefficient of the generator. - OUTPUT: an element in the base codomain + OUTPUT: an element in the base field EXAMPLES:: diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 66293a18638..b88066d4d3d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1108,7 +1108,7 @@ def invert(self, ore_pol): r = self.rank() if ore_pol not in self._ore_polring: raise TypeError('input must be an Ore polynomial') - if ore_pol in self._base.codomain(): + if ore_pol in self._base: return self._Fq(ore_pol) if deg % r != 0: raise ValueError('input must be in the image of the Drinfeld ' From cd96c6f7118c13daba560bf35deb46bb97977f2d Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 11:55:58 +0100 Subject: [PATCH 175/197] Fix some doctests --- .../drinfeld_modules/drinfeld_module.py | 19 +++++++++---------- .../finite_drinfeld_module.py | 2 +- 2 files changed, 10 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index b88066d4d3d..781a82c51fb 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -524,14 +524,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi = DrinfeldModule(A, [K(1), 1]) sage: isinstance(phi.ore_polring(), OrePolynomialRing) True - - Test that the base morphism is correct:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K = Frac(Fq) - sage: phi = DrinfeldModule(A, [Fq.gen(), K(1)]) - sage: phi.base_morphism().codomain() is K """ @staticmethod @@ -812,6 +804,7 @@ def _latex_(self): :: sage: psi = DrinfeldModule(A, [p_root, z12^3, z12^5], latexname='\psi') + ... sage: latex(psi) \psi @@ -1012,13 +1005,18 @@ def height(self): sage: phi.is_ordinary() True - sage: L = Frac(A) + :: + + sage: B. = Fq[] + sage: L = Frac(B) sage: phi = DrinfeldModule(A, [L(2), L(1)]) sage: phi.height() Traceback (most recent call last): ... ValueError: height is defined for prime function field characteristic + :: + sage: Fq = GF(343) sage: A. = Fq[] sage: K. = Fq.extension(2) @@ -1146,7 +1144,8 @@ def is_finite(self): sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.is_finite() True - sage: L = Frac(A) + sage: B. = Fq[] + sage: L = Frac(B) sage: psi = DrinfeldModule(A, [L(2), L(1)]) sage: psi.is_finite() False diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 5a9c7f77099..4fd8947fd5d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -173,7 +173,7 @@ def frobenius_endomorphism(self): """ t = self.ore_polring().gen() Fq = self._function_ring.base() - #FIXME + # FIXME deg = self._base.over(Fq).degree(Fq) return self._Hom_(self, category=self.category())(t**deg) From ba4fc7663b366ffcbc9194a454129c7ab1d76b06 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 15:54:05 +0100 Subject: [PATCH 176/197] Modify natural map creation --- .../drinfeld_modules/drinfeld_module.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 781a82c51fb..d012c02fde5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -606,16 +606,15 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): raise ValueError('LaTeX name should be a string') # Build the category + T = function_ring.gen() if isinstance(base_field_noext, RingExtension_generic): base_field = base_field_noext + elif base_field_noext.has_coerce_map_from(function_ring) \ + and T == gen[0]: + base_morphism = base_field_noext.coerce_map_from(function_ring) + base_field = base_field_noext.over(base_morphism) else: base_morphism = Hom(function_ring, base_field_noext)(gen[0]) - try: - natural_map = Hom(function_ring, base_field_noext).natural_map() - if base_morphism == natural_map: - base_morphism = natural_map - except TypeError: - pass base_field = base_field_noext.over(base_morphism) category = DrinfeldModules(base_field, name=name) From 7bc38004342d8925cda7fa37e3b2247e73923f25 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 16:58:48 +0100 Subject: [PATCH 177/197] Fix extensions over Fq --- src/sage/categories/drinfeld_modules.py | 3 +-- .../function_field/drinfeld_modules/finite_drinfeld_module.py | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 52eec420b13..1b79ad1d18e 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -269,8 +269,7 @@ def __init__(self, base_field, name='t'): # Create characteristic self._characteristic = None if K.is_finite(): - # FIXME: This minpoly is over Fp, not Fq - self._characteristic = A(K(base_morphism(T)).minpoly()) + self._characteristic = A(K.over(Fq)(base_morphism(T)).minpoly()) try: if A.is_subring(K): self._characteristic = Integer(0) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 4fd8947fd5d..1460d73e8c6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -173,7 +173,6 @@ def frobenius_endomorphism(self): """ t = self.ore_polring().gen() Fq = self._function_ring.base() - # FIXME deg = self._base.over(Fq).degree(Fq) return self._Hom_(self, category=self.category())(t**deg) From 2407ba051d0f757112a6add7843cc4443f4c6f07 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 17:36:10 +0100 Subject: [PATCH 178/197] Create base_over_constants_field DrinfeldModules method --- src/sage/categories/drinfeld_modules.py | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 1b79ad1d18e..87478f8bd00 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -275,6 +275,10 @@ def __init__(self, base_field, name='t'): self._characteristic = Integer(0) except NotImplementedError: pass + # Create base over constants field + i = A.coerce_map_from(Fq) + Fq_to_K = self._base_morphism * i + self._base_over_constants_field = base_field.over(Fq_to_K) super().__init__(base=base_field) def _latex_(self): @@ -380,6 +384,26 @@ def base_morphism(self): """ return self._base_morphism + def base_over_constants_field(self): + r""" + Return the base field, seen as an extension over the constants + field `\mathbb{F}_q`. + + OUTPUT: a ring extension + + EXAMPLES:: + + sage: Fq = GF(11) + sage: A. = Fq[] + sage: K. = Fq.extension(4) + sage: p_root = z^3 + 7*z^2 + 6*z + 10 + sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) + sage: cat = phi.category() + sage: cat.base_over_constants_field() + Field in z with defining polynomial x^4 + 8*x^2 + 10*x + 2 over its base + """ + return self._base_over_constants_field + def characteristic(self): r""" Return the function ring-characteristic. From 88505c2e9d53289200a1fedbd385623db1378b52 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 17:40:31 +0100 Subject: [PATCH 179/197] Create base_over_constants_field DrinfeldModule (singular) method --- src/sage/categories/drinfeld_modules.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 87478f8bd00..bbf45fbf5a0 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -634,6 +634,25 @@ def base_morphism(self): """ return self.category().base_morphism() + def base_over_constants_field(self): + r""" + Return the base field, seen as an extension over the constants + field `\mathbb{F}_q`. + + OUTPUT: a ring extension + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.base_over_constants_field() + Field in z12 with defining polynomial x^6 + (4*z2 + 3)*x^5 + x^4 + (3*z2 + 1)*x^3 + x^2 + (4*z2 + 1)*x + z2 over its base + """ + return self.category().base_over_constants_field() + def characteristic(self): r""" Return the function ring-characteristic. From 3cea9023aa6f34748b1dac6c27e0ffa940ce080f Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 17:41:51 +0100 Subject: [PATCH 180/197] (fix) Remove `not tested` comments from DrinfeldModules --- src/sage/categories/drinfeld_modules.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index bbf45fbf5a0..daad92e56ee 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -423,8 +423,8 @@ def characteristic(self): :: - sage: psi = DrinfeldModule(A, [Frac(A).gen(), 1]) # todo: not tested - sage: psi.category().characteristic() # todo: not tested + sage: psi = DrinfeldModule(A, [Frac(A).gen(), 1]) + sage: psi.category().characteristic() 0 """ if self._characteristic is None: @@ -602,8 +602,8 @@ def base(self): The base can be infinite:: - sage: sigma = DrinfeldModule(A, [Frac(A).gen(), 1]) # todo: not tested - sage: sigma.base() # todo: not tested + sage: sigma = DrinfeldModule(A, [Frac(A).gen(), 1]) + sage: sigma.base() Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 over its base """ return self.category().base() @@ -629,8 +629,8 @@ def base_morphism(self): The base field can be infinite:: - sage: sigma = DrinfeldModule(A, [Frac(A).gen(), 1]) # todo: not tested - sage: sigma.base_morphism() # todo: not tested + sage: sigma = DrinfeldModule(A, [Frac(A).gen(), 1]) + sage: sigma.base_morphism() """ return self.category().base_morphism() @@ -666,16 +666,17 @@ def characteristic(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi.characteristic() # todo: not tested + sage: phi.characteristic() T^2 + (4*z2 + 2)*T + 2 sage: phi.base_morphism()(phi.characteristic()) 0 :: - sage: L = Frac(A) # todo: not tested - sage: psi = DrinfeldModule(A, [L(1), 0, 0, L(1)]) # todo: not tested - sage: psi.characteristic() # todo: not tested + sage: B. = Fq[] + sage: L = Frac(B) + sage: psi = DrinfeldModule(A, [L(1), 0, 0, L(1)]) + sage: psi.characteristic() 0 """ return self.category().characteristic() From eb42cbcbe3aa4483f2d166404b0e00c408ff3cc8 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 18:12:26 +0100 Subject: [PATCH 181/197] Fix invert method and move it for *finite* Drinfeld modules --- src/sage/categories/drinfeld_modules.py | 256 +++++++++--------- .../drinfeld_modules/drinfeld_module.py | 105 ------- .../finite_drinfeld_module.py | 114 ++++++++ 3 files changed, 242 insertions(+), 233 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index daad92e56ee..a8f74cff940 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -634,120 +634,12 @@ def base_morphism(self): """ return self.category().base_morphism() - def base_over_constants_field(self): - r""" - Return the base field, seen as an extension over the constants - field `\mathbb{F}_q`. - - OUTPUT: a ring extension - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi.base_over_constants_field() - Field in z12 with defining polynomial x^6 + (4*z2 + 3)*x^5 + x^4 + (3*z2 + 1)*x^3 + x^2 + (4*z2 + 1)*x + z2 over its base - """ - return self.category().base_over_constants_field() - - def characteristic(self): - r""" - Return the function ring-characteristic. - - OUTPUT: a univariate polynomial ring - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi.characteristic() - T^2 + (4*z2 + 2)*T + 2 - sage: phi.base_morphism()(phi.characteristic()) - 0 - - :: - - sage: B. = Fq[] - sage: L = Frac(B) - sage: psi = DrinfeldModule(A, [L(1), 0, 0, L(1)]) - sage: psi.characteristic() - 0 - """ - return self.category().characteristic() - - def function_ring(self): - r""" - Return the function ring of the Drinfeld module. - - OUTPUT: a univariate polynomial ring - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi.function_ring() is A - True - """ - return self.category().function_ring() - - def constant_coefficient(self): - r""" - Return the constant coefficient of the generator. - - OUTPUT: an element in the base field - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi.constant_coefficient() == p_root - True - - Let `\mathbb{F}_q[T]` be the function ring, and let `\gamma` - the base of the Drinfeld module. The constant coefficient - equals `\gamma(T)`:: - - sage: cat = phi.category() - sage: base = cat.base() - sage: base(T) == phi.constant_coefficient() - True - - Naturally, two Drinfeld modules in the same category have the - same constant coefficient:: - - sage: t = phi.ore_polring().gen() - sage: psi = cat.object(phi.constant_coefficient() + t^3) - sage: psi - Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base - - Reciprocally, it is impossible to create two Drinfeld modules in - this category if they do not share the same constant - coefficient:: - - sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) - Traceback (most recent call last): - ... - ValueError: constant coefficient must equal that of the category - """ - return self.category().constant_coefficient() - - def ore_polring(self): + def base_over_constants_field(self): r""" - Return the Ore polynomial ring of the Drinfeld module. + Return the base field, seen as an extension over the constants + field `\mathbb{F}_q`. - OUTPUT: an Ore polynomial ring + OUTPUT: a ring extension EXAMPLES:: @@ -756,20 +648,128 @@ def ore_polring(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: ore_polring = phi.ore_polring() - sage: ore_polring - Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 - - The Ore polynomial ring can also be retrieved from the category - of the Drinfeld module:: - - sage: ore_polring is phi.category().ore_polring() - True - - The generator of the Drinfeld module is in the Ore polynomial - ring:: - - sage: phi(T) in ore_polring - True + sage: phi.base_over_constants_field() + Field in z12 with defining polynomial x^6 + (4*z2 + 3)*x^5 + x^4 + (3*z2 + 1)*x^3 + x^2 + (4*z2 + 1)*x + z2 over its base """ - return self.category().ore_polring() + return self.category().base_over_constants_field() + + def characteristic(self): + r""" + Return the function ring-characteristic. + + OUTPUT: a univariate polynomial ring + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.characteristic() + T^2 + (4*z2 + 2)*T + 2 + sage: phi.base_morphism()(phi.characteristic()) + 0 + + :: + + sage: B. = Fq[] + sage: L = Frac(B) + sage: psi = DrinfeldModule(A, [L(1), 0, 0, L(1)]) + sage: psi.characteristic() + 0 + """ + return self.category().characteristic() + + def function_ring(self): + r""" + Return the function ring of the Drinfeld module. + + OUTPUT: a univariate polynomial ring + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.function_ring() is A + True + """ + return self.category().function_ring() + + def constant_coefficient(self): + r""" + Return the constant coefficient of the generator. + + OUTPUT: an element in the base field + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.constant_coefficient() == p_root + True + + Let `\mathbb{F}_q[T]` be the function ring, and let `\gamma` + the base of the Drinfeld module. The constant coefficient + equals `\gamma(T)`:: + + sage: cat = phi.category() + sage: base = cat.base() + sage: base(T) == phi.constant_coefficient() + True + + Naturally, two Drinfeld modules in the same category have the + same constant coefficient:: + + sage: t = phi.ore_polring().gen() + sage: psi = cat.object(phi.constant_coefficient() + t^3) + sage: psi + Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base + + Reciprocally, it is impossible to create two Drinfeld modules in + this category if they do not share the same constant + coefficient:: + + sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) + Traceback (most recent call last): + ... + ValueError: constant coefficient must equal that of the category + """ + return self.category().constant_coefficient() + + def ore_polring(self): + r""" + Return the Ore polynomial ring of the Drinfeld module. + + OUTPUT: an Ore polynomial ring + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: ore_polring = phi.ore_polring() + sage: ore_polring + Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 + + The Ore polynomial ring can also be retrieved from the category + of the Drinfeld module:: + + sage: ore_polring is phi.category().ore_polring() + True + + The generator of the Drinfeld module is in the Ore polynomial + ring:: + + sage: phi(T) in ore_polring + True + """ + return self.category().ore_polring() diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index d012c02fde5..cf6743638a4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -26,9 +26,7 @@ from sage.categories.drinfeld_modules import DrinfeldModules from sage.categories.homset import Hom -from sage.matrix.constructor import Matrix from sage.misc.latex import latex -from sage.modules.free_module_element import vector from sage.rings.integer import Integer from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.polynomial_ring import PolynomialRing_general @@ -418,17 +416,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): The class ``DrinfeldModuleAction`` may be replaced later on. See issues #34833 and #34834. - .. RUBRIC:: Inverting the Drinfeld module - - The morphism that defines a Drinfeld module is injective. Given an - Ore polynomial that equals `\phi_a` for some function ring elelement - `a`, one can retrieve `a` (as a morphism, a Drinfeld module is - injective, see [Gos1998]_, cor. 4.5.2.):: - - sage: a = A.random_element() - sage: phi.invert(phi(a)) == a - True - TESTS: The generator must have positive degree:: @@ -1036,98 +1023,6 @@ def height(self): except NotImplementedError: raise NotImplementedError('height not implemented in this case') - def invert(self, ore_pol): - r""" - Return the preimage of the input under the Drinfeld module, if it - exists. - - INPUT: - - - ``ore_pol`` -- the Ore polynomial whose preimage we want to - compute - - OUTPUT: a function ring element - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: a = A.random_element() - sage: phi.invert(phi(a)) == a - True - sage: phi.invert(phi(T)) == T - True - sage: phi.invert(phi(Fq.gen())) == Fq.gen() - True - - When the input is not in the image of the Drinfeld module, an - exception is raised:: - - sage: t = phi.ore_polring().gen() - sage: phi.invert(t + 1) - Traceback (most recent call last): - ... - ValueError: input must be in the image of the Drinfeld module - sage: phi.invert(t^3 + t^2 + 1) - Traceback (most recent call last): - ... - ValueError: input must be in the image of the Drinfeld module - - ALGORITHM: - - The algorithm relies on the inversion of a linear algebra - system. See [MS2019]_, 3.2.5 for details. - - TESTS:: - - sage: a = A.random_element() - sage: cat = phi.category() - sage: phi_r1 = cat.random_object(1) - sage: phi_r1.invert(phi_r1(a)) == a - True - sage: phi_r2 = cat.random_object(2) - sage: phi_r2.invert(phi_r2(a)) == a - True - sage: phi_r3 = cat.random_object(3) - sage: phi_r3.invert(phi_r3(a)) == a - True - sage: phi_r4 = cat.random_object(4) - sage: phi_r4.invert(phi_r4(a)) == a - True - sage: phi_r5 = cat.random_object(5) - sage: phi_r5.invert(phi_r5(a)) == a - True - """ - deg = ore_pol.degree() - r = self.rank() - if ore_pol not in self._ore_polring: - raise TypeError('input must be an Ore polynomial') - if ore_pol in self._base: - return self._Fq(ore_pol) - if deg % r != 0: - raise ValueError('input must be in the image of the Drinfeld ' - 'module') - - k = deg // r - T = self._function_ring.gen() - mat_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] - for i in range(k+1): - phi_T_i = self(T**i) - for j in range(i+1): - mat_lines[j][i] = phi_T_i[r*j] - mat = Matrix(mat_lines) - vec = vector([list(ore_pol)[r*j] for j in range(k+1)]) - pre_image = self._function_ring(list((mat**(-1)) * vec)) - - if self(pre_image) == ore_pol: - return pre_image - else: - raise ValueError('input must be in the image of the Drinfeld ' - 'module') - def is_finite(self): r""" Return ``True`` whether the Drinfeld module is finite. diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 1460d73e8c6..a5041ec011a 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -21,6 +21,8 @@ # http://www.gnu.org/licenses/ # ***************************************************************************** +from sage.matrix.constructor import Matrix +from sage.modules.free_module_element import vector from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule @@ -104,6 +106,16 @@ class FiniteDrinfeldModule(DrinfeldModule): True sage: phi.is_supersingular() False + + .. RUBRIC:: Inverting the Drinfeld module + + The morphism that defines a Drinfeld module is injective (see + [Gos1998]_, cor. 4.5.2). If the Drinfeld module is finite, one can + retrieve preimages: + + sage: a = A.random_element() + sage: phi.invert(phi(a)) == a + True """ def __init__(self, gen, category, latexname=None): @@ -370,6 +382,108 @@ def frobenius_trace(self): self._frobenius_trace = self.invert(t**n + (self(B) // t**n)) return self._frobenius_trace + def invert(self, ore_pol): + r""" + Return the preimage of the input under the Drinfeld module, if it + exists. + + INPUT: + + - ``ore_pol`` -- the Ore polynomial whose preimage we want to + compute + + OUTPUT: a function ring element + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: a = A.random_element() + sage: phi.invert(phi(a)) == a + True + sage: phi.invert(phi(T)) == T + True + sage: phi.invert(phi(Fq.gen())) == Fq.gen() + True + + When the input is not in the image of the Drinfeld module, an + exception is raised:: + + sage: t = phi.ore_polring().gen() + sage: phi.invert(t + 1) + Traceback (most recent call last): + ... + ValueError: input must be in the image of the Drinfeld module + sage: phi.invert(t^3 + t^2 + 1) + Traceback (most recent call last): + ... + ValueError: input must be in the image of the Drinfeld module + + ALGORITHM: + + The algorithm relies on the inversion of a linear algebra + system. See [MS2019]_, 3.2.5 for details. + + TESTS:: + + sage: a = A.random_element() + sage: cat = phi.category() + sage: phi_r1 = cat.random_object(1) + sage: phi_r1.invert(phi_r1(a)) == a + True + sage: phi_r2 = cat.random_object(2) + sage: phi_r2.invert(phi_r2(a)) == a + True + sage: phi_r3 = cat.random_object(3) + sage: phi_r3.invert(phi_r3(a)) == a + True + sage: phi_r4 = cat.random_object(4) + sage: phi_r4.invert(phi_r4(a)) == a + True + sage: phi_r5 = cat.random_object(5) + sage: phi_r5.invert(phi_r5(a)) == a + True + """ + deg = ore_pol.degree() + r = self.rank() + base_over_Fq = self.base_over_constants_field() + if ore_pol not in self._ore_polring: + raise TypeError('input must be an Ore polynomial') + if ore_pol.degree() == 0: + coord = base_over_Fq(self._base(ore_pol)).vector() + if coord.nonzero_positions == [0]: + return self._Fq(coord[0]) + if ore_pol == 0: + return self._Fq.zero() + if deg % r != 0: + raise ValueError('input must be in the image of the Drinfeld ' + 'module') + + k = deg // r + T = self._function_ring.gen() + mat_lines = [[0 for _ in range(k+1)] for _ in range(k+1)] + for i in range(k+1): + phi_T_i = self(T**i) + for j in range(i+1): + mat_lines[j][i] = phi_T_i[r*j] + mat = Matrix(mat_lines) + vec = vector([list(ore_pol)[r*j] for j in range(k+1)]) + coeffs = list((mat**(-1)) * vec) + coeffs_in_Fq = [] + for coeff in coeffs: + coeff_in_Fq = base_over_Fq(coeff).vector()[0] + coeffs_in_Fq.append(coeff_in_Fq) + pre_image = self._function_ring(coeffs_in_Fq) + + if self(pre_image) == ore_pol: + return pre_image + else: + raise ValueError('input must be in the image of the Drinfeld ' + 'module') + def is_ordinary(self): r""" Return ``True`` whether the Drinfeld module is ordinary; raise a From de74a78c97c36e2641a0dcc389fb5780b344fa50 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 18:17:35 +0100 Subject: [PATCH 182/197] (fix) Identation problem in DrinfeldModules --- src/sage/categories/drinfeld_modules.py | 240 ++++++++++++------------ 1 file changed, 120 insertions(+), 120 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index a8f74cff940..5c1aa204d24 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -653,123 +653,123 @@ def base_over_constants_field(self): """ return self.category().base_over_constants_field() - def characteristic(self): - r""" - Return the function ring-characteristic. - - OUTPUT: a univariate polynomial ring - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi.characteristic() - T^2 + (4*z2 + 2)*T + 2 - sage: phi.base_morphism()(phi.characteristic()) - 0 - - :: - - sage: B. = Fq[] - sage: L = Frac(B) - sage: psi = DrinfeldModule(A, [L(1), 0, 0, L(1)]) - sage: psi.characteristic() - 0 - """ - return self.category().characteristic() - - def function_ring(self): - r""" - Return the function ring of the Drinfeld module. - - OUTPUT: a univariate polynomial ring - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi.function_ring() is A - True - """ - return self.category().function_ring() - - def constant_coefficient(self): - r""" - Return the constant coefficient of the generator. - - OUTPUT: an element in the base field - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: phi.constant_coefficient() == p_root - True - - Let `\mathbb{F}_q[T]` be the function ring, and let `\gamma` - the base of the Drinfeld module. The constant coefficient - equals `\gamma(T)`:: - - sage: cat = phi.category() - sage: base = cat.base() - sage: base(T) == phi.constant_coefficient() - True - - Naturally, two Drinfeld modules in the same category have the - same constant coefficient:: - - sage: t = phi.ore_polring().gen() - sage: psi = cat.object(phi.constant_coefficient() + t^3) - sage: psi - Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base - - Reciprocally, it is impossible to create two Drinfeld modules in - this category if they do not share the same constant - coefficient:: - - sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) - Traceback (most recent call last): - ... - ValueError: constant coefficient must equal that of the category - """ - return self.category().constant_coefficient() - - def ore_polring(self): - r""" - Return the Ore polynomial ring of the Drinfeld module. - - OUTPUT: an Ore polynomial ring - - EXAMPLES:: - - sage: Fq = GF(25) - sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: ore_polring = phi.ore_polring() - sage: ore_polring - Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 - - The Ore polynomial ring can also be retrieved from the category - of the Drinfeld module:: - - sage: ore_polring is phi.category().ore_polring() - True - - The generator of the Drinfeld module is in the Ore polynomial - ring:: - - sage: phi(T) in ore_polring - True - """ - return self.category().ore_polring() + def characteristic(self): + r""" + Return the function ring-characteristic. + + OUTPUT: a univariate polynomial ring + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.characteristic() + T^2 + (4*z2 + 2)*T + 2 + sage: phi.base_morphism()(phi.characteristic()) + 0 + + :: + + sage: B. = Fq[] + sage: L = Frac(B) + sage: psi = DrinfeldModule(A, [L(1), 0, 0, L(1)]) + sage: psi.characteristic() + 0 + """ + return self.category().characteristic() + + def function_ring(self): + r""" + Return the function ring of the Drinfeld module. + + OUTPUT: a univariate polynomial ring + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.function_ring() is A + True + """ + return self.category().function_ring() + + def constant_coefficient(self): + r""" + Return the constant coefficient of the generator. + + OUTPUT: an element in the base field + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.constant_coefficient() == p_root + True + + Let `\mathbb{F}_q[T]` be the function ring, and let `\gamma` + the base of the Drinfeld module. The constant coefficient + equals `\gamma(T)`:: + + sage: cat = phi.category() + sage: base = cat.base() + sage: base(T) == phi.constant_coefficient() + True + + Naturally, two Drinfeld modules in the same category have the + same constant coefficient:: + + sage: t = phi.ore_polring().gen() + sage: psi = cat.object(phi.constant_coefficient() + t^3) + sage: psi + Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base + + Reciprocally, it is impossible to create two Drinfeld modules in + this category if they do not share the same constant + coefficient:: + + sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) + Traceback (most recent call last): + ... + ValueError: constant coefficient must equal that of the category + """ + return self.category().constant_coefficient() + + def ore_polring(self): + r""" + Return the Ore polynomial ring of the Drinfeld module. + + OUTPUT: an Ore polynomial ring + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: ore_polring = phi.ore_polring() + sage: ore_polring + Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 + + The Ore polynomial ring can also be retrieved from the category + of the Drinfeld module:: + + sage: ore_polring is phi.category().ore_polring() + True + + The generator of the Drinfeld module is in the Ore polynomial + ring:: + + sage: phi(T) in ore_polring + True + """ + return self.category().ore_polring() From 506fb1561aea4faed202be6fe2a8fa101ec94bb8 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 18:22:37 +0100 Subject: [PATCH 183/197] (fix) Fix some doctest fails --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- .../function_field/drinfeld_modules/finite_drinfeld_module.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index cf6743638a4..68d6bb79abc 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -999,7 +999,7 @@ def height(self): sage: phi.height() Traceback (most recent call last): ... - ValueError: height is defined for prime function field characteristic + NotImplementedError: height not implemented in this case :: diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index a5041ec011a..4a0c0984c14 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -188,7 +188,7 @@ def frobenius_endomorphism(self): deg = self._base.over(Fq).degree(Fq) return self._Hom_(self, category=self.category())(t**deg) - def frobenius_charpoly(self, var='T'): + def frobenius_charpoly(self, var='X'): r""" Return the characteristic polynomial of the Frobenius endomorphism if the rank is two. Raise a NotImplementedError @@ -227,7 +227,7 @@ def frobenius_charpoly(self, var='T'): sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: chi = phi.frobenius_charpoly() sage: chi - T^2 + ((3*z3^2 + z3 + 4)*X + 4*z3^2 + 6*z3 + 3)*T + (5*z3^2 + 2*z3)*X^2 + (4*z3^2 + 3*z3)*X + 5*z3^2 + 2*z3 + X^2 + ((3*z3^2 + z3 + 4)*T + 4*z3^2 + 6*z3 + 3)*X + (5*z3^2 + 2*z3)*T^2 + (4*z3^2 + 3*z3)*T + 5*z3^2 + 2*z3 :: From 301aa0a78e588d4b1fe316ead5681007f1794b4c Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 20:59:21 +0100 Subject: [PATCH 184/197] (fix) Fix DrinfeldModules failing doctests --- src/sage/categories/drinfeld_modules.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 5c1aa204d24..c28948eb6e3 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -631,6 +631,10 @@ def base_morphism(self): sage: sigma = DrinfeldModule(A, [Frac(A).gen(), 1]) sage: sigma.base_morphism() + Ring morphism: + From: Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 + To: Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 over its base + Defn: T |--> T """ return self.category().base_morphism() @@ -677,7 +681,9 @@ def characteristic(self): sage: L = Frac(B) sage: psi = DrinfeldModule(A, [L(1), 0, 0, L(1)]) sage: psi.characteristic() - 0 + Traceback (most recent call last): + ... + NotImplementedError: function ring characteristic notimplemented in this case """ return self.category().characteristic() From 90aa9ac02dedee8a2cb76142129df8b887659169 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 21:07:09 +0100 Subject: [PATCH 185/197] Enhance invert method code --- .../drinfeld_modules/finite_drinfeld_module.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 4a0c0984c14..014f587a36e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -471,12 +471,9 @@ def invert(self, ore_pol): mat_lines[j][i] = phi_T_i[r*j] mat = Matrix(mat_lines) vec = vector([list(ore_pol)[r*j] for j in range(k+1)]) - coeffs = list((mat**(-1)) * vec) - coeffs_in_Fq = [] - for coeff in coeffs: - coeff_in_Fq = base_over_Fq(coeff).vector()[0] - coeffs_in_Fq.append(coeff_in_Fq) - pre_image = self._function_ring(coeffs_in_Fq) + coeffs_K = list((mat**(-1)) * vec) + coeffs_Fq = list(map(lambda x: base_over_Fq(x).vector()[0], coeffs_K)) + pre_image = self._function_ring(coeffs_Fq) if self(pre_image) == ore_pol: return pre_image From d954bc3c5830964dd06d83452b523450f6530180 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Tue, 7 Feb 2023 21:51:34 +0100 Subject: [PATCH 186/197] Lighten DrinfeldModule representation --- src/sage/categories/drinfeld_modules.py | 10 +++--- .../function_field/drinfeld_modules/action.py | 4 +-- .../drinfeld_modules/drinfeld_module.py | 32 ++++++++----------- .../finite_drinfeld_module.py | 2 +- .../drinfeld_modules/morphism.py | 4 +-- 5 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index c28948eb6e3..d3b931397c3 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -132,7 +132,7 @@ class DrinfeldModules(Category_over_base_ring): sage: psi = cat.object([p_root, 1]) sage: psi - Drinfeld module defined by T |--> t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 over its base + Drinfeld module defined by T |--> t + z^3 + 7*z^2 + 6*z + 10 sage: psi.category() is cat True @@ -149,7 +149,7 @@ class DrinfeldModules(Category_over_base_ring): sage: rho = cat.random_object(2) sage: rho # random - Drinfeld module defined by T |--> (7*z^3 + 7*z^2 + 10*z + 2)*t^2 + (9*z^3 + 5*z^2 + 2*z + 7)*t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + Drinfeld module defined by T |--> (7*z^3 + 7*z^2 + 10*z + 2)*t^2 + (9*z^3 + 5*z^2 + 2*z + 7)*t + z^3 + 7*z^2 + 6*z + 10 sage: rho.rank() == 2 True sage: rho.category() is cat @@ -494,7 +494,7 @@ def object(self, gen): sage: cat = phi.category() sage: psi = cat.object([p_root, 0, 1]) sage: psi - Drinfeld module defined by T |--> t^2 + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 over its base + Drinfeld module defined by T |--> t^2 + z^3 + 7*z^2 + 6*z + 10 sage: t = phi.ore_polring().gen() sage: cat.object(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi True @@ -547,7 +547,7 @@ def random_object(self, rank): sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: cat = phi.category() sage: psi = cat.random_object(3) # random - Drinfeld module defined by T |--> (6*z^3 + 4*z^2 + 10*z + 9)*t^3 + (4*z^3 + 8*z^2 + 8*z)*t^2 + (10*z^3 + 3*z^2 + 6*z)*t + z^3 + 7*z^2 + 6*z + 10 over Finite Field in z of size 11^4 + Drinfeld module defined by T |--> (6*z^3 + 4*z^2 + 10*z + 9)*t^3 + (4*z^3 + 8*z^2 + 8*z)*t^2 + (10*z^3 + 3*z^2 + 6*z)*t + z^3 + 7*z^2 + 6*z + 10 sage: psi.rank() == 3 True """ @@ -736,7 +736,7 @@ def constant_coefficient(self): sage: t = phi.ore_polring().gen() sage: psi = cat.object(phi.constant_coefficient() + t^3) sage: psi - Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 Reciprocally, it is impossible to create two Drinfeld modules in this category if they do not share the same constant diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index be93b643526..c324014745d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -58,7 +58,7 @@ class DrinfeldModuleAction(Action): sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 11^2 over its base + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z The action on elements is computed as follows:: @@ -170,7 +170,7 @@ def _repr_(self): sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: action - Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 11^2 over its base + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by T |--> t^3 + z """ return f'Action on {self._base} induced by ' \ f'{self._drinfeld_module}' diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 68d6bb79abc..fdd4997bbf7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -49,10 +49,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): A Drinfeld `\mathbb{F}_q[T]`-module over the `base \mathbb{F}_q[T]`-field `K` is an `\mathbb{F}_q`-algebra morphism - `\phi: \mathbb{F}_q[T] \to K\{\tau\}` such that: - 1. The image of `\phi` contains nonconstant Ore polynomials. - 2. For every element `a` in the `\mathbb{F}_q[T]`, the constant - coefficient `\phi(a)` is `\gamma(a)`. + `\phi: \mathbb{F}_q[T] \to K\{\tau\}` such that `\Im(\phi) \not\subset K` + and `\phi` agrees with `\gamma` on `\mathbb{F}_q`. For `a` in `\mathbb{F}_q[T]`, `\phi(a)` is denoted `\phi_a`. @@ -80,7 +78,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(A, [z, 4, 1]) sage: phi - Drinfeld module defined by T |--> t^2 + 4*t + z over Finite Field in z of size 5^12 over its base + Drinfeld module defined by T |--> t^2 + 4*t + z :: @@ -89,7 +87,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: K. = Frac(A) sage: psi = DrinfeldModule(A, [z, T+1]) sage: psi - Drinfeld module defined by T |--> (T + 1)*t + T over Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 7^2 over its base + Drinfeld module defined by T |--> (T + 1)*t + T .. NOTE:: @@ -128,7 +126,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(A, [z, 1, 1]) sage: phi - Drinfeld module defined by T |--> t^2 + t + z over Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> t^2 + t + z .. NOTE:: @@ -141,7 +139,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: L = Frac(A) sage: psi = DrinfeldModule(A, [L(T), 1, T^3 + T + 1]) sage: psi - Drinfeld module defined by T |--> (T^3 + T + 1)*t^2 + t + T over Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 over its base + Drinfeld module defined by T |--> (T^3 + T + 1)*t^2 + t + T :: @@ -159,7 +157,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: rho_T = z + t^3 sage: rho = DrinfeldModule(A, rho_T) sage: rho - Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> t^3 + z sage: rho(T) == rho_T True @@ -195,7 +193,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: cat = phi.category() sage: cat.object([z, 0, 0, 1]) - Drinfeld module defined by T |--> t^3 + z over Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> t^3 + z .. RUBRIC:: The base field of a Drinfeld module @@ -223,8 +221,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): One can retrieve basic properties:: - :: - sage: phi.base_morphism() Ring morphism: From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 @@ -366,7 +362,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z sage: psi = phi.velu(ore_pol) sage: psi - Drinfeld module defined by T |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z over Finite Field in z of size 3^12 over its base + Drinfeld module defined by T |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z sage: ore_pol in Hom(phi, psi) True sage: ore_pol * phi(T) == psi(T) * ore_pol @@ -397,7 +393,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: action = phi.action() sage: action - Action on Finite Field in z of size 3^12 over its base induced by Drinfeld module defined by T |--> t^2 + t + z over Finite Field in z of size 3^12 over its base + Action on Finite Field in z of size 3^12 over its base induced by Drinfeld module defined by T |--> t^2 + t + z The action on elements is computed by calling the action object:: @@ -823,10 +819,10 @@ def _repr_(self): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi - Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 """ return f'Drinfeld module defined by {self._function_ring.gen()} ' \ - f'|--> {self._gen} over {self._base}' + f'|--> {self._gen}' def action(self): r""" @@ -846,7 +842,7 @@ def action(self): sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: action = phi.action() sage: action - Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base + Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 The action on elements is computed as follows:: @@ -1206,7 +1202,7 @@ def velu(self, isog): sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) sage: psi - Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: isog in Hom(phi, psi) True diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 014f587a36e..bc87115f2f5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -50,7 +50,7 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, 0, 5]) sage: phi - Drinfeld module defined by T |--> 5*t^2 + z6 over Finite Field in z6 of size 7^6 over its base + Drinfeld module defined by T |--> 5*t^2 + z6 :: diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index f99be1e93e9..48bd560961f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -64,14 +64,14 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, One can get basic data on the morphism:: sage: morphism.domain() - Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: morphism.domain() is phi True :: sage: morphism.codomain() - Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 over Finite Field in z12 of size 5^12 over its base + Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: morphism.codomain() is psi True From 3b2314b55e517e9639600b342ced9dff18993c33 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 7 Feb 2023 23:53:38 +0100 Subject: [PATCH 187/197] small changes --- .../drinfeld_modules/drinfeld_module.py | 72 ++++++------ .../finite_drinfeld_module.py | 62 +++++----- .../function_field/drinfeld_modules/homset.py | 107 +++++++++--------- .../drinfeld_modules/morphism.py | 18 +-- 4 files changed, 118 insertions(+), 141 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index fdd4997bbf7..a5a5f6dceab 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -47,15 +47,15 @@ class DrinfeldModule(Parent, UniqueRepresentation): polynomials with coefficients in `K`, whose multiplication is given by the rule `\tau \lambda = \lambda^q \tau` for any `\lambda \in K`. - A Drinfeld `\mathbb{F}_q[T]`-module over the `base - \mathbb{F}_q[T]`-field `K` is an `\mathbb{F}_q`-algebra morphism + A Drinfeld `\mathbb{F}_q[T]`-module over the base + `\mathbb{F}_q[T]`-field `K` is an `\mathbb{F}_q`-algebra morphism `\phi: \mathbb{F}_q[T] \to K\{\tau\}` such that `\Im(\phi) \not\subset K` and `\phi` agrees with `\gamma` on `\mathbb{F}_q`. For `a` in `\mathbb{F}_q[T]`, `\phi(a)` is denoted `\phi_a`. The Drinfeld `\mathbb{F}_q[T]`-module `\phi` is uniquely determined - by the image `\phi_T` of `T` — this serves as input of the class. + by the image `\phi_T` of `T`; this serves as input of the class. .. NOTE:: @@ -84,8 +84,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: Fq = GF(49) sage: A. = Fq[] - sage: K. = Frac(A) - sage: psi = DrinfeldModule(A, [z, T+1]) + sage: K = Frac(A) + sage: psi = DrinfeldModule(A, [K(T), T+1]) sage: psi Drinfeld module defined by T |--> (T + 1)*t + T @@ -111,8 +111,10 @@ class DrinfeldModule(Parent, UniqueRepresentation): - ``function_ring`` -- a univariate polynomial ring whose base field is a finite field + - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial + - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring generator @@ -131,7 +133,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. NOTE:: Note that the definition of the base field is implicit; it is - automatically defined as the compositum of all the parents of + automatically defined as the compositum of all the parents of the coefficients. The above Drinfeld module is finite; it can also be infinite:: @@ -337,7 +339,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: identity_morphism.ore_polynomial() 1 - It is easy to check if a morphism is an isogeny, endomorphism or + One checks if a morphism is an isogeny, endomorphism or isomorphism:: sage: frobenius_endomorphism.is_isogeny() @@ -355,17 +357,17 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: The Vélu formula - Let ``ore_pol`` be a nonzero Ore polynomial. We can decide if there - exists a Drinfeld module ``psi`` such that ``ore_pol`` is an isogeny - from ``self`` to ``psi``. If so, we find ``psi``:: + Let ``P`` be a nonzero Ore polynomial. We can decide if ``P`` + defines an isogeny with a given domain and, if it does, find + the codomain:: - sage: ore_pol = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z - sage: psi = phi.velu(ore_pol) + sage: P = (2*z^6 + z^3 + 2*z^2 + z + 2)*t + z^11 + 2*z^10 + 2*z^9 + 2*z^8 + z^7 + 2*z^6 + z^5 + z^3 + z^2 + z + sage: psi = phi.velu(P) sage: psi Drinfeld module defined by T |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z - sage: ore_pol in Hom(phi, psi) + sage: P in Hom(phi, psi) True - sage: ore_pol * phi(T) == psi(T) * ore_pol + sage: P * phi(T) == psi(T) * P True If the input does not define an isogeny, an exception is raised: @@ -519,14 +521,19 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): - ``function_ring`` -- a univariate polynomial ring whose base is a finite field + - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial + - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring gen + - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld module - OUTPUT: a DrinfeldModule or FiniteDrinfeldModule + OUTPUT: + + A DrinfeldModule or FiniteDrinfeldModule. TESTS:: @@ -618,17 +625,20 @@ def __init__(self, gen, category, latexname=None): """ Initialize ``self``. - Validity of the input is checked in ``__classcall_private__``. - The ``__init__`` just saves attributes. + Validity of the input is checked in meth:`__classcall_private__`. + The meth:`__init__` just saves attributes. INPUT: - ``function_ring`` -- a univariate polynomial ring whose base is a finite field + - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial + - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring gen + - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld module @@ -718,11 +728,10 @@ def _Hom_(self, other, category): INPUT: - ``other`` -- the codomain of the homset + - ``category`` -- the category in which we consider the morphisms, usually ``self.category()`` - OUTPUT: an homset - EXAMPLES:: sage: Fq = GF(25) @@ -768,10 +777,8 @@ def _latex_(self): r""" Return a LaTeX representation of the Drinfeld module. - If a LaTeX name was given at init. using `latexname`, use the LaTeX - name. Otherwise, create a representation. - - OUTPUT: a string + If a LaTeX name was given at initialization, we use it. + Otherwise, we create a representation. EXAMPLES:: @@ -807,9 +814,7 @@ def _latex_(self): def _repr_(self): r""" - Return a string representation of the Drinfeld module. - - OUTPUT: a string + Return a string representation of this Drinfeld module. EXAMPLES:: @@ -844,7 +849,7 @@ def action(self): sage: action Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - The action on elements is computed as follows:: + The action on elements is computed as follows:: sage: P = T^2 + T + 1 sage: a = z12 + 1 @@ -905,8 +910,6 @@ def coefficients(self, sparse=True): - ``sparse`` -- a boolean - OUTPUT: a list of elements in the base codomain - EXAMPLES:: sage: Fq = GF(25) @@ -939,8 +942,6 @@ def gen(self): r""" Return the generator of the Drinfeld module. - OUTPUT: an Ore polynomial - EXAMPLES:: sage: Fq = GF(25) @@ -973,8 +974,6 @@ def height(self): A rank two Drinfeld module is supersingular if and only if its height equals its rank. - OUTPUT: an integer - EXAMPLES:: sage: Fq = GF(25) @@ -1015,15 +1014,14 @@ def height(self): 'function field characteristic') else: p = self.characteristic() - return Integer((self(p).valuation()) // (p.degree())) + return Integer(self(p).valuation() // p.degree()) except NotImplementedError: raise NotImplementedError('height not implemented in this case') def is_finite(self): r""" - Return ``True`` whether the Drinfeld module is finite. - - OUTPUT: a boolean + Return ``True`` if this Drinfeld module is finite, + ``False`` otherwise. EXAMPLES:: diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index bc87115f2f5..0d8939051ca 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -91,7 +91,7 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: chi(frob_pol, phi(T)) 0 - This makes it possible to compute the Frobenius trace and norm:: + as well as its trace and norm:: sage: phi.frobenius_trace() 6*T + 5*z3^2 + 5*z3 + 6 @@ -100,7 +100,7 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: phi.frobenius_norm() 2*T^2 + (z3^2 + z3 + 4)*T + 2*z3 - And to decide if a Drinfeld module is ordinary or supersingular:: + We can decide if a Drinfeld module is ordinary or supersingular:: sage: phi.is_ordinary() True @@ -129,10 +129,13 @@ def __init__(self, gen, category, latexname=None): - ``function_ring`` -- a univariate polynomial ring whose base is a finite field + - ``gen`` -- the generator of the Drinfeld module as a list of coefficients or an Ore polynomial + - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring gen + - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld module @@ -148,11 +151,9 @@ def __init__(self, gen, category, latexname=None): sage: phi._gen == ore_polring(gen) True """ - # NOTE: There used to be no __init__ here (which was fine). I # added one to ensure that FiniteDrinfeldModule would always # have _frobenius_norm and _frobenius_trace attributes. - super().__init__(gen, category, latexname) self._frobenius_norm = None self._frobenius_trace = None @@ -166,8 +167,6 @@ def frobenius_endomorphism(self): *Frobenius endomorphism* is defined as the endomorphism whose defining Ore polynomial is `t^q`. - OUTPUT: a Drinfeld module morphism - EXAMPLES:: sage: Fq = GF(343) @@ -179,6 +178,9 @@ def frobenius_endomorphism(self): From (gen): z6*t^2 + 1 To (gen): z6*t^2 + 1 Defn: t^2 + + TESTS:: + sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism sage: isinstance(phi.frobenius_endomorphism(), DrinfeldModuleMorphism) True @@ -214,10 +216,9 @@ def frobenius_charpoly(self, var='X'): Note that the *Frobenius trace* is defined as `A(T)` and the *Frobenius norm* is defined as `B(T)`. - INPUT: (default: ``'T'``) the name of the second variable + INPUT: - OUTPUT: an univariate polynomial with coefficients in the - function ring + - ``var`` (default: ``'X'``) -- the name of the second variable EXAMPLES:: @@ -283,8 +284,6 @@ def frobenius_norm(self): Let `n` be the degree of the base field over `\mathbb{F}_q` Then the Frobenius norm has degree `n`. - OUTPUT: an element in the function ring - EXAMPLES:: sage: Fq = GF(343) @@ -331,26 +330,11 @@ def frobenius_trace(self): Let `\mathbb{F}_q[T]` be the function ring, write `\chi = T^2 - A(X)T + B(X) \in \mathbb{F}_q[T][X]` for the characteristic - polynomial of the Frobenius endomorphism. The *Frobenius norm* - is defined as the polynomial `B(T) \in \mathbb{F}_q[T]`. + polynomial of the Frobenius endomorphism. The *Frobenius trace* + is defined as the polynomial `A(T) \in \mathbb{F}_q[T]`. Let `n` be the degree over `\mathbb{F}_q` of the base codomain. - Then the Frobenius trace has degree `\leq \frac{n}{2}`. - - OUTPUT: an element in the function ring - - ALGORITHM: - - Let `A(T)` denote the Frobenius trace and `B(T)` denote the - Frobenius norm. We begin by computing `B(T)`, see docstring - of method :meth:`frobenius_norm` for details. The - characteristic polynomial of the Frobenius yields `t^{2n} - - \phi_A t^n + \phi_B = 0`, where `t^n` is the Frobenius - endomorphism. As `\phi_B` is now known, we can compute - `\phi_A = (t^{2n} + \phi_B) / t^n`. We get `A(T)` by - inverting this quantity, using the method - :meth:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule.invert`, - see its docstring for details. + Then the Frobenius trace has degree at most `\frac{n}{2}`. EXAMPLES:: @@ -372,6 +356,19 @@ def frobenius_trace(self): sage: A == -phi.frobenius_charpoly()[1] True + + ALGORITHM: + + Let `A(T)` denote the Frobenius trace and `B(T)` denote the + Frobenius norm. We begin by computing `B(T)`, see docstring + of method :meth:`frobenius_norm` for details. The + characteristic polynomial of the Frobenius yields `t^{2n} - + \phi_A t^n + \phi_B = 0`, where `t^n` is the Frobenius + endomorphism. As `\phi_B` is now known, we can compute + `\phi_A = (t^{2n} + \phi_B) / t^n`. We get `A(T)` by + inverting this quantity, using the method + :meth:`sage.rings.function_fields.drinfeld_module.drinfeld_module.DrinfeldModule.invert`, + see its docstring for details. """ self._check_rank_two() # Notations from Schost-Musleh: @@ -392,8 +389,6 @@ def invert(self, ore_pol): - ``ore_pol`` -- the Ore polynomial whose preimage we want to compute - OUTPUT: a function ring element - EXAMPLES:: sage: Fq = GF(25) @@ -417,6 +412,7 @@ def invert(self, ore_pol): Traceback (most recent call last): ... ValueError: input must be in the image of the Drinfeld module + sage: phi.invert(t^3 + t^2 + 1) Traceback (most recent call last): ... @@ -494,8 +490,6 @@ def is_ordinary(self): A rank two Drinfeld module is *ordinary* if and only if it is not supersingular; see :meth:`is_supersingular`. - OUTPUT: a boolean - EXAMPLES:: sage: Fq = GF(343) @@ -530,8 +524,6 @@ def is_supersingular(self): trace. An *ordinary* rank two finite Drinfeld module is a Drinfeld module that is not supersingular. - OUTPUT: a boolean - EXAMPLES:: sage: Fq = GF(343) diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index c5391095631..f9d3fac81e4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -34,6 +34,7 @@ class DrinfeldModuleHomset(Homset): INPUT: - ``X`` -- the domain + - ``Y`` -- the codomain EXAMPLES:: @@ -43,22 +44,22 @@ class DrinfeldModuleHomset(Homset): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, z6, 2]) sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) - sage: hom = Hom(phi, psi) - sage: hom + sage: H = Hom(phi, psi) + sage: H Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 :: sage: from sage.rings.function_field.drinfeld_modules.homset import DrinfeldModuleHomset - sage: isinstance(hom, DrinfeldModuleHomset) + sage: isinstance(H, DrinfeldModuleHomset) True There is a simpler syntax for endomorphisms sets:: - sage: end = End(phi) - sage: end + sage: E = End(phi) + sage: E Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + z6*t + z6 - sage: end is Hom(phi, phi) + sage: E is Hom(phi, phi) True The domain and codomain must have the same Drinfeld modules @@ -80,8 +81,7 @@ class DrinfeldModuleHomset(Homset): One can create morphism objects by calling the homset:: - sage: t = phi.ore_polring().gen() - sage: identity_morphism = end(1) + sage: identity_morphism = E(1) sage: identity_morphism Drinfeld Module morphism: From (gen): 2*t^2 + z6*t + z6 @@ -90,7 +90,8 @@ class DrinfeldModuleHomset(Homset): :: - sage: frobenius_endomorphism = end(t^6) + sage: t = phi.ore_polring().gen() + sage: frobenius_endomorphism = E(t^6) sage: frobenius_endomorphism Drinfeld Module morphism: From (gen): 2*t^2 + z6*t + z6 @@ -99,7 +100,7 @@ class DrinfeldModuleHomset(Homset): :: - sage: isogeny = hom(t + 1) + sage: isogeny = H(t + 1) sage: isogeny Drinfeld Module morphism: From (gen): 2*t^2 + z6*t + z6 @@ -109,28 +110,28 @@ class DrinfeldModuleHomset(Homset): And one can test if an Ore polynomial defines a morphism using the ``in`` syntax:: - sage: 1 in hom + sage: 1 in H False - sage: t^6 in hom + sage: t^6 in H False - sage: t + 1 in hom + sage: t + 1 in H True - sage: 1 in end + sage: 1 in E True - sage: t^6 in end + sage: t^6 in E True - sage: t + 1 in end + sage: t + 1 in E False This also works if the candidate is a morphism object:: - sage: isogeny in hom + sage: isogeny in H True - sage: end(0) in end + sage: E(0) in E True - sage: identity_morphism in hom + sage: identity_morphism in H False - sage: frobenius_endomorphism in hom + sage: frobenius_endomorphism in H False """ @@ -144,9 +145,12 @@ def __init__(self, X, Y, category=None, check=True): INPUT: - ``X`` -- the domain of the homset + - ``Y`` -- the codomain of the homset + - ``category`` (default: ``None``) -- the Drinfeld modules category of the domain and codomain + - ``check`` (default: ``True``) -- check the validity of the category TESTS:: @@ -156,10 +160,10 @@ def __init__(self, X, Y, category=None, check=True): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, z6, 2]) sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) - sage: hom = Hom(phi, psi) - sage: hom.domain() is phi + sage: H = Hom(phi, psi) + sage: H.domain() is phi True - sage: hom.codomain() is psi + sage: H.codomain() is psi True """ if category is None: @@ -178,8 +182,6 @@ def _latex_(self): r""" Return a LaTeX representation of the homset. - OUTPUT: a string - EXAMPLES:: sage: Fq = GF(27) @@ -187,8 +189,8 @@ def _latex_(self): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, z6, 2]) sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) - sage: hom = Hom(phi, psi) - sage: latex(hom) + sage: H = Hom(phi, psi) + sage: latex(H) \text{Set{ }of{ }Drinfeld{ }module{ }morphisms{ }from{ }(gen){ }}2 t^{2} + z_{6} t + z_{6}\text{{ }to{ }(gen){ }}2 t^{2} + \left(2 z_{6}^{5} + 2 z_{6}^{4} + 2 z_{6} + 1\right) t + z_{6} """ return f'\\text{{Set{{ }}of{{ }}Drinfeld{{ }}module{{ }}morphisms' \ @@ -200,8 +202,6 @@ def _repr_(self): r""" Return a string representation of the homset. - OUTPUT: a string - EXAMPLES:: sage: Fq = GF(27) @@ -209,8 +209,8 @@ def _repr_(self): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, z6, 2]) sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) - sage: hom = Hom(phi, psi) - sage: hom + sage: H = Hom(phi, psi) + sage: H Set of Drinfeld module morphisms from (gen) 2*t^2 + z6*t + z6 to (gen) 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 """ return f'Set of Drinfeld module morphisms from (gen) '\ @@ -218,15 +218,12 @@ def _repr_(self): def __contains__(self, x): r""" - Implement the ``in`` operator for the homset; return ``True`` - whether the input defines a morphism in the homset. + Return ``True`` if the input defines a morphism in the homset. INPUT: - ``x`` -- an Ore polynomial or a Drinfeld module morphism - OUTPUT: a boolean - EXAMPLES: In the next examples, the input is an Ore polynomial:: @@ -236,34 +233,34 @@ def __contains__(self, x): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, z6, 2]) sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) - sage: hom = Hom(phi, psi) - sage: end = End(phi) + sage: H = Hom(phi, psi) + sage: E = End(phi) sage: t = phi.ore_polring().gen() - sage: 1 in hom + sage: 1 in H False - sage: t^6 in hom + sage: t^6 in H False - sage: t + 1 in hom + sage: t + 1 in H True - sage: 1 in end + sage: 1 in E True - sage: t^6 in end + sage: t^6 in E True - sage: t + 1 in end + sage: t + 1 in E False Whereas the input is now a Drinfeld module morphism:: - sage: isogeny = hom(t + 1) - sage: isogeny in hom + sage: isogeny = H(t + 1) + sage: isogeny in H True - sage: end(0) in end + sage: E(0) in E True - sage: identity_morphism = end(1) - sage: identity_morphism in hom + sage: identity_morphism = E(1) + sage: identity_morphism in H False sage: frobenius_endomorphism = phi.frobenius_endomorphism() - sage: frobenius_endomorphism in hom + sage: frobenius_endomorphism in H False """ try: @@ -279,8 +276,6 @@ def _element_constructor_(self, *args, **kwds): INPUT: an Ore polynomial - OUTPUT: a Drinfeld module morphism - EXAMPLES:: sage: Fq = GF(27) @@ -288,10 +283,10 @@ def _element_constructor_(self, *args, **kwds): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, z6, 2]) sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) - sage: hom = Hom(phi, psi) - sage: end = End(phi) + sage: H = Hom(phi, psi) + sage: E = End(phi) sage: t = phi.ore_polring().gen() - sage: identity_morphism = end(1) + sage: identity_morphism = E(1) sage: identity_morphism Drinfeld Module morphism: From (gen): 2*t^2 + z6*t + z6 @@ -300,7 +295,7 @@ def _element_constructor_(self, *args, **kwds): :: - sage: frobenius_endomorphism = end(t^6) + sage: frobenius_endomorphism = E(t^6) sage: frobenius_endomorphism Drinfeld Module morphism: From (gen): 2*t^2 + z6*t + z6 @@ -309,7 +304,7 @@ def _element_constructor_(self, *args, **kwds): :: - sage: isogeny = hom(t + 1) + sage: isogeny = H(t + 1) sage: isogeny Drinfeld Module morphism: From (gen): 2*t^2 + z6*t + z6 diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 48bd560961f..275b49d163f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -30,7 +30,7 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, This class represents Drinfeld `\mathbb{F}_q[T]`-module morphisms. Let `\phi` and `\psi` be two Drinfeld `\mathbb{F}_q[T]`-modules over - a field `K`. A *morphism of Drinfeld modules `\phi \to \psi`* is an + a field `K`. A *morphism of Drinfeld modules* `\phi \to \psi` is an Ore polynomial `f \in K\{\tau\}` such that `f \phi_a = \psi_a f` for every `a \in \mathbb{F}_q[T]`. In our case, this is equivalent to `f \phi_T = \psi_T f`. An *isogeny* is a nonzero morphism. @@ -68,8 +68,6 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: morphism.domain() is phi True - :: - sage: morphism.codomain() Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: morphism.codomain() is psi @@ -80,8 +78,6 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: morphism.ore_polynomial() t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 - :: - sage: morphism.ore_polynomial() is ore_pol True @@ -126,13 +122,13 @@ def __classcall_private__(cls, parent, x): INPUT: - - ``cls`` -- DrinfeldModuleMorphism + - ``cls`` -- the class ``DrinfeldModuleMorphism`` + - ``parent`` -- the Drinfeld module homset + - ``x`` -- the Ore polynomial defining the morphism or a DrinfeldModuleMorphism - OUTPUT: the morphism object - TESTS:: sage: Fq = GF(2) @@ -176,6 +172,7 @@ def __init__(self, parent, ore_pol): INPUT: - ``parent`` -- the Drinfeld module homset + - ``ore_pol`` -- the Ore polynomial that defines the morphism TESTS:: @@ -194,7 +191,6 @@ def __init__(self, parent, ore_pol): sage: morphism._ore_polynomial == t + z6^5 + z6^2 + 1 True """ - super().__init__(parent) self._domain = parent.domain() self._codomain = parent.codomain() @@ -204,8 +200,6 @@ def _latex_(self): r""" Return a LaTeX representation of the morphism. - OUTPUT: a string - EXAMPLES:: sage: Fq = GF(2) @@ -237,8 +231,6 @@ def _repr_(self): r""" Return a string representation of the morphism. - OUTPUT: a string - EXAMPLES:: sage: Fq = GF(2) From 4ccc577fdb1c7ccd098f18cd0bb23f80257275e9 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 8 Feb 2023 07:43:19 +0100 Subject: [PATCH 188/197] category --- src/sage/categories/drinfeld_modules.py | 263 +++++++++++------------- 1 file changed, 123 insertions(+), 140 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index d3b931397c3..4e57cf36a5f 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -40,13 +40,10 @@ class DrinfeldModules(Category_over_base_ring): polynomials with coefficients in `K`, whose multiplication is given by the rule `\tau \lambda = \lambda^q \tau` for any `\lambda \in K`. - We call `K` the *base field* of the category, and `\gamma` its *base - morphism*. - - .. NOTE:: - - Equivalently, the base of the category could be defined as the - base morphism `\gamma: \mathbb{F}_q[T] \to K`. + The extension `K`/`\mathbb{F}_q[T]` (represented as an instance of + the class class:`sage.rings.ring_extension.RingExtension`) is the + *base field* of the category; its defining morphism `\gamma` is + called the *base morphism*. The monic polynomial that generates the kernel of `\gamma` is called the `\mathbb{F}_q[T]`-*characteristic*, or *function-field @@ -55,8 +52,6 @@ class DrinfeldModules(Category_over_base_ring): polynomial ring*. The constant coefficient of the category is the image of `T` under the base morphism. - INPUT: the base field - .. RUBRIC:: Construction Generally, Drinfeld modules objects are created before their @@ -68,9 +63,9 @@ class DrinfeldModules(Category_over_base_ring): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat - Category of Drinfeld modules defined over Finite Field in z of size 11^4 over its base + sage: C = phi.category() + sage: C + Category of Drinfeld modules over Finite Field in z of size 11^4 over its base The output tells the user that the category is only defined by its base. @@ -79,13 +74,13 @@ class DrinfeldModules(Category_over_base_ring): The base field is retrieved using the method :meth:`base`. - sage: cat.base() + sage: C.base() Finite Field in z of size 11^4 over its base Equivalently, one can use :meth:`base_morphism` to retrieve the base morphism:: - sage: cat.base_morphism() + sage: C.base_morphism() Ring morphism: From: Univariate Polynomial Ring in T over Finite Field of size 11 To: Finite Field in z of size 11^4 over its base @@ -95,51 +90,54 @@ class DrinfeldModules(Category_over_base_ring): Drinfeld modules in the category --- is simply the image of `T` by the base morphism:: - sage: cat.constant_coefficient() + sage: C.constant_coefficient() z^3 + 7*z^2 + 6*z + 10 - sage: cat.base_morphism()(T) == cat.constant_coefficient() + sage: C.base_morphism()(T) == C.constant_coefficient() True Similarly, the function ring-characteristic of the category is either `0` or the unique monic polynomial in `\mathbb{F}_q[T]` that generates the kernel of the base:: - sage: cat.characteristic() + sage: C.characteristic() T^2 + 7*T + 2 - sage: cat.base_morphism()(cat.characteristic()) + sage: C.base_morphism()(C.characteristic()) 0 The base field, base morphism, function ring and Ore polynomial ring are the same for the category and its objects:: - sage: cat.base() is phi.base() + sage: C.base() is phi.base() True - sage: cat.base_morphism() is phi.base_morphism() + sage: C.base_morphism() is phi.base_morphism() True - sage: cat.function_ring() is phi.function_ring() - True - sage: cat.function_ring() + + sage: C.function_ring() Univariate Polynomial Ring in T over Finite Field of size 11 - sage: cat.ore_polring() is phi.ore_polring() + sage: C.function_ring() is phi.function_ring() True - sage: cat.ore_polring() + + sage: C.ore_polring() Ore Polynomial Ring in t over Finite Field in z of size 11^4 over its base twisted by Frob + sage: C.ore_polring() is phi.ore_polring() + True + .. RUBRIC:: Creating Drinfeld module objects from the category Calling :meth:`object` with an Ore polynomial creates a Drinfeld module object in the category whose generator is the input:: - sage: psi = cat.object([p_root, 1]) + sage: psi = C.object([p_root, 1]) sage: psi Drinfeld module defined by T |--> t + z^3 + 7*z^2 + 6*z + 10 - sage: psi.category() is cat + sage: psi.category() is C True Of course, the constant coefficient of the input must be the same as the category:: - sage: cat.object([z, 1]) + sage: C.object([z, 1]) Traceback (most recent call last): ... ValueError: constant coefficient must equal that of the category @@ -147,12 +145,12 @@ class DrinfeldModules(Category_over_base_ring): It is also possible to create a random object in the category. The input is the desired rank:: - sage: rho = cat.random_object(2) + sage: rho = C.random_object(2) sage: rho # random Drinfeld module defined by T |--> (7*z^3 + 7*z^2 + 10*z + 2)*t^2 + (9*z^3 + 5*z^2 + 2*z + 7)*t + z^3 + 7*z^2 + 6*z + 10 sage: rho.rank() == 2 True - sage: rho.category() is cat + sage: rho.category() is C True TESTS:: @@ -162,20 +160,20 @@ class DrinfeldModules(Category_over_base_ring): sage: K. = Fq.extension(4) sage: from sage.categories.drinfeld_modules import DrinfeldModules sage: base = Hom(A, K)(0) - sage: cat = DrinfeldModules(base) + sage: C = DrinfeldModules(base) Traceback (most recent call last): ... TypeError: base field must be a ring extension - + :: - sage: cat.base().defining_morphism() == cat.base_morphism() + sage: C.base().defining_morphism() == C.base_morphism() True :: sage: base = Hom(A, A)(1) - sage: cat = DrinfeldModules(base) + sage: C = DrinfeldModules(base) Traceback (most recent call last): ... TypeError: base field must be a ring extension @@ -183,7 +181,7 @@ class DrinfeldModules(Category_over_base_ring): :: sage: base = 'I hate Rostropovitch' - sage: cat = DrinfeldModules(base) # known bug (blankline) + sage: C = DrinfeldModules(base) # known bug (blankline) Traceback (most recent call last): ... @@ -193,7 +191,7 @@ class DrinfeldModules(Category_over_base_ring): sage: ZZT. = ZZ[] sage: base = Hom(ZZT, K)(1) - sage: cat = DrinfeldModules(base) # known bug (blankline) + sage: C = DrinfeldModules(base) # known bug (blankline) Traceback (most recent call last): ... @@ -208,7 +206,8 @@ def __init__(self, base_field, name='t'): - ``base_field`` -- the base field, which is a ring extension over a base - - ``name`` (default: `'t'`) -- the name of the Ore polynomial + + - ``name`` (default: ``'t'``) -- the name of the Ore polynomial variable TESTS:: @@ -218,21 +217,21 @@ def __init__(self, base_field, name='t'): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() + sage: C = phi.category() sage: ore_polring. = OrePolynomialRing(phi.base(), phi.base().frobenius_endomorphism()) - sage: cat._ore_polring is ore_polring + sage: C._ore_polring is ore_polring True sage: i = phi.base().coerce_map_from(K) sage: base_morphism = Hom(A, K)(p_root) - sage: cat.base() == K.over(base_morphism) + sage: C.base() == K.over(base_morphism) True - sage: cat._base_morphism == i * base_morphism + sage: C._base_morphism == i * base_morphism True - sage: cat._function_ring is A + sage: C._function_ring is A True - sage: cat._constant_coefficient == base_morphism(T) + sage: C._constant_coefficient == base_morphism(T) True - sage: cat._characteristic(cat._constant_coefficient) + sage: C._characteristic(C._constant_coefficient) 0 """ # Check input is a ring extension @@ -270,11 +269,12 @@ def __init__(self, base_field, name='t'): self._characteristic = None if K.is_finite(): self._characteristic = A(K.over(Fq)(base_morphism(T)).minpoly()) - try: - if A.is_subring(K): - self._characteristic = Integer(0) - except NotImplementedError: - pass + else: + try: + if base_morphism.is_injective(): + self._characteristic = Integer(0) + except NotImplementedError: + pass # Create base over constants field i = A.coerce_map_from(Fq) Fq_to_K = self._base_morphism * i @@ -294,12 +294,12 @@ def _latex_(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: latex(cat) - \text{Category{ }of{ }Drinfeld{ }modules{ }defined{ }over{ }\Bold{F}_{11^{4}} + sage: C = phi.category() + sage: latex(C) + \text{Category{ }of{ }Drinfeld{ }modules{ }over{ }\Bold{F}_{11^{4}} """ return f'\\text{{Category{{ }}of{{ }}Drinfeld{{ }}modules{{ }}' \ - f'defined{{ }}over{{ }}{latex(self._base_field)}' + f'over{{ }}{latex(self._base_field)}' def _repr_(self): r""" @@ -314,18 +314,16 @@ def _repr_(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat - Category of Drinfeld modules defined over Finite Field in z of size 11^4 over its base + sage: C = phi.category() + sage: C + Category of Drinfeld modules over Finite Field in z of size 11^4 over its base """ - return f'Category of Drinfeld modules defined over {self._base_field}' + return f'Category of Drinfeld modules over {self._base_field}' def Homsets(self): r""" Return the category of homsets. - OUTPUT: the category of homsets - EXAMPLES:: sage: Fq = GF(11) @@ -333,9 +331,10 @@ def Homsets(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() + sage: C = phi.category() + sage: from sage.categories.homsets import Homsets - sage: cat.Homsets() is Homsets() + sage: C.Homsets() is Homsets() True """ return Homsets() @@ -344,8 +343,6 @@ def Endsets(self): r""" Return the category of endsets. - OUTPUT: the category of endsets - EXAMPLES:: sage: Fq = GF(11) @@ -353,9 +350,10 @@ def Endsets(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() + sage: C = phi.category() + sage: from sage.categories.homsets import Homsets - sage: cat.Endsets() is Homsets().Endsets() + sage: C.Endsets() is Homsets().Endsets() True """ return Homsets().Endsets() @@ -364,8 +362,6 @@ def base_morphism(self): r""" Return the base morphism of the category. - OUTPUT: a ring morphism - EXAMPLES:: sage: Fq = GF(11) @@ -373,13 +369,14 @@ def base_morphism(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat.base_morphism() + sage: C = phi.category() + sage: C.base_morphism() Ring morphism: From: Univariate Polynomial Ring in T over Finite Field of size 11 To: Finite Field in z of size 11^4 over its base Defn: T |--> z^3 + 7*z^2 + 6*z + 10 - sage: cat.constant_coefficient() == cat.base_morphism()(T) + + sage: C.constant_coefficient() == C.base_morphism()(T) True """ return self._base_morphism @@ -389,8 +386,6 @@ def base_over_constants_field(self): Return the base field, seen as an extension over the constants field `\mathbb{F}_q`. - OUTPUT: a ring extension - EXAMPLES:: sage: Fq = GF(11) @@ -398,8 +393,8 @@ def base_over_constants_field(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat.base_over_constants_field() + sage: C = phi.category() + sage: C.base_over_constants_field() Field in z with defining polynomial x^4 + 8*x^2 + 10*x + 2 over its base """ return self._base_over_constants_field @@ -408,8 +403,6 @@ def characteristic(self): r""" Return the function ring-characteristic. - OUTPUT: `0` or a monic prime polynomial in the function ring - EXAMPLES:: sage: Fq = GF(11) @@ -417,18 +410,19 @@ def characteristic(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat.characteristic() + sage: C = phi.category() + sage: C.characteristic() T^2 + 7*T + 2 :: sage: psi = DrinfeldModule(A, [Frac(A).gen(), 1]) - sage: psi.category().characteristic() + sage: C = psi.category() + sage: C.characteristic() 0 """ if self._characteristic is None: - raise NotImplementedError('function ring characteristic not' \ + raise NotImplementedError('function ring characteristic not ' \ 'implemented in this case') return self._characteristic @@ -436,8 +430,6 @@ def constant_coefficient(self): r""" Return the constant coefficient of the category. - OUTPUT: an element in the base field - EXAMPLES:: sage: Fq = GF(11) @@ -445,10 +437,10 @@ def constant_coefficient(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat.constant_coefficient() + sage: C = phi.category() + sage: C.constant_coefficient() z^3 + 7*z^2 + 6*z + 10 - sage: cat.constant_coefficient() == cat.base()(T) + sage: C.constant_coefficient() == C.base()(T) True """ return self._constant_coefficient @@ -457,8 +449,6 @@ def function_ring(self): r""" Return the function ring of the category. - OUTPUT: a univariate polynomial ring - EXAMPLES:: sage: Fq = GF(11) @@ -466,10 +456,10 @@ def function_ring(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat.function_ring() + sage: C = phi.category() + sage: C.function_ring() Univariate Polynomial Ring in T over Finite Field of size 11 - sage: cat.function_ring() is A + sage: C.function_ring() is A True """ return self._function_ring @@ -479,10 +469,10 @@ def object(self, gen): Return a Drinfeld module object in the category whose generator is the input. - INPUT: the generator of the Drinfeld module, given as an Ore - polynomial or a list of coefficients + INPUT: - OUTPUT: a Drinfeld module in the category + - ``gen`` -- the generator of the Drinfeld module, given as an Ore + polynomial or a list of coefficients EXAMPLES:: @@ -490,13 +480,14 @@ def object(self, gen): sage: A. = Fq[] sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 - sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: psi = cat.object([p_root, 0, 1]) - sage: psi + sage: psi = DrinfeldModule(A, [p_root, 1]) + sage: C = psi.category() + + sage: phi = C.object([p_root, 0, 1]) + sage: phi Drinfeld module defined by T |--> t^2 + z^3 + 7*z^2 + 6*z + 10 sage: t = phi.ore_polring().gen() - sage: cat.object(t^3 + z^3 + 7*z^2 + 6*z + 10) is phi + sage: C.object(t^2 + z^3 + 7*z^2 + 6*z + 10) is phi True """ from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule @@ -510,9 +501,7 @@ def object(self, gen): def ore_polring(self): r""" - Return the Ore polynomial ring of the category. - - OUTPUT: an Ore polynomial ring + Return the Ore polynomial ring of the category EXAMPLES:: @@ -521,22 +510,19 @@ def ore_polring(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat.ore_polring() + sage: C = phi.category() + sage: C.ore_polring() Ore Polynomial Ring in t over Finite Field in z of size 11^4 over its base twisted by Frob - sage: cat.ore_polring() is phi.ore_polring() - True """ return self._ore_polring def random_object(self, rank): r""" - Return a random Drinfeld module in the category, whose rank is - the input. + Return a random Drinfeld module in the category with given rank. - INPUT: an integer, the rank of the Drinfeld module + INPUT: - OUTPUT: a Drinfeld module in the category + - ``rank`` -- an integer, the rank of the Drinfeld module EXAMPLES:: @@ -545,8 +531,9 @@ def random_object(self, rank): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: psi = cat.random_object(3) # random + sage: C = phi.category() + + sage: psi = C.random_object(3) # random Drinfeld module defined by T |--> (6*z^3 + 4*z^2 + 10*z + 9)*t^3 + (4*z^3 + 8*z^2 + 8*z)*t^2 + (10*z^3 + 3*z^2 + 6*z)*t + z^3 + 7*z^2 + 6*z + 10 sage: psi.rank() == 3 True @@ -576,8 +563,8 @@ def super_categories(self): sage: K. = Fq.extension(4) sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) - sage: cat = phi.category() - sage: cat.super_categories() + sage: C = phi.category() + sage: C.super_categories() [] """ return [] @@ -586,9 +573,10 @@ class ParentMethods: def base(self): r""" - Return the base field of the Drinfeld module. + Return the base field of this Drinfeld module, viewed as + an algebra over the function ring. - OUTPUT: a field, which is a ring extension over a base + This is an instance of the class class:`sage.rings.ring_extension.RingExtension`. EXAMPLES:: @@ -610,9 +598,7 @@ def base(self): def base_morphism(self): r""" - Return the base morphism of the Drinfeld module. - - OUTPUT: a ring morphism + Return the base morphism of this Drinfeld module. EXAMPLES:: @@ -643,7 +629,7 @@ def base_over_constants_field(self): Return the base field, seen as an extension over the constants field `\mathbb{F}_q`. - OUTPUT: a ring extension + This is an instance of the class class:`sage.rings.ring_extension.RingExtension`. EXAMPLES:: @@ -661,8 +647,6 @@ def characteristic(self): r""" Return the function ring-characteristic. - OUTPUT: a univariate polynomial ring - EXAMPLES:: sage: Fq = GF(25) @@ -683,15 +667,13 @@ def characteristic(self): sage: psi.characteristic() Traceback (most recent call last): ... - NotImplementedError: function ring characteristic notimplemented in this case + NotImplementedError: function ring characteristic not implemented in this case """ return self.category().characteristic() def function_ring(self): r""" - Return the function ring of the Drinfeld module. - - OUTPUT: a univariate polynomial ring + Return the function ring of this Drinfeld module. EXAMPLES:: @@ -700,6 +682,8 @@ def function_ring(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi.function_ring() + Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 sage: phi.function_ring() is A True """ @@ -707,7 +691,8 @@ def function_ring(self): def constant_coefficient(self): r""" - Return the constant coefficient of the generator. + Return the constant coefficient of the generator + of this Drinfeld module. OUTPUT: an element in the base field @@ -721,12 +706,12 @@ def constant_coefficient(self): sage: phi.constant_coefficient() == p_root True - Let `\mathbb{F}_q[T]` be the function ring, and let `\gamma` - the base of the Drinfeld module. The constant coefficient - equals `\gamma(T)`:: + Let `\mathbb{F}_q[T]` be the function ring, and let `\gamma` be + the base of the Drinfeld module. The constant coefficient is + `\gamma(T)`:: - sage: cat = phi.category() - sage: base = cat.base() + sage: C = phi.category() + sage: base = C.base() sage: base(T) == phi.constant_coefficient() True @@ -734,7 +719,7 @@ def constant_coefficient(self): same constant coefficient:: sage: t = phi.ore_polring().gen() - sage: psi = cat.object(phi.constant_coefficient() + t^3) + sage: psi = C.object(phi.constant_coefficient() + t^3) sage: psi Drinfeld module defined by T |--> t^3 + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 @@ -742,7 +727,7 @@ def constant_coefficient(self): this category if they do not share the same constant coefficient:: - sage: rho = cat.object(phi.constant_coefficient() + 1 + t^3) + sage: rho = C.object(phi.constant_coefficient() + 1 + t^3) Traceback (most recent call last): ... ValueError: constant coefficient must equal that of the category @@ -751,9 +736,7 @@ def constant_coefficient(self): def ore_polring(self): r""" - Return the Ore polynomial ring of the Drinfeld module. - - OUTPUT: an Ore polynomial ring + Return the Ore polynomial ring of this Drinfeld module. EXAMPLES:: @@ -762,20 +745,20 @@ def ore_polring(self): sage: K. = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) - sage: ore_polring = phi.ore_polring() - sage: ore_polring + sage: S = phi.ore_polring() + sage: S Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 over its base twisted by Frob^2 The Ore polynomial ring can also be retrieved from the category of the Drinfeld module:: - sage: ore_polring is phi.category().ore_polring() + sage: S is phi.category().ore_polring() True The generator of the Drinfeld module is in the Ore polynomial ring:: - sage: phi(T) in ore_polring + sage: phi(T) in S True """ return self.category().ore_polring() From b7f0ecfea92ddb8bbe4f9bf4d568b38e901f9340 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Wed, 8 Feb 2023 15:23:45 +0100 Subject: [PATCH 189/197] Fix stuff --- src/sage/categories/drinfeld_modules.py | 5 +- .../function_field/drinfeld_modules/action.py | 2 +- .../drinfeld_modules/drinfeld_module.py | 60 +++++++++---- .../finite_drinfeld_module.py | 12 +-- .../function_field/drinfeld_modules/homset.py | 34 +++---- .../drinfeld_modules/morphism.py | 90 +++++++++++-------- 6 files changed, 115 insertions(+), 88 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 4e57cf36a5f..f07aeac6049 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -18,6 +18,7 @@ # http://www.gnu.org/licenses/ # ****************************************************************************** +from sage.categories.objects import Objects from sage.categories.category_types import Category_over_base_ring from sage.categories.homsets import Homsets from sage.misc.functional import log @@ -565,9 +566,9 @@ def super_categories(self): sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: C = phi.category() sage: C.super_categories() - [] + [Category of objects] """ - return [] + return [Objects()] class ParentMethods: diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index c324014745d..32a00f9ea71 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -150,7 +150,7 @@ def _latex_(self): sage: phi = DrinfeldModule(A, [z, 0, 0, 1]) sage: action = phi.action() sage: latex(action) - \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\text{Drinfeld{ }module{ }defined{ }by{ }} T \mapsto t^{3} + z\text{{ }over{ }base{ }}\Bold{F}_{11^{2}} + \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\phi: T \mapsto t^{3} + z """ return f'\\text{{Action{{ }}on{{ }}}}' \ f'{latex(self._base)}\\text{{{{ }}' \ diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index a5a5f6dceab..502785f53d7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -27,11 +27,13 @@ from sage.categories.drinfeld_modules import DrinfeldModules from sage.categories.homset import Hom from sage.misc.latex import latex +from sage.misc.lazy_string import _LazyString from sage.rings.integer import Integer from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.ring_extension import RingExtension_generic from sage.structure.parent import Parent +from sage.structure.sage_object import SageObject from sage.structure.sequence import Sequence from sage.structure.unique_representation import UniqueRepresentation @@ -185,7 +187,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): :class:`sage.categories.drinfeld_modules.DrinfeldModules`):: sage: phi.category() - Category of Drinfeld modules defined over Finite Field in z of size 3^12 over its base + Category of Drinfeld modules over Finite Field in z of size 3^12 over its base sage: phi.category() is psi.category() False sage: phi.category() is rho.category() @@ -316,20 +318,13 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: identity_morphism = hom(1) sage: zero_morphism = hom(0) sage: frobenius_endomorphism - Drinfeld Module morphism: - From (gen): t^2 + t + z - To (gen): t^2 + t + z - Defn: t^6 + Endomorphism of Drinfeld module defined by T |--> t^2 + t + z + Defn: t^6 sage: identity_morphism - Drinfeld Module morphism: - From (gen): t^2 + t + z - To (gen): t^2 + t + z - Defn: 1 + Identity morphism of Drinfeld module defined by T |--> t^2 + t + z sage: zero_morphism - Drinfeld Module morphism: - From (gen): t^2 + t + z - To (gen): t^2 + t + z - Defn: 0 + Endomorphism of Drinfeld module defined by T |--> t^2 + t + z + Defn: 0 The underlying Ore polynomial is retrieved with the method :meth:`ore_polynomial`:: @@ -663,6 +658,10 @@ def __init__(self, gen, category, latexname=None): True sage: phi._latexname is None True + + :: + + sage: TestSuite(phi).run() """ self._base = category.base() self._function_ring = category.function_ring() @@ -788,7 +787,7 @@ def _latex_(self): sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: latex(phi) - \text{Drinfeld{ }module{ }defined{ }by{ }} T \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12}\text{{ }over{ }base{ }}\Bold{F}_{5^{12}} + \phi: T \mapsto z_{12}^{5} t^{2} + z_{12}^{3} t + 2 z_{12}^{11} + 2 z_{12}^{10} + z_{12}^{9} + 3 z_{12}^{8} + z_{12}^{7} + 2 z_{12}^{5} + 2 z_{12}^{4} + 3 z_{12}^{3} + z_{12}^{2} + 2 z_{12} :: @@ -807,10 +806,8 @@ def _latex_(self): if self._latexname is not None: return self._latexname else: - return f'\\text{{Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}} ' \ - f'{latex(self._function_ring.gen())} '\ - f'\\mapsto {latex(self._gen)}' \ - f'\\text{{{{ }}over{{ }}base{{ }}}}{latex(self._base)}' + return f'\\phi: {latex(self._function_ring.gen())} \\mapsto ' \ + f'{latex(self._gen)}' def _repr_(self): r""" @@ -829,6 +826,33 @@ def _repr_(self): return f'Drinfeld module defined by {self._function_ring.gen()} ' \ f'|--> {self._gen}' + def _test_category(self, **options): + """ + Run generic tests on the method :meth:`.category`. + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: phi._test_category() + + .. NOTE:: + + We reimplemented this method because Drinfeld modules are + parents, and + meth:`sage.structure.parent.Parent._test_category` requires + parents' categories to be subcategories of ``Sets()``. + """ + tester = self._tester(**options) + SageObject._test_category(self, tester=tester) + category = self.category() + # Tests that self inherits methods from the categories + tester.assertTrue(isinstance(self, category.parent_class), + _LazyString("category of %s improperly initialized", (self,), {})) + def action(self): r""" Return the action object diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 0d8939051ca..237da641a18 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -77,10 +77,8 @@ class FiniteDrinfeldModule(DrinfeldModule): sage: frobenius_endomorphism = phi.frobenius_endomorphism() sage: frobenius_endomorphism - Drinfeld Module morphism: - From (gen): 5*t^2 + z6 - To (gen): 5*t^2 + z6 - Defn: t^2 + Endomorphism of Drinfeld module defined by T |--> 5*t^2 + z6 + Defn: t^2 Its characteristic polynomial can be computed:: @@ -174,10 +172,8 @@ def frobenius_endomorphism(self): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: phi.frobenius_endomorphism() - Drinfeld Module morphism: - From (gen): z6*t^2 + 1 - To (gen): z6*t^2 + 1 - Defn: t^2 + Endomorphism of Drinfeld module defined by T |--> z6*t^2 + 1 + Defn: t^2 TESTS:: diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index f9d3fac81e4..84fdc4c6e14 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -83,29 +83,24 @@ class DrinfeldModuleHomset(Homset): sage: identity_morphism = E(1) sage: identity_morphism - Drinfeld Module morphism: - From (gen): 2*t^2 + z6*t + z6 - To (gen): 2*t^2 + z6*t + z6 - Defn: 1 + Identity morphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 :: sage: t = phi.ore_polring().gen() sage: frobenius_endomorphism = E(t^6) sage: frobenius_endomorphism - Drinfeld Module morphism: - From (gen): 2*t^2 + z6*t + z6 - To (gen): 2*t^2 + z6*t + z6 - Defn: t^6 + Endomorphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 + Defn: t^6 :: sage: isogeny = H(t + 1) sage: isogeny Drinfeld Module morphism: - From (gen): 2*t^2 + z6*t + z6 - To (gen): 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 - Defn: t + 1 + From: Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 + To: Drinfeld module defined by T |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 + Defn: t + 1 And one can test if an Ore polynomial defines a morphism using the ``in`` syntax:: @@ -288,28 +283,23 @@ def _element_constructor_(self, *args, **kwds): sage: t = phi.ore_polring().gen() sage: identity_morphism = E(1) sage: identity_morphism - Drinfeld Module morphism: - From (gen): 2*t^2 + z6*t + z6 - To (gen): 2*t^2 + z6*t + z6 - Defn: 1 + Identity morphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 :: sage: frobenius_endomorphism = E(t^6) sage: frobenius_endomorphism - Drinfeld Module morphism: - From (gen): 2*t^2 + z6*t + z6 - To (gen): 2*t^2 + z6*t + z6 - Defn: t^6 + Endomorphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 + Defn: t^6 :: sage: isogeny = H(t + 1) sage: isogeny Drinfeld Module morphism: - From (gen): 2*t^2 + z6*t + z6 - To (gen): 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 - Defn: t + 1 + From: Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 + To: Drinfeld module defined by T |--> 2*t^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*t + z6 + Defn: t + 1 """ # NOTE: This used to be self.element_class(self, ...), but this # would call __init__ instead of __classcall_private__. This diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 275b49d163f..40c0caa2635 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -39,20 +39,20 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, instantiate :class:`DrinfeldModuleMorphism`, but rather call the parent homset with the defining Ore polynomial:: - sage: Fq = GF(25) + sage: Fq = GF(4) sage: A. = Fq[] - sage: K. = Fq.extension(6) - sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: K. = Fq.extension(3) + sage: phi = DrinfeldModule(A, [z, z^2 + z, z^2 + z]) sage: t = phi.ore_polring().gen() - sage: ore_pol = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + sage: ore_pol = t + z^5 + z^3 + z + 1 sage: psi = phi.velu(ore_pol) sage: morphism = Hom(phi, psi)(ore_pol) sage: morphism Drinfeld Module morphism: - From (gen): z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - To (gen): (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + From: Drinfeld module defined by T |--> (z^2 + z)*t^2 + (z^2 + z)*t + z + To: Drinfeld module defined by T |--> (z^5 + z^2 + z + 1)*t^2 + (z^4 + z + 1)*t + z + Defn: t + z^5 + z^3 + z + 1 + The given Ore polynomial must indeed define a morphism:: @@ -64,20 +64,19 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, One can get basic data on the morphism:: sage: morphism.domain() - Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Drinfeld module defined by T |--> (z^2 + z)*t^2 + (z^2 + z)*t + z sage: morphism.domain() is phi True sage: morphism.codomain() - Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + Drinfeld module defined by T |--> (z^5 + z^2 + z + 1)*t^2 + (z^4 + z + 1)*t + z sage: morphism.codomain() is psi True :: sage: morphism.ore_polynomial() - t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 - + t + z^5 + z^3 + z + 1 sage: morphism.ore_polynomial() is ore_pol True @@ -108,9 +107,9 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) Drinfeld Module morphism: - From (gen): z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - To (gen): (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 - Defn: t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 + From: Drinfeld module defined by T |--> (z^2 + z)*t^2 + (z^2 + z)*t + z + To: Drinfeld module defined by T |--> (z^5 + z^2 + z + 1)*t^2 + (z^4 + z + 1)*t + z + Defn: t + z^5 + z^3 + z + 1 sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) is morphism True """ @@ -210,22 +209,9 @@ def _latex_(self): sage: t = phi.ore_polring().gen() sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: latex(morphism) - \begin{array}{l} - \text{Drinfeld{ }module{ }morphism:}\\ - \text{{ }{ }From (gen):{ }}t^{2} + t + z_{6}\\ - \text{{ }{ }To (gen):{ }}{ }{ }t^{2} + \left(z_{6}^{4} + z_{6}^{2} + 1\right) t + z_{6}\\ - \text{{ }{ }Defn:{ }}t + z_{6}^{5} + z_{6}^{2} + 1 - \end{array} + t + z_{6}^{5} + z_{6}^{2} + 1 """ - return f'\\begin{{array}}{{l}}\n' \ - f'\\text{{Drinfeld{{ }}module{{ }}morphism:}}\\\\\n' \ - f'\\text{{{{ }}{{ }}From (gen):{{ }}}}'\ - f'{latex(self._domain.gen())}\\\\\n' \ - f'\\text{{{{ }}{{ }}To (gen):{{ }}}}{{ }}{{ }}' \ - f'{latex(self._codomain.gen())}\\\\\n' \ - f'\\text{{{{ }}{{ }}Defn:{{ }}}}' \ - f'{latex(self._ore_polynomial)}\n' \ - f'\\end{{array}}' + return f'{latex(self._ore_polynomial)}' def _repr_(self): r""" @@ -242,14 +228,20 @@ def _repr_(self): sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) sage: morphism Drinfeld Module morphism: - From (gen): t^2 + t + z6 - To (gen): t^2 + (z6^4 + z6^2 + 1)*t + z6 - Defn: t + z6^5 + z6^2 + 1 + From: Drinfeld module defined by T |--> t^2 + t + z6 + To: Drinfeld module defined by T |--> t^2 + (z6^4 + z6^2 + 1)*t + z6 + Defn: t + z6^5 + z6^2 + 1 """ - return f'Drinfeld Module morphism:\n' \ - f' From (gen): {self._domain.gen()}\n' \ - f' To (gen): {self._codomain.gen()}\n' \ - f' Defn: {self._ore_polynomial}' + if self.is_identity(): + return f'Identity morphism of {self._domain}' + elif self.is_endomorphism(): + return f'Endomorphism of {self._domain}\n' \ + f' Defn: {self._ore_polynomial}' + else: + return f'Drinfeld Module morphism:\n' \ + f' From: {self._domain}\n' \ + f' To: {self._codomain}\n' \ + f' Defn: {self._ore_polynomial}' def is_zero(self): r""" @@ -275,6 +267,30 @@ def is_zero(self): """ return self._ore_polynomial.is_zero() + def is_identity(self): + r""" + Return ``True`` whether the morphism is the identity morphism. + + EXAMPLES:: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: morphism = End(phi)(1) + sage: morphism.is_identity() + True + + :: + + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_polring().gen() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism.is_identity() + False + """ + return self._ore_polynomial == 1 + def is_isogeny(self): r""" Return ``True`` whether the morphism is an isogeny. From 30617f991360e2575347be4aaf75391a56428297 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Wed, 8 Feb 2023 15:32:33 +0100 Subject: [PATCH 190/197] Add hash methods --- .../drinfeld_modules/drinfeld_module.py | 16 ++++++++++++++++ .../drinfeld_modules/morphism.py | 18 ++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 502785f53d7..67e4ab963e8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -853,6 +853,22 @@ def _test_category(self, **options): tester.assertTrue(isinstance(self, category.parent_class), _LazyString("category of %s improperly initialized", (self,), {})) + def __hash__(self): + r""" + Return a hash of ``self``. + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 + sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) + sage: hash(phi) # random + -6894299335185957188 + """ + return hash((self.base(), self._gen)) + def action(self): r""" Return the action object diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 40c0caa2635..dab86c43efa 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -243,6 +243,24 @@ def _repr_(self): f' To: {self._codomain}\n' \ f' Defn: {self._ore_polynomial}' + def __hash__(self): + r""" + Return a hash of ``self``. + + EXAMPLES:: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) + sage: t = phi.ore_polring().gen() + sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: hash(morphism) # random + -4214883752078138009 + """ + return hash((self._domain, self._codomain, self._ore_polynomial)) + def is_zero(self): r""" Return ``True`` whether the morphism is the zero morphism. From 39022557c93db40e11523a19587fac90d34b6e4b Mon Sep 17 00:00:00 2001 From: Kryzar Date: Wed, 8 Feb 2023 15:37:40 +0100 Subject: [PATCH 191/197] (fix) Typo --- src/sage/categories/drinfeld_modules.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index f07aeac6049..7720d3fde76 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -473,7 +473,7 @@ def object(self, gen): INPUT: - ``gen`` -- the generator of the Drinfeld module, given as an Ore - polynomial or a list of coefficients + polynomial or a list of coefficients EXAMPLES:: From fbaf715324f08cc9372617528d887f19e018e7ea Mon Sep 17 00:00:00 2001 From: Kryzar Date: Thu, 9 Feb 2023 21:36:05 +0100 Subject: [PATCH 192/197] Refactor latexname --- .../drinfeld_modules/drinfeld_module.py | 52 +++++-------------- .../finite_drinfeld_module.py | 7 +-- 2 files changed, 15 insertions(+), 44 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 67e4ab963e8..4d49705c82b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -27,6 +27,7 @@ from sage.categories.drinfeld_modules import DrinfeldModules from sage.categories.homset import Hom from sage.misc.latex import latex +from sage.misc.latex import latex_variable_name from sage.misc.lazy_string import _LazyString from sage.rings.integer import Integer from sage.rings.polynomial.ore_polynomial_element import OrePolynomial @@ -174,13 +175,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi(1) # phi_1 1 - One can give a LaTeX name to be used for LaTeX representation:: - - sage: sigma = DrinfeldModule(A, [z, 1, 1], latexname='\sigma') - ... - sage: latex(sigma) - \sigma - .. RUBRIC:: The category of Drinfeld modules Drinfeld modules have their own category (see class @@ -507,7 +501,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): """ @staticmethod - def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): + def __classcall_private__(cls, function_ring, gen, name='t'): """ Check input validity and return a ``DrinfeldModule`` or ``FiniteDrinfeldModule`` object accordingly. @@ -523,9 +517,6 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring gen - - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld - module - OUTPUT: A DrinfeldModule or FiniteDrinfeldModule. @@ -586,10 +577,6 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): base_field_noext.has_coerce_map_from(function_ring.base_ring())): raise ValueError('function ring base must coerce into base field') - # Check LaTeX name - if latexname is not None and type(latexname) is not str: - raise ValueError('LaTeX name should be a string') - # Build the category T = function_ring.gen() if isinstance(base_field_noext, RingExtension_generic): @@ -613,10 +600,10 @@ def __classcall_private__(cls, function_ring, gen, name='t', latexname=None): # Instantiate the appropriate class if base_field.is_finite(): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule - return FiniteDrinfeldModule(gen, category, latexname) - return cls.__classcall__(cls, gen, category, latexname) + return FiniteDrinfeldModule(gen, category) + return cls.__classcall__(cls, gen, category) - def __init__(self, gen, category, latexname=None): + def __init__(self, gen, category): """ Initialize ``self``. @@ -634,9 +621,6 @@ def __init__(self, gen, category, latexname=None): - ``name`` (default: ``'t'``) -- the name of the Ore polynomial ring gen - - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld - module - TESTS:: sage: Fq = GF(25) @@ -656,8 +640,6 @@ def __init__(self, gen, category, latexname=None): True sage: phi._morphism == Hom(A, ore_polring)(phi._gen) True - sage: phi._latexname is None - True :: @@ -666,7 +648,6 @@ def __init__(self, gen, category, latexname=None): self._base = category.base() self._function_ring = category.function_ring() self._gen = gen - self._latexname = latexname self._morphism = category._function_ring.hom([gen]) self._ore_polring = gen.parent() self._Fq = self._function_ring.base_ring() # Must be last @@ -776,8 +757,8 @@ def _latex_(self): r""" Return a LaTeX representation of the Drinfeld module. - If a LaTeX name was given at initialization, we use it. - Otherwise, we create a representation. + If a representation name is given with meth:`rename`, it is + taken into account for LaTeX representation. EXAMPLES:: @@ -791,20 +772,13 @@ def _latex_(self): :: - sage: psi = DrinfeldModule(A, [p_root, z12^3, z12^5], latexname='\psi') - ... - sage: latex(psi) - \psi - - :: - - sage: psi = DrinfeldModule(A, [p_root, z12^3, z12^5], latexname=1729) - Traceback (most recent call last): - ... - ValueError: LaTeX name should be a string + sage: phi.rename('phi') + sage: latex(phi) + \phi + sage: phi.reset_name() """ - if self._latexname is not None: - return self._latexname + if hasattr(self, '__custom_name'): + return latex_variable_name(getattr(self, '__custom_name')) else: return f'\\phi: {latex(self._function_ring.gen())} \\mapsto ' \ f'{latex(self._gen)}' diff --git a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py index 237da641a18..114d1730b9b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py @@ -116,7 +116,7 @@ class FiniteDrinfeldModule(DrinfeldModule): True """ - def __init__(self, gen, category, latexname=None): + def __init__(self, gen, category): """ Initialize `self`. @@ -134,9 +134,6 @@ def __init__(self, gen, category, latexname=None): - ``name`` (default: `'t'`) -- the name of the Ore polynomial ring gen - - ``latexname`` (default: ``None``) -- the LaTeX name of the Drinfeld - module - TESTS:: sage: Fq = GF(25) @@ -152,7 +149,7 @@ def __init__(self, gen, category, latexname=None): # NOTE: There used to be no __init__ here (which was fine). I # added one to ensure that FiniteDrinfeldModule would always # have _frobenius_norm and _frobenius_trace attributes. - super().__init__(gen, category, latexname) + super().__init__(gen, category) self._frobenius_norm = None self._frobenius_trace = None From 14879847094a9c6b490117bfd49da9395e8962d1 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Thu, 9 Feb 2023 21:42:19 +0100 Subject: [PATCH 193/197] Enhance an error message --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 4d49705c82b..9544f66c007 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -589,6 +589,11 @@ def __classcall_private__(cls, function_ring, gen, name='t'): base_morphism = Hom(function_ring, base_field_noext)(gen[0]) base_field = base_field_noext.over(base_morphism) + # This test is also done in the category. We put it here also + # to have a friendlier error message + if not base_field.is_field(): + raise ValueError('generator coefficients must live in a field') + category = DrinfeldModules(base_field, name=name) # Check gen as Ore polynomial From 0e6cee3d7aa71458b17eb073346fcc4090920b23 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Thu, 9 Feb 2023 23:08:40 +0100 Subject: [PATCH 194/197] (fix) Fix PEP8 typo --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index 9544f66c007..c2f05bbdf56 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -589,7 +589,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): base_morphism = Hom(function_ring, base_field_noext)(gen[0]) base_field = base_field_noext.over(base_morphism) - # This test is also done in the category. We put it here also + # This test is also done in the category. We put it here also # to have a friendlier error message if not base_field.is_field(): raise ValueError('generator coefficients must live in a field') From 53cbf193a750cbc4bf39cf41afd2200632e37a55 Mon Sep 17 00:00:00 2001 From: Kryzar Date: Wed, 15 Feb 2023 17:13:16 +0100 Subject: [PATCH 195/197] (fix) Fix an indentation problem in the doc --- .../function_field/drinfeld_modules/drinfeld_module.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index c2f05bbdf56..e38df609682 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -267,10 +267,10 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.j_invariant() # j-invariant 1 - A Drinfeld `\mathbb{F}_q[T]`-module can be seen as an Ore - polynomial with positive degree and constant coefficient - `\gamma(T)`, where `\gamma` is the base morphism. This analogy is - the motivation for the following methods:: + A Drinfeld `\mathbb{F}_q[T]`-module can be seen as an Ore polynomial + with positive degree and constant coefficient `\gamma(T)`, where + `\gamma` is the base morphism. This analogy is the motivation for + the following methods:: sage: phi.coefficients() [z, 1, 1] From fbf1e092a22eb589a5e3f2bbaeb0f863ad0ae24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 28 Feb 2023 19:06:35 +0100 Subject: [PATCH 196/197] Fix master bib. file entries This would lead the pdf build to crash. --- src/doc/en/reference/references/index.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 49e555f8e19..206ce1e3359 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -4638,7 +4638,7 @@ REFERENCES: Int. Math. Res. Not. (2015). :doi:`10.1093/imrn/rnv194`, :arxiv:`1408.0320`. -.. [MS2019] \Y. Musleh and É. Schost. Computing the characteristic polynomial +.. [MS2019] \Y. Musleh and \'E. Schost. Computing the characteristic polynomial of a finite rank two Drinfeld module. In Proceedings of the 2019 ACM on International Symposium on Symbolic and Algebraic Computation, pages 307–314. ACM, 2019. @@ -6097,7 +6097,7 @@ REFERENCES: quaternion algebras and quadratic forms, to appear. .. [VS06] \G.D. Villa Salvador. Topics in the Theory of Algebraic Function - Fields. Birkhäuser, 2006. + Fields. Birkh\"auser, 2006. .. [VW1994] Leonard Van Wyk. *Graph groups are biautomatic*. J. Pure Appl. Alg. **94** (1994). no. 3, 341-352. From 1346b219378c8cedc08972be00b27db771af2efc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antoine=20Leudi=C3=A8re?= Date: Tue, 28 Feb 2023 19:07:12 +0100 Subject: [PATCH 197/197] Fix doc typos --- src/sage/categories/drinfeld_modules.py | 8 +++++--- .../drinfeld_modules/drinfeld_module.py | 13 +++++++------ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 7720d3fde76..08613d2d14a 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -42,7 +42,7 @@ class DrinfeldModules(Category_over_base_ring): by the rule `\tau \lambda = \lambda^q \tau` for any `\lambda \in K`. The extension `K`/`\mathbb{F}_q[T]` (represented as an instance of - the class class:`sage.rings.ring_extension.RingExtension`) is the + the class :class:`sage.rings.ring_extension.RingExtension`) is the *base field* of the category; its defining morphism `\gamma` is called the *base morphism*. @@ -577,7 +577,8 @@ def base(self): Return the base field of this Drinfeld module, viewed as an algebra over the function ring. - This is an instance of the class class:`sage.rings.ring_extension.RingExtension`. + This is an instance of the class + :class:`sage.rings.ring_extension.RingExtension`. EXAMPLES:: @@ -630,7 +631,8 @@ def base_over_constants_field(self): Return the base field, seen as an extension over the constants field `\mathbb{F}_q`. - This is an instance of the class class:`sage.rings.ring_extension.RingExtension`. + This is an instance of the class + :class:`sage.rings.ring_extension.RingExtension`. EXAMPLES:: diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index e38df609682..1a24f09d66d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -46,14 +46,14 @@ class DrinfeldModule(Parent, UniqueRepresentation): Let `\mathbb{F}_q[T]` be a polynomial ring with coefficients in a finite field `\mathbb{F}_q` and let `K` be a field. Fix a ring morphism `\gamma: \mathbb{F}_q[T] \to K`; we say that `K` is an - `\mathbb{F}_q[T]`*-field*. Let `K\{\tau\}` be the ring of Ore + `\mathbb{F}_q[T]`-*field*. Let `K\{\tau\}` be the ring of Ore polynomials with coefficients in `K`, whose multiplication is given by the rule `\tau \lambda = \lambda^q \tau` for any `\lambda \in K`. A Drinfeld `\mathbb{F}_q[T]`-module over the base `\mathbb{F}_q[T]`-field `K` is an `\mathbb{F}_q`-algebra morphism - `\phi: \mathbb{F}_q[T] \to K\{\tau\}` such that `\Im(\phi) \not\subset K` - and `\phi` agrees with `\gamma` on `\mathbb{F}_q`. + `\phi: \mathbb{F}_q[T] \to K\{\tau\}` such that `\mathrm{Im}(\phi) + \not\subset K` and `\phi` agrees with `\gamma` on `\mathbb{F}_q`. For `a` in `\mathbb{F}_q[T]`, `\phi(a)` is denoted `\phi_a`. @@ -359,7 +359,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: P * phi(T) == psi(T) * P True - If the input does not define an isogeny, an exception is raised: + If the input does not define an isogeny, an exception is raised:: sage: phi.velu(0) Traceback (most recent call last): @@ -375,8 +375,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): The `\mathbb{F}_q[T]`-Drinfeld module `\phi` induces a special left `\mathbb{F}_q[T]`-module structure on any field extension `L/K`. Let `x \in L` and `a` be in the function ring; the action is defined as - `(a, x) \mapsto \phi_a(x)`. The method :meth:`action` returns an - ``Action`` object representing the Drinfeld module action. + `(a, x) \mapsto \phi_a(x)`. The method :meth:`action` returns a + :class:`sage.rings.function_field.drinfeld_modules.action.Action` + object representing the Drinfeld module action. .. NOTE::