From 7c1a26ba86f74aad70668ecb174ee986d96afc60 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 17 Jul 2025 11:18:34 +0200 Subject: [PATCH 01/13] =?UTF-8?q?t=20->=20=CF=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/sage/categories/drinfeld_modules.py | 23 +-- .../function_field/drinfeld_modules/action.py | 6 +- .../charzero_drinfeld_module.py | 10 +- .../drinfeld_modules/drinfeld_module.py | 126 +++++++-------- .../finite_drinfeld_module.py | 28 ++-- .../function_field/drinfeld_modules/homset.py | 92 +++++------ .../drinfeld_modules/morphism.py | 144 +++++++++--------- 7 files changed, 215 insertions(+), 214 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 95b7f67c8e0..aa80f8cc3b8 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # sage_setup: distribution = sagemath-categories # sage.doctest: needs sage.rings.finite_rings r""" @@ -123,7 +124,7 @@ class DrinfeldModules(Category_over_base_ring): True sage: C.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 τ over Finite Field in z of size 11^4 twisted by z |--> z^11 sage: C.ore_polring() is phi.ore_polring() True @@ -135,7 +136,7 @@ class DrinfeldModules(Category_over_base_ring): sage: psi = C.object([p_root, 1]) sage: psi - Drinfeld module defined by T |--> t + z^3 + 7*z^2 + 6*z + 10 + Drinfeld module defined by T |--> τ + z^3 + 7*z^2 + 6*z + 10 sage: psi.category() is C True @@ -207,7 +208,7 @@ class DrinfeldModules(Category_over_base_ring): TypeError: function ring base must be a finite field """ - def __init__(self, base_morphism, name='t'): + def __init__(self, base_morphism, name='τ'): r""" Initialize ``self``. @@ -216,7 +217,7 @@ def __init__(self, base_morphism, 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: ``'τ'``) the name of the Ore polynomial variable TESTS:: @@ -227,7 +228,7 @@ def __init__(self, base_morphism, name='t'): sage: p_root = z^3 + 7*z^2 + 6*z + 10 sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: C = phi.category() - sage: ore_polring. = OrePolynomialRing(K, K.frobenius_endomorphism()) + sage: ore_polring.<τ> = OrePolynomialRing(K, K.frobenius_endomorphism()) sage: C._ore_polring is ore_polring True sage: C._function_ring is A @@ -507,7 +508,7 @@ def object(self, gen): 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 + Drinfeld module defined by T |--> τ^2 + z^3 + 7*z^2 + 6*z + 10 sage: t = phi.ore_polring().gen() sage: C.object(t^2 + z^3 + 7*z^2 + 6*z + 10) is phi True @@ -534,7 +535,7 @@ def ore_polring(self): sage: phi = DrinfeldModule(A, [p_root, 0, 0, 1]) sage: C = phi.category() sage: C.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 τ over Finite Field in z of size 11^4 twisted by z |--> z^11 """ return self._ore_polring @@ -770,7 +771,7 @@ def constant_coefficient(self): sage: t = phi.ore_polring().gen() 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 + Drinfeld module defined by 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 @@ -796,7 +797,7 @@ def ore_polring(self): sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: S = phi.ore_polring() sage: S - Ore Polynomial Ring in t over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) + Ore Polynomial Ring in τ 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:: @@ -825,8 +826,8 @@ def ore_variable(self): sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.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 τ over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) sage: phi.ore_variable() - t + τ """ return self.category().ore_polring().gen() diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 4962162424e..06f8897326d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -60,7 +60,7 @@ class DrinfeldModuleAction(Action): 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 + induced by Drinfeld module defined by T |--> τ^3 + z The action on elements is computed as follows:: @@ -154,7 +154,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{ }}\phi: T \mapsto t^{3} + z + \text{Action{ }on{ }}\Bold{F}_{11^{2}}\text{{ }induced{ }by{ }}\phi: T \mapsto τ^{3} + z """ return f'\\text{{Action{{ }}on{{ }}}}' \ f'{latex(self._base)}\\text{{{{ }}' \ @@ -174,7 +174,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 + Action on Finite Field in z of size 11^2 over its base induced by Drinfeld module defined by 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/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 594f8645ae1..99dd8dc9ce5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -60,7 +60,7 @@ class DrinfeldModule_charzero(DrinfeldModule): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 1]) sage: phi - Drinfeld module defined by T |--> t + T + Drinfeld module defined by T |--> τ + T :: @@ -110,7 +110,7 @@ class DrinfeldModule_charzero(DrinfeldModule): sage: L. = LaurentSeriesRing(GF(2)) # s = 1/T sage: phi = DrinfeldModule(A, [1/s, s + s^2 + s^5 + O(s^6), 1+1/s]) sage: phi(T) - (s^-1 + 1)*t^2 + (s + s^2 + s^5 + O(s^6))*t + s^-1 + (s^-1 + 1)*τ^2 + (s + s^2 + s^5 + O(s^6))*τ + s^-1 One can also construct Drinfeld modules over SageMath's global function fields:: @@ -119,7 +119,7 @@ class DrinfeldModule_charzero(DrinfeldModule): sage: K. = FunctionField(GF(5)) # z = T sage: phi = DrinfeldModule(A, [z, 1, z^2]) sage: phi(T) - z^2*t^2 + t + z + z^2*τ^2 + τ + z """ @cached_method def _compute_coefficient_exp(self, k): @@ -462,7 +462,7 @@ class DrinfeldModule_rational(DrinfeldModule_charzero): sage: A = Fq['T'] sage: K. = Frac(A) sage: C = DrinfeldModule(A, [T, 1]); C - Drinfeld module defined by T |--> t + T + Drinfeld module defined by T |--> τ + T sage: type(C) """ @@ -573,7 +573,7 @@ def class_polynomial(self): sage: A = Fq['T'] sage: K. = Frac(A) sage: C = DrinfeldModule(A, [T, 1]); C - Drinfeld module defined by T |--> t + T + Drinfeld module defined by T |--> τ + T sage: C.class_polynomial() 1 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 6c2f2450118..4d26afc2abb 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 + Drinfeld module defined by T |--> τ^2 + 4*τ + z :: @@ -99,7 +99,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: K = Frac(A) sage: psi = DrinfeldModule(A, [K(T), T+1]) sage: psi - Drinfeld module defined by T |--> (T + 1)*t + T + Drinfeld module defined by T |--> (T + 1)*τ + T .. NOTE:: @@ -140,7 +140,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 + Drinfeld module defined by T |--> τ^2 + τ + z .. NOTE:: @@ -153,7 +153,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 + Drinfeld module defined by T |--> (T^3 + T + 1)*τ^2 + τ + T :: @@ -163,15 +163,15 @@ class DrinfeldModule(Parent, UniqueRepresentation): False In those examples, we used a list of coefficients (``[z, 1, 1]``) to - represent the generator `\phi_T = z + t + t^2`. One can also use + represent the generator `\phi_T = z + τ + τ^2`. One can also use regular Ore polynomials:: sage: ore_polring = phi.ore_polring() - sage: t = ore_polring.gen() - sage: rho_T = z + t^3 + sage: tau = ore_polring.gen() + sage: rho_T = z + tau^3 sage: rho = DrinfeldModule(A, rho_T) sage: rho - Drinfeld module defined by T |--> t^3 + z + Drinfeld module defined by T |--> τ^3 + z sage: rho(T) == rho_T True @@ -179,12 +179,12 @@ class DrinfeldModule(Parent, UniqueRepresentation): object:: sage: phi(T) # phi_T, the generator of the Drinfeld module - t^2 + t + z + τ^2 + τ + z 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 + τ^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*τ^4 + + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*τ^3 + + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*τ^2 + + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*τ + z^3 + z + 1 sage: phi(1) # phi_1 1 @@ -204,7 +204,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: cat = phi.category() sage: cat.object([z, 0, 0, 1]) - Drinfeld module defined by T |--> t^3 + z + Drinfeld module defined by T |--> τ^3 + z .. RUBRIC:: The base field of a Drinfeld module @@ -243,7 +243,7 @@ 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) + Ore Polynomial Ring in τ over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) :: @@ -253,7 +253,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): :: sage: phi.gen() # phi_T - t^2 + t + z + τ^2 + τ + z sage: phi.gen() == phi(T) True @@ -267,9 +267,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.morphism() # The Drinfeld module as a morphism Ring morphism: 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 + To: Ore Polynomial Ring in τ over Finite Field in z of size 3^12 twisted by z |--> z^(3^2) - Defn: T |--> t^2 + t + z + Defn: T |--> τ^2 + τ + z One can compute the rank and height:: @@ -309,9 +309,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi(T) in Hom(phi, phi) True - sage: t^6 in Hom(phi, phi) + sage: tau^6 in Hom(phi, phi) True - sage: t^5 + 2*t^3 + 1 in Hom(phi, phi) + sage: tau^5 + 2*tau^3 + 1 in Hom(phi, phi) False sage: 1 in Hom(phi, rho) False @@ -324,23 +324,23 @@ class DrinfeldModule(Parent, UniqueRepresentation): homset (``hom``):: sage: hom = Hom(phi, phi) - sage: frobenius_endomorphism = hom(t^6) + sage: frobenius_endomorphism = hom(tau^6) sage: identity_morphism = hom(1) sage: zero_morphism = hom(0) sage: frobenius_endomorphism - Endomorphism of Drinfeld module defined by T |--> t^2 + t + z - Defn: t^6 + Endomorphism of Drinfeld module defined by T |--> τ^2 + τ + z + Defn: τ^6 sage: identity_morphism - Identity morphism of Drinfeld module defined by T |--> t^2 + t + z + Identity morphism of Drinfeld module defined by T |--> τ^2 + τ + z sage: zero_morphism - Endomorphism of Drinfeld module defined by T |--> t^2 + t + z + Endomorphism of Drinfeld module defined by T |--> τ^2 + τ + z Defn: 0 The underlying Ore polynomial is retrieved with the method :meth:`ore_polynomial`:: sage: frobenius_endomorphism.ore_polynomial() - t^6 + τ^6 sage: identity_morphism.ore_polynomial() 1 @@ -366,11 +366,11 @@ class DrinfeldModule(Parent, UniqueRepresentation): defines an isogeny with a given domain and, if it does, find the codomain:: - 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: P = (2*z^6 + z^3 + 2*z^2 + z + 2)*tau + 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 + Drinfeld module defined by T |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*τ^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)*τ + z sage: P in Hom(phi, psi) True sage: P * phi(T) == psi(T) * P @@ -382,7 +382,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): Traceback (most recent call last): ... ValueError: the input does not define an isogeny - sage: phi.velu(t) + sage: phi.velu(tau) Traceback (most recent call last): ... ValueError: the input does not define an isogeny @@ -403,7 +403,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 + induced by Drinfeld module defined by T |--> τ^2 + τ + z The action on elements is computed by calling the action object:: @@ -520,7 +520,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): """ @staticmethod - def __classcall_private__(cls, function_ring, gen, name='t'): + def __classcall_private__(cls, function_ring, gen, name='τ'): """ Check input validity and return a ``DrinfeldModule`` or ``DrinfeldModule_finite`` object accordingly. @@ -533,7 +533,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): - ``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: ``'τ'``) the name of the Ore polynomial ring gen OUTPUT: a DrinfeldModule or DrinfeldModule_finite @@ -774,7 +774,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) - \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} + \phi: T \mapsto z_{12}^{5} τ^{2} + z_{12}^{3} τ + 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} :: @@ -801,7 +801,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(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 + Drinfeld module defined by T |--> z12^5*τ^2 + z12^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 """ return f'Drinfeld module defined by {self._function_ring.gen()} ' \ f'|--> {self._gen}' @@ -868,7 +868,7 @@ def action(self): 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 + induced by Drinfeld module defined by T |--> z12^5*τ^2 + z12^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 The action on elements is computed as follows:: @@ -1301,7 +1301,7 @@ def is_isomorphic(self, other, absolutely=False): sage: A. = Fq[] sage: K. = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) - sage: t = phi.ore_variable() + sage: tau = phi.ore_variable() We create a second Drinfeld module, which is isomorphic to `\phi` and then check that they are indeed isomorphic:: @@ -1313,7 +1313,7 @@ def is_isomorphic(self, other, absolutely=False): In the example below, `\phi` and `\psi` are isogenous but not isomorphic:: - sage: psi = phi.velu(t + 1) + sage: psi = phi.velu(tau + 1) sage: phi.is_isomorphic(psi) False @@ -1783,9 +1783,9 @@ def morphism(self): sage: phi.morphism() Ring morphism: 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 + To: Ore Polynomial Ring in τ over Finite Field in z12 of size 5^12 twisted by z12 |--> z12^(5^2) - Defn: T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + Defn: T |--> z12^5*τ^2 + z12^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 sage: from sage.rings.morphism import RingHomomorphism sage: isinstance(phi.morphism(), RingHomomorphism) @@ -1809,7 +1809,7 @@ class the ``__call__`` method of this morphism:: 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^5*τ^2 + z12^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] sage: phi(T) == m.im_gens()[0] True @@ -1899,8 +1899,8 @@ def velu(self, isog): 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 + (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*τ^2 + + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*τ + 2*z12^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 @@ -1963,7 +1963,7 @@ def hom(self, x, codomain=None): sage: K. = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) sage: phi - Drinfeld module defined by T |--> z*t^3 + t^2 + z + Drinfeld module defined by T |--> z*τ^3 + τ^2 + z An important class of endomorphisms of a Drinfeld module `\phi` is given by scalar multiplications, that are endomorphisms @@ -1971,37 +1971,37 @@ def hom(self, x, codomain=None): ring `A`. We construct them as follows:: sage: phi.hom(T) - Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z - Defn: z*t^3 + t^2 + z + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: z*τ^3 + τ^2 + z :: sage: phi.hom(T^2 + 1) - Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z - Defn: z^2*t^6 + (3*z^2 + z + 1)*t^5 + t^4 + 2*z^2*t^3 + (3*z^2 + z + 1)*t^2 + z^2 + 1 + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: z^2*τ^6 + (3*z^2 + z + 1)*τ^5 + τ^4 + 2*z^2*τ^3 + (3*z^2 + z + 1)*τ^2 + z^2 + 1 We can also define a morphism by passing in the Ore polynomial defining it. For example, below, we construct the Frobenius endomorphism of `\phi`:: - sage: t = phi.ore_variable() - sage: phi.hom(t^3) - Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z - Defn: t^3 + sage: tau = phi.ore_variable() + sage: phi.hom(tau^3) + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: τ^3 If the input Ore polynomial defines a morphism to another Drinfeld module, the latter is determined automatically:: - sage: phi.hom(t + 1) + sage: phi.hom(tau + 1) Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^3 + t^2 + z - To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^3 + (3*z^2 + 2*z + 2)*t^2 + (2*z^2 + 3*z + 4)*t + z - Defn: t + 1 + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*τ^3 + (3*z^2 + 2*z + 2)*τ^2 + (2*z^2 + 3*z + 4)*τ + z + Defn: τ + 1 TESTS:: - sage: phi.hom(t) + sage: phi.hom(tau) Traceback (most recent call last): ... ValueError: the input does not define an isogeny @@ -2015,7 +2015,7 @@ def hom(self, x, codomain=None): :: - sage: phi.hom(t + 1, codomain=phi) + sage: phi.hom(tau + 1, codomain=phi) Traceback (most recent call last): ... ValueError: Ore polynomial does not define a morphism @@ -2062,16 +2062,16 @@ def scalar_multiplication(self, x): sage: K. = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) sage: phi - Drinfeld module defined by T |--> z*t^3 + t^2 + z + Drinfeld module defined by T |--> z*τ^3 + τ^2 + z sage: phi.hom(T) # indirect doctest - Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z - Defn: z*t^3 + t^2 + z + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: z*τ^3 + τ^2 + z :: sage: phi.hom(T^2 + 1) - Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z - Defn: z^2*t^6 + (3*z^2 + z + 1)*t^5 + t^4 + 2*z^2*t^3 + (3*z^2 + z + 1)*t^2 + z^2 + 1 + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: z^2*τ^6 + (3*z^2 + z + 1)*τ^5 + τ^4 + 2*z^2*τ^3 + (3*z^2 + z + 1)*τ^2 + z^2 + 1 """ if not self.function_ring().has_coerce_map_from(x.parent()): raise ValueError("%s is not element of the function ring" % x) 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 38c93a70106..685d5b5c465 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 @@ -57,7 +57,7 @@ class DrinfeldModule_finite(DrinfeldModule): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [z6, 0, 5]) sage: phi - Drinfeld module defined by T |--> 5*t^2 + z6 + Drinfeld module defined by T |--> 5*τ^2 + z6 :: @@ -84,8 +84,8 @@ class DrinfeldModule_finite(DrinfeldModule): sage: frobenius_endomorphism = phi.frobenius_endomorphism() sage: frobenius_endomorphism - Endomorphism of Drinfeld module defined by T |--> 5*t^2 + z6 - Defn: t^2 + Endomorphism of Drinfeld module defined by T |--> 5*τ^2 + z6 + Defn: τ^2 Its characteristic polynomial can be computed:: @@ -235,7 +235,7 @@ def frobenius_endomorphism(self): 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`. + defining Ore polynomial is `τ^q`. EXAMPLES:: @@ -244,8 +244,8 @@ def frobenius_endomorphism(self): sage: K. = Fq.extension(2) sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: phi.frobenius_endomorphism() - Endomorphism of Drinfeld module defined by T |--> z6*t^2 + 1 - Defn: t^2 + Endomorphism of Drinfeld module defined by T |--> z6*τ^2 + 1 + Defn: τ^2 TESTS:: @@ -273,14 +273,14 @@ def frobenius_charpoly(self, var='X', algorithm=None): Let `\chi = X^r + \sum_{i=0}^{r-1} A_{i}(T)X^{i}` be the characteristic polynomial of the Frobenius endomorphism, and - let `t^n` be the Ore polynomial that defines the Frobenius + let `τ^n` be the Ore polynomial that defines the Frobenius endomorphism of `\phi`; by definition, `n` is the degree of `K` over the base field `\mathbb{F}_q`. Then we have .. MATH:: - \chi(t^n)(\phi(T)) - = t^{nr} + \sum_{i=1}^{r} \phi_{A_{i}}t^{n(i)} + \chi(τ^n)(\phi(T)) + = τ^{nr} + \sum_{i=1}^{r} \phi_{A_{i}}τ^{n(i)} = 0, with `\deg(A_i) \leq \frac{n(r-i)}{r}`. @@ -340,7 +340,7 @@ def frobenius_charpoly(self, var='X', algorithm=None): sage: chi(frob_pol, phi(T)) 0 sage: phi.frobenius_charpoly(algorithm='motive')(phi.frobenius_endomorphism()) - Endomorphism of Drinfeld module defined by T |--> z6*t^2 + 1 + Endomorphism of Drinfeld module defined by T |--> z6*τ^2 + 1 Defn: 0 :: @@ -935,15 +935,15 @@ 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_polring().gen() - sage: phi.invert(t + 1) + sage: tau = phi.ore_variable() + sage: phi.invert(tau + 1) Traceback (most recent call last): ... ValueError: input must be in the image of the Drinfeld module :: - sage: phi.invert(t^4 + t^2 + 1) + sage: phi.invert(tau^4 + tau^2 + 1) Traceback (most recent call last): ... ValueError: input must be in the image of the Drinfeld module @@ -1091,7 +1091,7 @@ def is_supersingular(self): sage: phi.is_supersingular() True sage: phi(phi.characteristic()) # Purely inseparable - z6*t^2 + z6*τ^2 In rank two, a Drinfeld module is either ordinary or supersinguler. In higher ranks, it could be neither of diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index faf6b45c5b4..7dac2d93194 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -41,16 +41,16 @@ class DrinfeldModuleMorphismAction(Action): sage: phi = DrinfeldModule(A, [z, 1, z]) sage: psi = DrinfeldModule(A, [z, z^2 + 4*z + 3, 2*z^2 + 4*z + 4]) sage: H = Hom(phi, psi) - sage: t = phi.ore_variable() - sage: f = H(t + 2) + sage: tau = phi.ore_variable() + sage: f = H(tau + 2) Left action:: sage: (T + 1) * f Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^2 + t + z - To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^2 + (z^2 + 4*z + 3)*t + z - Defn: (2*z^2 + 4*z + 4)*t^3 + (2*z + 1)*t^2 + (2*z^2 + 4*z + 2)*t + 2*z + 2 + From: Drinfeld module defined by T |--> z*τ^2 + τ + z + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*τ^2 + (z^2 + 4*z + 3)*τ + z + Defn: (2*z^2 + 4*z + 4)*τ^3 + (2*z + 1)*τ^2 + (2*z^2 + 4*z + 2)*τ + 2*z + 2 Right action currently does not work (it is a known bug, due to an incompatibility between multiplication of morphisms and the coercion @@ -60,9 +60,9 @@ class DrinfeldModuleMorphismAction(Action): Traceback (most recent call last): ... TypeError: right (=T + 1) must be a map to multiply it by Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^2 + t + z - To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^2 + (z^2 + 4*z + 3)*t + z - Defn: t + 2 + From: Drinfeld module defined by T |--> z*τ^2 + τ + z + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*τ^2 + (z^2 + 4*z + 3)*τ + z + Defn: τ + 2 """ def __init__(self, A, H, is_left, op): r""" @@ -114,9 +114,9 @@ def _act_(self, a, f): sage: f = phi.hom(t + 1) sage: T*f # indirect doctest Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^3 + t^2 + z - To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^3 + (3*z^2 + 2*z + 2)*t^2 + (2*z^2 + 3*z + 4)*t + z - Defn: (2*z^2 + 4*z + 4)*t^4 + (z + 1)*t^3 + t^2 + (2*z^2 + 4*z + 4)*t + z + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*τ^3 + (3*z^2 + 2*z + 2)*τ^2 + (2*z^2 + 3*z + 4)*τ + z + Defn: (2*z^2 + 4*z + 4)*τ^4 + (z + 1)*τ^3 + τ^2 + (2*z^2 + 4*z + 4)*τ + z """ u = f.ore_polynomial() if self._is_left: @@ -147,8 +147,8 @@ class DrinfeldModuleHomset(Homset): 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 + from (gen) 2*τ^2 + z6*τ + z6 + to (gen) 2*τ^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*τ + z6 :: @@ -160,7 +160,7 @@ class DrinfeldModuleHomset(Homset): 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 + Set of Drinfeld module morphisms from (gen) 2*τ^2 + z6*τ + z6 to (gen) 2*τ^2 + z6*τ + z6 sage: E is Hom(phi, phi) True @@ -185,39 +185,39 @@ class DrinfeldModuleHomset(Homset): sage: identity_morphism = E(1) sage: identity_morphism - Identity morphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 + Identity morphism of Drinfeld module defined by T |--> 2*τ^2 + z6*τ + z6 :: - sage: t = phi.ore_polring().gen() - sage: frobenius_endomorphism = E(t^6) + sage: tau = phi.ore_variable() + sage: frobenius_endomorphism = E(tau^6) sage: frobenius_endomorphism - Endomorphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 - Defn: t^6 + Endomorphism of Drinfeld module defined by T |--> 2*τ^2 + z6*τ + z6 + Defn: τ^6 :: - sage: isogeny = H(t + 1) + sage: isogeny = H(tau + 1) sage: isogeny Drinfeld Module morphism: - 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 + From: Drinfeld module defined by T |--> 2*τ^2 + z6*τ + z6 + To: Drinfeld module defined by T |--> 2*τ^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*τ + z6 + Defn: τ + 1 And one can test if an Ore polynomial defines a morphism using the ``in`` syntax:: sage: 1 in H False - sage: t^6 in H + sage: tau^6 in H False - sage: t + 1 in H + sage: tau + 1 in H True sage: 1 in E True - sage: t^6 in E + sage: tau^6 in E True - sage: t + 1 in E + sage: tau + 1 in E False This also works if the candidate is a morphism object:: @@ -293,7 +293,7 @@ def _latex_(self): sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) 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} + \text{Set{ }of{ }Drinfeld{ }module{ }morphisms{ }from{ }(gen){ }}2 τ^{2} + z_{6} τ + z_{6}\text{{ }to{ }(gen){ }}2 τ^{2} + \left(2 z_{6}^{5} + 2 z_{6}^{4} + 2 z_{6} + 1\right) τ + z_{6} """ return f'\\text{{Set{{ }}of{{ }}Drinfeld{{ }}module{{ }}morphisms' \ f'{{ }}from{{ }}(gen){{ }}}}{latex(self.domain().gen())}' \ @@ -313,7 +313,7 @@ def _repr_(self): sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) 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 + Set of Drinfeld module morphisms from (gen) 2*τ^2 + z6*τ + z6 to (gen) 2*τ^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*τ + z6 """ return f'Set of Drinfeld module morphisms from (gen) '\ f'{self.domain().gen()} to (gen) {self.codomain().gen()}' @@ -337,23 +337,23 @@ def __contains__(self, x): sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: H = Hom(phi, psi) sage: E = End(phi) - sage: t = phi.ore_polring().gen() + sage: tau = phi.ore_variable() sage: 1 in H False - sage: t^6 in H + sage: tau^6 in H False - sage: t + 1 in H + sage: tau + 1 in H True sage: 1 in E True - sage: t^6 in E + sage: tau^6 in E True - sage: t + 1 in E + sage: tau + 1 in E False Whereas the input is now a Drinfeld module morphism:: - sage: isogeny = H(t + 1) + sage: isogeny = H(tau + 1) sage: isogeny in H True sage: E(0) in E @@ -385,33 +385,33 @@ def _element_constructor_(self, *args, **kwds): sage: psi = DrinfeldModule(A, [z6, 2*z6^5 + 2*z6^4 + 2*z6 + 1, 2]) sage: H = Hom(phi, psi) sage: E = End(phi) - sage: t = phi.ore_polring().gen() + sage: tau = phi.ore_variable() sage: identity_morphism = E(1) sage: identity_morphism - Identity morphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 + Identity morphism of Drinfeld module defined by T |--> 2*τ^2 + z6*τ + z6 :: sage: scalar_multiplication = E(T) sage: scalar_multiplication - Endomorphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 - Defn: 2*t^2 + z6*t + z6 + Endomorphism of Drinfeld module defined by T |--> 2*τ^2 + z6*τ + z6 + Defn: 2*τ^2 + z6*τ + z6 :: - sage: frobenius_endomorphism = E(t^6) + sage: frobenius_endomorphism = E(tau^6) sage: frobenius_endomorphism - Endomorphism of Drinfeld module defined by T |--> 2*t^2 + z6*t + z6 - Defn: t^6 + Endomorphism of Drinfeld module defined by T |--> 2*τ^2 + z6*τ + z6 + Defn: τ^6 :: - sage: isogeny = H(t + 1) + sage: isogeny = H(tau + 1) sage: isogeny Drinfeld Module morphism: - 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 + From: Drinfeld module defined by T |--> 2*τ^2 + z6*τ + z6 + To: Drinfeld module defined by T |--> 2*τ^2 + (2*z6^5 + 2*z6^4 + 2*z6 + 1)*τ + z6 + Defn: τ + 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 beff948f980..2c7bf3b19c8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -48,15 +48,15 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, sage: A. = Fq[] 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 + z^5 + z^3 + z + 1 + sage: tau = phi.ore_variable() + sage: ore_pol = tau + 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: 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 + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z)*τ + z + To: Drinfeld module defined by T |--> (z^5 + z^2 + z + 1)*τ^2 + (z^4 + z + 1)*τ + z + Defn: τ + z^5 + z^3 + z + 1 The given Ore polynomial must indeed define a morphism:: @@ -69,19 +69,19 @@ class DrinfeldModuleMorphism(Morphism, UniqueRepresentation, One can get basic data on the morphism:: sage: morphism.domain() - Drinfeld module defined by T |--> (z^2 + z)*t^2 + (z^2 + z)*t + z + Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z)*τ + z sage: morphism.domain() is phi True sage: morphism.codomain() - Drinfeld module defined by T |--> (z^5 + z^2 + z + 1)*t^2 + (z^4 + z + 1)*t + z + Drinfeld module defined by T |--> (z^5 + z^2 + z + 1)*τ^2 + (z^4 + z + 1)*τ + z sage: morphism.codomain() is psi True :: sage: morphism.ore_polynomial() - t + z^5 + z^3 + z + 1 + τ + z^5 + z^3 + z + 1 sage: morphism.ore_polynomial() is ore_pol True @@ -117,9 +117,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: 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 + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z)*τ + z + To: Drinfeld module defined by T |--> (z^5 + z^2 + z + 1)*τ^2 + (z^4 + z + 1)*τ + z + Defn: τ + z^5 + z^3 + z + 1 sage: DrinfeldModuleMorphism(Hom(phi, psi), ore_pol) is morphism True """ @@ -145,21 +145,21 @@ def __classcall_private__(cls, parent, x): sage: K. = Fq.extension(6) sage: phi = DrinfeldModule(A, [z6, 1, 1]) sage: End(phi)(T + 1) - Endomorphism of Drinfeld module defined by T |--> t^2 + t + z6 - Defn: t^2 + t + z6 + 1 + Endomorphism of Drinfeld module defined by T |--> τ^2 + τ + z6 + Defn: τ^2 + τ + z6 + 1 :: - sage: t = phi.ore_polring().gen() + sage: tau = phi.ore_variable() sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1]) - sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1) + sage: morphism = Hom(phi, psi)(tau + 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) + sage: morphism = DrinfeldModuleMorphism(Sets(), tau + 1) Traceback (most recent call last): ... TypeError: parent should be a DrinfeldModuleHomset @@ -203,13 +203,13 @@ def __init__(self, parent, ore_pol): 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + 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 + sage: morphism._ore_polynomial == tau + z6^5 + z6^2 + 1 True """ super().__init__(parent) @@ -228,10 +228,10 @@ def _latex_(self): 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + z6^5 + z6^2 + 1) sage: latex(morphism) - t + z_{6}^{5} + z_{6}^{2} + 1 + τ + z_{6}^{5} + z_{6}^{2} + 1 """ return f'{latex(self._ore_polynomial)}' @@ -246,13 +246,13 @@ def _repr_(self): 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + z6^5 + z6^2 + 1) sage: morphism Drinfeld Module morphism: - 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 + From: Drinfeld module defined by T |--> τ^2 + τ + z6 + To: Drinfeld module defined by T |--> τ^2 + (z6^4 + z6^2 + 1)*τ + z6 + Defn: τ + z6^5 + z6^2 + 1 """ if self.is_identity(): return f'Identity morphism of {self._domain}' @@ -276,8 +276,8 @@ def __hash__(self): 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + z6^5 + z6^2 + 1) sage: hash(morphism) # random -4214883752078138009 """ @@ -294,8 +294,8 @@ def is_zero(self): 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + z6^5 + z6^2 + 1) sage: morphism.is_zero() False @@ -324,8 +324,8 @@ def is_identity(self): :: 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + z6^5 + z6^2 + 1) sage: morphism.is_identity() False """ @@ -342,8 +342,8 @@ def is_isogeny(self): 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + z6^5 + z6^2 + 1) sage: morphism.is_isogeny() True @@ -378,8 +378,8 @@ def is_isomorphism(self): 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + z6^5 + z6^2 + 1) sage: morphism.is_isomorphism() False @@ -414,11 +414,11 @@ def ore_polynomial(self): 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: tau = phi.ore_variable() + sage: morphism = Hom(phi, psi)(tau + z6^5 + z6^2 + 1) sage: ore_pol = morphism.ore_polynomial() sage: ore_pol - t + z6^5 + z6^2 + 1 + τ + z6^5 + z6^2 + 1 :: @@ -443,21 +443,21 @@ def __add__(self, other): sage: A. = Fq[] sage: K. = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) - sage: t = phi.ore_variable() - sage: f = phi.hom(t + 1) + sage: tau = phi.ore_variable() + sage: f = phi.hom(tau + 1) sage: g = T * f sage: f + g # indirect doctest Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^3 + t^2 + z - To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^3 + (3*z^2 + 2*z + 2)*t^2 + (2*z^2 + 3*z + 4)*t + z - Defn: (2*z^2 + 4*z + 4)*t^4 + (z + 1)*t^3 + t^2 + (2*z^2 + 4*z)*t + z + 1 + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*τ^3 + (3*z^2 + 2*z + 2)*τ^2 + (2*z^2 + 3*z + 4)*τ + z + Defn: (2*z^2 + 4*z + 4)*τ^4 + (z + 1)*τ^3 + τ^2 + (2*z^2 + 4*z)*τ + z + 1 We check that coercion from the function ring works:: sage: F = phi.frobenius_endomorphism() sage: F + T - Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z - Defn: (z + 1)*t^3 + t^2 + z + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: (z + 1)*τ^3 + τ^2 + z """ return self.parent()(self.ore_polynomial() + other.ore_polynomial()) @@ -473,11 +473,11 @@ def _composition_(self, other, H): sage: phi = DrinfeldModule(A, [z, 1, z, z^2]) sage: f = phi.frobenius_endomorphism() sage: f - Endomorphism of Drinfeld module defined by T |--> z^2*t^3 + z*t^2 + t + z - Defn: t^3 + Endomorphism of Drinfeld module defined by T |--> z^2*τ^3 + z*τ^2 + τ + z + Defn: τ^3 sage: f * f # indirect doctest - Endomorphism of Drinfeld module defined by T |--> z^2*t^3 + z*t^2 + t + z - Defn: t^6 + Endomorphism of Drinfeld module defined by T |--> z^2*τ^3 + z*τ^2 + τ + z + Defn: τ^6 """ return H(self.ore_polynomial() * other.ore_polynomial()) @@ -495,10 +495,10 @@ def inverse(self): sage: K. = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 1, z, z^2]) sage: f = phi.hom(2); f - Endomorphism of Drinfeld module defined by T |--> z^2*t^3 + z*t^2 + t + z + Endomorphism of Drinfeld module defined by T |--> z^2*τ^3 + z*τ^2 + τ + z Defn: 2 sage: f.inverse() - Endomorphism of Drinfeld module defined by T |--> z^2*t^3 + z*t^2 + t + z + Endomorphism of Drinfeld module defined by T |--> z^2*τ^3 + z*τ^2 + τ + z Defn: 3 Inversion of general isomorphisms between different Drinfeld modules @@ -506,13 +506,13 @@ def inverse(self): sage: g = phi.hom(z); g Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z^2*t^3 + z*t^2 + t + z - To: Drinfeld module defined by T |--> z^2*t^3 + (z^2 + 2*z + 3)*t^2 + (z^2 + 3*z)*t + z + From: Drinfeld module defined by T |--> z^2*τ^3 + z*τ^2 + τ + z + To: Drinfeld module defined by T |--> z^2*τ^3 + (z^2 + 2*z + 3)*τ^2 + (z^2 + 3*z)*τ + z Defn: z sage: g.inverse() Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z^2*t^3 + (z^2 + 2*z + 3)*t^2 + (z^2 + 3*z)*t + z - To: Drinfeld module defined by T |--> z^2*t^3 + z*t^2 + t + z + From: Drinfeld module defined by T |--> z^2*τ^3 + (z^2 + 2*z + 3)*τ^2 + (z^2 + 3*z)*τ + z + To: Drinfeld module defined by T |--> z^2*τ^3 + z*τ^2 + τ + z Defn: 3*z^2 + 4 When the morphism is not invertible, an error is raised:: @@ -564,8 +564,8 @@ def _motive_matrix(self): sage: A. = Fq[] sage: K. = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1]) - sage: t = phi.ore_variable() - sage: u = t^2 + (2*z^2 + 3*z + 3)*t + (2*z + 3) + sage: tau = phi.ore_variable() + sage: u = tau^2 + (2*z^2 + 3*z + 3)*tau + (2*z + 3) sage: f = phi.hom(u) sage: f._motive_matrix() [ T + 3 + z 3 + 3*z + 2*z^2] @@ -621,8 +621,8 @@ def norm(self, ideal=True): sage: A. = Fq[] sage: K. = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) - sage: t = phi.ore_variable() - sage: f = phi.hom(t + 1) + sage: tau = phi.ore_variable() + sage: f = phi.hom(tau + 1) sage: f.norm() Principal ideal (T + 4) of Univariate Polynomial Ring in T over Finite Field of size 5 @@ -682,19 +682,19 @@ def dual_isogeny(self): sage: A. = Fq[] sage: K. = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) - sage: t = phi.ore_variable() - sage: f = phi.hom(t + 1) + sage: tau = phi.ore_variable() + sage: f = phi.hom(tau + 1) sage: f Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^3 + t^2 + z - To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^3 + (3*z^2 + 2*z + 2)*t^2 + (2*z^2 + 3*z + 4)*t + z - Defn: t + 1 + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*τ^3 + (3*z^2 + 2*z + 2)*τ^2 + (2*z^2 + 3*z + 4)*τ + z + Defn: τ + 1 sage: g = f.dual_isogeny() sage: g Drinfeld Module morphism: - From: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^3 + (3*z^2 + 2*z + 2)*t^2 + (2*z^2 + 3*z + 4)*t + z - To: Drinfeld module defined by T |--> z*t^3 + t^2 + z - Defn: z*t^2 + (4*z + 1)*t + z + 4 + From: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*τ^3 + (3*z^2 + 2*z + 2)*τ^2 + (2*z^2 + 3*z + 4)*τ + z + To: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: z*τ^2 + (4*z + 1)*τ + z + 4 We check that `f \circ g` (resp. `g \circ f`) is the multiplication by the norm of `f`:: @@ -760,8 +760,8 @@ def characteristic_polynomial(self, var='X'): TESTS:: - sage: t = phi.ore_variable() - sage: isog = phi.hom(t + 1) + sage: tau = phi.ore_variable() + sage: isog = phi.hom(tau + 1) sage: isog.characteristic_polynomial() Traceback (most recent call last): ... @@ -800,7 +800,7 @@ def charpoly(self, var='X'): morphism (Cayley-Hamilton's theorem):: sage: chi(f) - Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z Defn: 0 We verify, on an example, that the characteristic polynomial From 27e890b5b1771607f0ad009cba2b010abf71ae46 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 17 Jul 2025 11:19:59 +0200 Subject: [PATCH 02/13] declare encoding --- src/sage/rings/function_field/drinfeld_modules/action.py | 1 + .../function_field/drinfeld_modules/charzero_drinfeld_module.py | 1 + .../rings/function_field/drinfeld_modules/drinfeld_module.py | 1 + .../function_field/drinfeld_modules/finite_drinfeld_module.py | 1 + src/sage/rings/function_field/drinfeld_modules/homset.py | 1 + src/sage/rings/function_field/drinfeld_modules/morphism.py | 1 + 6 files changed, 6 insertions(+) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 06f8897326d..7cb428452b1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" The module action induced by a Drinfeld module diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 99dd8dc9ce5..4c0c4b21437 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # sage.doctest: optional - sage.rings.finite_rings r""" Drinfeld modules over rings of characteristic zero 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 4d26afc2abb..1720d445ae2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" Drinfeld modules 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 685d5b5c465..599aa775d72 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,3 +1,4 @@ +# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" Finite Drinfeld modules diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index 7dac2d93194..e1918686519 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" Set of morphisms between two Drinfeld modules diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 2c7bf3b19c8..490adc634ea 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" Drinfeld module morphisms From e82e19addc82e4a4f92e60e988bf54c88f855eb2 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 17 Jul 2025 17:06:36 +0200 Subject: [PATCH 03/13] relative Frobenius --- .../finite_drinfeld_module.py | 36 +++++++++++++++++++ 1 file changed, 36 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 38c93a70106..590f9446787 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 @@ -907,6 +907,42 @@ def frobenius_trace(self, algorithm=None): # If all fail, we raise an error raise NotImplementedError(f'algorithm "{algorithm}" not implemented') + def frobenius_relative(self): + r""" + Return the relative Frobenius of this Drinfeld module, that is + the isogeny of the form `\tau^d` with `d` minimal. + + We note that `d` is the degree of `\gamma(T)` over the `\mathbb F_q`. + + EXAMPLES:: + + sage: Fq = GF(5) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: phi = DrinfeldModule(A, [1, z, z]) + sage: phi.frobenius_relative() + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> z*t^2 + z*t + 1 + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^2 + (2*z^2 + 4*z + 4)*t + 1 + Defn: t + + When the constant coefficient generates the field `K`, the relative + Frobenius is the same as the Frobenius endomorphism:: + + sage: psi = DrinfeldModule(A, [z, z, 1]) + sage: psi.frobenius_relative() + Endomorphism of Drinfeld module defined by T |--> t^2 + z*t + z + Defn: t^3 + sage: psi.frobenius_endomorphism() + Endomorphism of Drinfeld module defined by T |--> t^2 + z*t + z + Defn: t^3 + """ + E = self.base_over_constants_field() + z = E(self.constant_coefficient()) + d = z.minpoly().degree() + tau = self.ore_variable() + return self.hom(tau**d) + def invert(self, ore_pol): r""" Return the preimage of the input under the Drinfeld module, if it From 606d762b06f5b36adabec877f6930724dbc84ef2 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Thu, 17 Jul 2025 17:26:27 +0200 Subject: [PATCH 04/13] add the argument n, and move code in DrinfeldModule --- .../drinfeld_modules/drinfeld_module.py | 64 +++++++++++++++++++ .../finite_drinfeld_module.py | 36 ----------- 2 files changed, 64 insertions(+), 36 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 f65b6753831..23e06784da4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -2084,3 +2084,67 @@ def scalar_multiplication(self, x): if not self.function_ring().has_coerce_map_from(x.parent()): raise ValueError("%s is not element of the function ring" % x) return self.Hom(self)(x) + + def frobenius_relative(self, n=1): + r""" + Return the `n`-th iterate relative Frobenius of this Drinfeld module. + + By definition, the relative Frobenius is the isogeny represented by + the Ore polynomial `tau^d` where `d` is the degree of the characteristic + of this Drinfeld module (which is also the degree of `\gamma(T)` over + the `\mathbb F_q`). + + INPUT: + + - ``n`` -- a nonnegative integer (default: ``1``) + + EXAMPLES:: + + sage: Fq = GF(5) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: phi = DrinfeldModule(A, [1, z, z]) + sage: phi.frobenius_relative() + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> z*t^2 + z*t + 1 + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^2 + (2*z^2 + 4*z + 4)*t + 1 + Defn: t + sage: phi.frobenius_relative(2) + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> z*t^2 + z*t + 1 + To: Drinfeld module defined by T |--> (3*z^2 + 1)*t^2 + (3*z^2 + 1)*t + 1 + Defn: t^2 + + When `n` is the degree of `F` over `\FF_q(\gamma(T))`, we obtain + the Frobenius endomorphism:: + + sage: phi.frobenius_relative(3) == phi.frobenius_endomorphism() + True + + In particular, when `\gamma(T)` generates the field `K`, the relative + Frobenius is the same as the Frobenius endomorphism:: + + sage: psi = DrinfeldModule(A, [z, z, 1]) + sage: psi.frobenius_relative() + Endomorphism of Drinfeld module defined by T |--> t^2 + z*t + z + Defn: t^3 + sage: psi.frobenius_endomorphism() + Endomorphism of Drinfeld module defined by T |--> t^2 + z*t + z + Defn: t^3 + + When the characteristic is zero, the relative Frobenius is not defined + and an error is raised:: + + sage: R. = Fq[] + sage: K. = Frac(R) + sage: phi = DrinfeldModule(A, [1, z]) + sage: phi.frobenius_relative() + Traceback (most recent call last): + ... + NotImplementedError: function ring characteristic not implemented in this case + """ + tau = self.ore_variable() + d = self.characteristic().degree() + if d < 0: + raise ValueError("the characteristic of the Drinfeld module must be nonzero") + return self.hom(tau**(n*d)) 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 590f9446787..38c93a70106 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 @@ -907,42 +907,6 @@ def frobenius_trace(self, algorithm=None): # If all fail, we raise an error raise NotImplementedError(f'algorithm "{algorithm}" not implemented') - def frobenius_relative(self): - r""" - Return the relative Frobenius of this Drinfeld module, that is - the isogeny of the form `\tau^d` with `d` minimal. - - We note that `d` is the degree of `\gamma(T)` over the `\mathbb F_q`. - - EXAMPLES:: - - sage: Fq = GF(5) - sage: A. = Fq[] - sage: K. = Fq.extension(3) - sage: phi = DrinfeldModule(A, [1, z, z]) - sage: phi.frobenius_relative() - Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^2 + z*t + 1 - To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^2 + (2*z^2 + 4*z + 4)*t + 1 - Defn: t - - When the constant coefficient generates the field `K`, the relative - Frobenius is the same as the Frobenius endomorphism:: - - sage: psi = DrinfeldModule(A, [z, z, 1]) - sage: psi.frobenius_relative() - Endomorphism of Drinfeld module defined by T |--> t^2 + z*t + z - Defn: t^3 - sage: psi.frobenius_endomorphism() - Endomorphism of Drinfeld module defined by T |--> t^2 + z*t + z - Defn: t^3 - """ - E = self.base_over_constants_field() - z = E(self.constant_coefficient()) - d = z.minpoly().degree() - tau = self.ore_variable() - return self.hom(tau**d) - def invert(self, ore_pol): r""" Return the preimage of the input under the Drinfeld module, if it From 2caec64f2344fbea9b2a16f2bebd5e37d30005b4 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 18 Jul 2025 21:37:20 +0200 Subject: [PATCH 05/13] some remaining t --- .../function_field/drinfeld_modules/drinfeld_module.py | 10 +++++----- .../drinfeld_modules/finite_drinfeld_module.py | 4 ++-- 2 files changed, 7 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 1720d445ae2..7e2de8a4161 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -128,7 +128,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): - ``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: ``'τ'``) the name of the Ore polynomial ring generator .. RUBRIC:: Construction @@ -534,8 +534,8 @@ def __classcall_private__(cls, function_ring, gen, name='τ'): - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` -- (default: ``'τ'``) the name of the Ore polynomial - ring gen + - ``name`` -- (default: ``'τ'``) the name of the variable of + the Ore polynomial OUTPUT: a DrinfeldModule or DrinfeldModule_finite @@ -646,8 +646,8 @@ def __init__(self, gen, category): - ``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: ``'τ'``) the name of the variable of + the Ore polynomial ring 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 599aa775d72..f0bf581caa2 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 @@ -139,8 +139,8 @@ def __init__(self, gen, category): - ``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: ``'τ'``) the name of the variable of + the Ore polynomial ring TESTS:: From b1a5d1e3a9e08feba952910533533fa81f43efd7 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 12 Aug 2025 05:42:51 +0200 Subject: [PATCH 06/13] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../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 0e1baadb668..75c8f19edba 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -2084,7 +2084,7 @@ def frobenius_relative(self, n=1): By definition, the relative Frobenius is the isogeny represented by the Ore polynomial `tau^d` where `d` is the degree of the characteristic of this Drinfeld module (which is also the degree of `\gamma(T)` over - the `\mathbb F_q`). + `\mathbb F_q`, where `\gamma` is the base morphism `\mathbb F_q[T] \to F`). INPUT: From da2183637788cd7524bd31107315ff2ff37d522f Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Tue, 12 Aug 2025 07:05:33 +0200 Subject: [PATCH 07/13] =?UTF-8?q?t=20->=20=CF=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- conftest.py | 343 ------------------ .../drinfeld_modules/drinfeld_module.py | 24 +- 2 files changed, 12 insertions(+), 355 deletions(-) delete mode 100644 conftest.py diff --git a/conftest.py b/conftest.py deleted file mode 100644 index 98fc948afa7..00000000000 --- a/conftest.py +++ /dev/null @@ -1,343 +0,0 @@ -# pyright: strict -"""Configuration and fixtures for pytest. - -This file configures pytest and provides some global fixtures. -See https://docs.pytest.org/en/latest/index.html for more details. -""" - -from __future__ import annotations - -import doctest -import inspect -import sys -import warnings -from pathlib import Path -from typing import Any, Iterable, Optional - -import pytest -from _pytest.doctest import ( - DoctestItem, - DoctestModule, - _get_continue_on_failure, - _get_runner, - _is_mocked, - _patch_unwrap_mock_aware, - get_optionflags, -) -from _pytest.pathlib import ImportMode, import_path - -from sage.doctest.forker import ( - init_sage, - showwarning_with_traceback, -) -from sage.doctest.parsing import SageDocTestParser, SageOutputChecker - - -class SageDoctestModule(DoctestModule): - """ - This is essentially a copy of `DoctestModule` from - https://github.com/pytest-dev/pytest/blob/main/src/_pytest/doctest.py. - The only change is that we use `SageDocTestParser` to extract the doctests - and `SageOutputChecker` to verify the output. - """ - - def collect(self) -> Iterable[DoctestItem]: - import doctest - - class MockAwareDocTestFinder(doctest.DocTestFinder): - """A hackish doctest finder that overrides stdlib internals to fix a stdlib bug. - https://github.com/pytest-dev/pytest/issues/3456 - https://bugs.python.org/issue25532 - """ - - def __init__(self) -> None: - super().__init__(parser=SageDocTestParser(set(["sage"]))) - - def _find_lineno(self, obj, source_lines): - """Doctest code does not take into account `@property`, this - is a hackish way to fix it. https://bugs.python.org/issue17446 - Wrapped Doctests will need to be unwrapped so the correct - line number is returned. This will be reported upstream. #8796 - """ - if isinstance(obj, property): - obj = getattr(obj, "fget", obj) - - if hasattr(obj, "__wrapped__"): - # Get the main obj in case of it being wrapped - obj = inspect.unwrap(obj) - - # Type ignored because this is a private function. - return super()._find_lineno( # type:ignore[misc] - obj, - source_lines, - ) - - def _find( - self, tests, obj, name, module, source_lines, globs, seen - ) -> None: - if _is_mocked(obj): - return - with _patch_unwrap_mock_aware(): - # Type ignored because this is a private function. - super()._find( # type:ignore[misc] - tests, obj, name, module, source_lines, globs, seen - ) - - if self.path.name == "conftest.py": - module = self.config.pluginmanager._importconftest( - self.path, - self.config.getoption("importmode"), - rootpath=self.config.rootpath, - consider_namespace_packages=True, - ) - else: - try: - module = import_path( - self.path, - mode=ImportMode.importlib, - root=self.config.rootpath, - consider_namespace_packages=True, - ) - except ImportError as exception: - if self.config.getvalue("doctest_ignore_import_errors"): - pytest.skip("unable to import module %r" % self.path) - else: - if isinstance(exception, ModuleNotFoundError): - # Ignore some missing features/modules for now - # TODO: Remove this once all optional things are using Features - if exception.name in ( - "valgrind", - "rpy2", - "sage.libs.coxeter3.coxeter", - ): - pytest.skip( - f"unable to import module { self.path } due to missing feature { exception.name }" - ) - raise - # Uses internal doctest module parsing mechanism. - finder = MockAwareDocTestFinder() - optionflags = get_optionflags(self.config) - from sage.features import FeatureNotPresentError - - runner = _get_runner( - verbose=False, - optionflags=optionflags, - checker=SageOutputChecker(), - continue_on_failure=_get_continue_on_failure(self.config), - ) - try: - for test in finder.find(module, module.__name__): - if test.examples: # skip empty doctests - yield DoctestItem.from_parent( - self, name=test.name, runner=runner, dtest=test - ) - except FeatureNotPresentError as exception: - pytest.skip( - f"unable to import module { self.path } due to missing feature { exception.feature.name }" - ) - except ModuleNotFoundError as exception: - # TODO: Remove this once all optional things are using Features - pytest.skip( - f"unable to import module { self.path } due to missing module { exception.name }" - ) - - -class IgnoreCollector(pytest.Collector): - """ - Ignore a file. - """ - - def __init__(self, parent: pytest.Collector) -> None: - super().__init__("ignore", parent) - - def collect(self) -> Iterable[pytest.Item | pytest.Collector]: - return [] - - -def pytest_collect_file( - file_path: Path, parent: pytest.Collector -) -> pytest.Collector | None: - """ - This hook is called when collecting test files, and can be used to - modify the file or test selection logic by returning a list of - ``pytest.Item`` objects which the ``pytest`` command will directly - add to the list of test items. - - See `pytest documentation `_. - """ - if ( - file_path.parent.name == "combinat" - or file_path.parent.parent.name == "combinat" - ): - # Crashes CI for some reason - return IgnoreCollector.from_parent(parent) - if file_path.suffix == ".pyx": - # We don't allow pytests to be defined in Cython files. - # Normally, Cython files are filtered out already by pytest and we only - # hit this here if someone explicitly runs `pytest some_file.pyx`. - return IgnoreCollector.from_parent(parent) - elif file_path.suffix == ".py": - if parent.config.option.doctest: - if file_path.name == "__main__.py" or file_path.name == "setup.py": - # We don't allow tests to be defined in __main__.py/setup.py files (because their import will fail). - return IgnoreCollector.from_parent(parent) - if ( - ( - file_path.name == "postprocess.py" - and file_path.parent.name == "nbconvert" - ) - or ( - file_path.name == "giacpy-mkkeywords.py" - and file_path.parent.name == "autogen" - ) - or ( - file_path.name == "flint_autogen.py" - and file_path.parent.name == "autogen" - ) - ): - # This is an executable file. - return IgnoreCollector.from_parent(parent) - - if ( - ( - file_path.name == "finite_dimensional_lie_algebras_with_basis.py" - and file_path.parent.name == "categories" - ) - or ( - file_path.name == "__init__.py" - and file_path.parent.name == "crypto" - ) - or (file_path.name == "__init__.py" and file_path.parent.name == "mq") - ): - # TODO: Fix these (import fails with "RuntimeError: dictionary changed size during iteration") - return IgnoreCollector.from_parent(parent) - - if ( - file_path.name in ("forker.py", "reporting.py") - ) and file_path.parent.name == "doctest": - # Fails with many errors due to different testing framework - return IgnoreCollector.from_parent(parent) - - if ( - ( - file_path.name == "arithgroup_generic.py" - and file_path.parent.name == "arithgroup" - ) - or ( - file_path.name == "pari.py" - and file_path.parent.name == "lfunctions" - ) - or ( - file_path.name == "permgroup_named.py" - and file_path.parent.name == "perm_gps" - ) - or ( - file_path.name == "finitely_generated.py" - and file_path.parent.name == "matrix_gps" - ) - or ( - file_path.name == "libgap_mixin.py" - and file_path.parent.name == "groups" - ) - or ( - file_path.name == "finitely_presented.py" - and file_path.parent.name == "groups" - ) - or ( - file_path.name == "classical_geometries.py" - and file_path.parent.name == "generators" - ) - ): - # Fails with "Fatal Python error" - return IgnoreCollector.from_parent(parent) - - return SageDoctestModule.from_parent(parent, path=file_path) - - -def pytest_addoption(parser): - # Add a command line option to run doctests - # (we don't use the built-in --doctest-modules option because then doctests are collected twice) - group = parser.getgroup("collect") - group.addoption( - "--doctest", - action="store_true", - default=False, - help="Run doctests in all .py modules", - dest="doctest", - ) - - -# Monkey patch exception printing to replace the full qualified name of the exception by its short name -# TODO: Remove this hack once migration to pytest is complete -import traceback - -old_format_exception_only = traceback.format_exception_only - - -def format_exception_only(etype: type, value: BaseException) -> list[str]: - formatted_exception = old_format_exception_only(etype, value) - exception_name = etype.__name__ - if etype.__module__: - exception_full_name = etype.__module__ + "." + etype.__qualname__ - else: - exception_full_name = etype.__qualname__ - - for i, line in enumerate(formatted_exception): - if line.startswith(exception_full_name): - formatted_exception[i] = line.replace( - exception_full_name, exception_name, 1 - ) - return formatted_exception - - -# Initialize Sage-specific doctest stuff -init_sage() - -# Monkey patch doctest to use our custom printer etc -old_run = doctest.DocTestRunner.run - - -def doctest_run( - self: doctest.DocTestRunner, - test: doctest.DocTest, - compileflags: Optional[int] = None, - out: Any = None, - clear_globs: bool = True, -) -> doctest.TestResults: - from sage.repl.rich_output import get_display_manager - from sage.repl.user_globals import set_globals - - traceback.format_exception_only = format_exception_only - - # Display warnings in doctests - warnings.showwarning = showwarning_with_traceback - setattr(sys, "__displayhook__", get_display_manager().displayhook) - - # Ensure that injecting globals works as expected in doctests - set_globals(test.globs) - return old_run(self, test, compileflags, out, clear_globs) - - -doctest.DocTestRunner.run = doctest_run - - -@pytest.fixture(autouse=True, scope="session") -def add_imports(doctest_namespace: dict[str, Any]): - """ - Add global imports for doctests. - - See `pytest documentation `. - """ - # Inject sage.all into each doctest - import sage.repl.ipython_kernel.all_jupyter - - dict_all = sage.repl.ipython_kernel.all_jupyter.__dict__ - - # Remove '__package__' item from the globals since it is not - # always in the globals in an actual Sage session. - dict_all.pop("__package__", None) - - sage_namespace = dict(dict_all) - sage_namespace["__name__"] = "__main__" - - doctest_namespace.update(**sage_namespace) 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 4a0e408b4dd..d5551c2c13d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -2099,17 +2099,17 @@ def frobenius_relative(self, n=1): sage: phi = DrinfeldModule(A, [1, z, z]) sage: phi.frobenius_relative() Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^2 + z*t + 1 - To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*t^2 + (2*z^2 + 4*z + 4)*t + 1 - Defn: t + From: Drinfeld module defined by T |--> z*τ^2 + z*τ + 1 + To: Drinfeld module defined by T |--> (2*z^2 + 4*z + 4)*τ^2 + (2*z^2 + 4*z + 4)*τ + 1 + Defn: τ sage: phi.frobenius_relative(2) Drinfeld Module morphism: - From: Drinfeld module defined by T |--> z*t^2 + z*t + 1 - To: Drinfeld module defined by T |--> (3*z^2 + 1)*t^2 + (3*z^2 + 1)*t + 1 - Defn: t^2 + From: Drinfeld module defined by T |--> z*τ^2 + z*τ + 1 + To: Drinfeld module defined by T |--> (3*z^2 + 1)*τ^2 + (3*z^2 + 1)*τ + 1 + Defn: τ^2 - When `n` is the degree of `F` over `\FF_q(\gamma(T))`, we obtain - the Frobenius endomorphism:: + If `F` is finite and `n` is the degree of `F` over `\FF_q(\gamma(T))`, + we obtain the Frobenius endomorphism:: sage: phi.frobenius_relative(3) == phi.frobenius_endomorphism() True @@ -2119,11 +2119,11 @@ def frobenius_relative(self, n=1): sage: psi = DrinfeldModule(A, [z, z, 1]) sage: psi.frobenius_relative() - Endomorphism of Drinfeld module defined by T |--> t^2 + z*t + z - Defn: t^3 + Endomorphism of Drinfeld module defined by T |--> τ^2 + z*τ + z + Defn: τ^3 sage: psi.frobenius_endomorphism() - Endomorphism of Drinfeld module defined by T |--> t^2 + z*t + z - Defn: t^3 + Endomorphism of Drinfeld module defined by T |--> τ^2 + z*τ + z + Defn: τ^3 When the characteristic is zero, the relative Frobenius is not defined and an error is raised:: From 15b399a7549334e781b37cc8c96a52eb2ff96e07 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 13 Aug 2025 17:40:23 +0200 Subject: [PATCH 08/13] remove coding utf8 --- src/sage/rings/function_field/drinfeld_modules/action.py | 1 - .../function_field/drinfeld_modules/charzero_drinfeld_module.py | 1 - .../rings/function_field/drinfeld_modules/drinfeld_module.py | 1 - .../function_field/drinfeld_modules/finite_drinfeld_module.py | 1 - src/sage/rings/function_field/drinfeld_modules/homset.py | 1 - src/sage/rings/function_field/drinfeld_modules/morphism.py | 1 - 6 files changed, 6 deletions(-) diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 7cb428452b1..06f8897326d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" The module action induced by a Drinfeld module diff --git a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py index 4c0c4b21437..99dd8dc9ce5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/charzero_drinfeld_module.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # sage.doctest: optional - sage.rings.finite_rings r""" Drinfeld modules over rings of characteristic zero 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 7e2de8a4161..eaf958ff0ee 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" Drinfeld modules 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 0cb6d60d265..15d616b5d2f 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,4 +1,3 @@ -# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" Finite Drinfeld modules diff --git a/src/sage/rings/function_field/drinfeld_modules/homset.py b/src/sage/rings/function_field/drinfeld_modules/homset.py index e1918686519..7dac2d93194 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" Set of morphisms between two Drinfeld modules diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index 490adc634ea..2c7bf3b19c8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/morphism.py +++ b/src/sage/rings/function_field/drinfeld_modules/morphism.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # sage.doctest: needs sage.rings.finite_rings r""" Drinfeld module morphisms From c7a5f2a9fffedff529b60192d06f40c715a50211 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 13 Aug 2025 17:44:11 +0200 Subject: [PATCH 09/13] conftest.py --- conftest.py | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 343 insertions(+) create mode 100644 conftest.py diff --git a/conftest.py b/conftest.py new file mode 100644 index 00000000000..98fc948afa7 --- /dev/null +++ b/conftest.py @@ -0,0 +1,343 @@ +# pyright: strict +"""Configuration and fixtures for pytest. + +This file configures pytest and provides some global fixtures. +See https://docs.pytest.org/en/latest/index.html for more details. +""" + +from __future__ import annotations + +import doctest +import inspect +import sys +import warnings +from pathlib import Path +from typing import Any, Iterable, Optional + +import pytest +from _pytest.doctest import ( + DoctestItem, + DoctestModule, + _get_continue_on_failure, + _get_runner, + _is_mocked, + _patch_unwrap_mock_aware, + get_optionflags, +) +from _pytest.pathlib import ImportMode, import_path + +from sage.doctest.forker import ( + init_sage, + showwarning_with_traceback, +) +from sage.doctest.parsing import SageDocTestParser, SageOutputChecker + + +class SageDoctestModule(DoctestModule): + """ + This is essentially a copy of `DoctestModule` from + https://github.com/pytest-dev/pytest/blob/main/src/_pytest/doctest.py. + The only change is that we use `SageDocTestParser` to extract the doctests + and `SageOutputChecker` to verify the output. + """ + + def collect(self) -> Iterable[DoctestItem]: + import doctest + + class MockAwareDocTestFinder(doctest.DocTestFinder): + """A hackish doctest finder that overrides stdlib internals to fix a stdlib bug. + https://github.com/pytest-dev/pytest/issues/3456 + https://bugs.python.org/issue25532 + """ + + def __init__(self) -> None: + super().__init__(parser=SageDocTestParser(set(["sage"]))) + + def _find_lineno(self, obj, source_lines): + """Doctest code does not take into account `@property`, this + is a hackish way to fix it. https://bugs.python.org/issue17446 + Wrapped Doctests will need to be unwrapped so the correct + line number is returned. This will be reported upstream. #8796 + """ + if isinstance(obj, property): + obj = getattr(obj, "fget", obj) + + if hasattr(obj, "__wrapped__"): + # Get the main obj in case of it being wrapped + obj = inspect.unwrap(obj) + + # Type ignored because this is a private function. + return super()._find_lineno( # type:ignore[misc] + obj, + source_lines, + ) + + def _find( + self, tests, obj, name, module, source_lines, globs, seen + ) -> None: + if _is_mocked(obj): + return + with _patch_unwrap_mock_aware(): + # Type ignored because this is a private function. + super()._find( # type:ignore[misc] + tests, obj, name, module, source_lines, globs, seen + ) + + if self.path.name == "conftest.py": + module = self.config.pluginmanager._importconftest( + self.path, + self.config.getoption("importmode"), + rootpath=self.config.rootpath, + consider_namespace_packages=True, + ) + else: + try: + module = import_path( + self.path, + mode=ImportMode.importlib, + root=self.config.rootpath, + consider_namespace_packages=True, + ) + except ImportError as exception: + if self.config.getvalue("doctest_ignore_import_errors"): + pytest.skip("unable to import module %r" % self.path) + else: + if isinstance(exception, ModuleNotFoundError): + # Ignore some missing features/modules for now + # TODO: Remove this once all optional things are using Features + if exception.name in ( + "valgrind", + "rpy2", + "sage.libs.coxeter3.coxeter", + ): + pytest.skip( + f"unable to import module { self.path } due to missing feature { exception.name }" + ) + raise + # Uses internal doctest module parsing mechanism. + finder = MockAwareDocTestFinder() + optionflags = get_optionflags(self.config) + from sage.features import FeatureNotPresentError + + runner = _get_runner( + verbose=False, + optionflags=optionflags, + checker=SageOutputChecker(), + continue_on_failure=_get_continue_on_failure(self.config), + ) + try: + for test in finder.find(module, module.__name__): + if test.examples: # skip empty doctests + yield DoctestItem.from_parent( + self, name=test.name, runner=runner, dtest=test + ) + except FeatureNotPresentError as exception: + pytest.skip( + f"unable to import module { self.path } due to missing feature { exception.feature.name }" + ) + except ModuleNotFoundError as exception: + # TODO: Remove this once all optional things are using Features + pytest.skip( + f"unable to import module { self.path } due to missing module { exception.name }" + ) + + +class IgnoreCollector(pytest.Collector): + """ + Ignore a file. + """ + + def __init__(self, parent: pytest.Collector) -> None: + super().__init__("ignore", parent) + + def collect(self) -> Iterable[pytest.Item | pytest.Collector]: + return [] + + +def pytest_collect_file( + file_path: Path, parent: pytest.Collector +) -> pytest.Collector | None: + """ + This hook is called when collecting test files, and can be used to + modify the file or test selection logic by returning a list of + ``pytest.Item`` objects which the ``pytest`` command will directly + add to the list of test items. + + See `pytest documentation `_. + """ + if ( + file_path.parent.name == "combinat" + or file_path.parent.parent.name == "combinat" + ): + # Crashes CI for some reason + return IgnoreCollector.from_parent(parent) + if file_path.suffix == ".pyx": + # We don't allow pytests to be defined in Cython files. + # Normally, Cython files are filtered out already by pytest and we only + # hit this here if someone explicitly runs `pytest some_file.pyx`. + return IgnoreCollector.from_parent(parent) + elif file_path.suffix == ".py": + if parent.config.option.doctest: + if file_path.name == "__main__.py" or file_path.name == "setup.py": + # We don't allow tests to be defined in __main__.py/setup.py files (because their import will fail). + return IgnoreCollector.from_parent(parent) + if ( + ( + file_path.name == "postprocess.py" + and file_path.parent.name == "nbconvert" + ) + or ( + file_path.name == "giacpy-mkkeywords.py" + and file_path.parent.name == "autogen" + ) + or ( + file_path.name == "flint_autogen.py" + and file_path.parent.name == "autogen" + ) + ): + # This is an executable file. + return IgnoreCollector.from_parent(parent) + + if ( + ( + file_path.name == "finite_dimensional_lie_algebras_with_basis.py" + and file_path.parent.name == "categories" + ) + or ( + file_path.name == "__init__.py" + and file_path.parent.name == "crypto" + ) + or (file_path.name == "__init__.py" and file_path.parent.name == "mq") + ): + # TODO: Fix these (import fails with "RuntimeError: dictionary changed size during iteration") + return IgnoreCollector.from_parent(parent) + + if ( + file_path.name in ("forker.py", "reporting.py") + ) and file_path.parent.name == "doctest": + # Fails with many errors due to different testing framework + return IgnoreCollector.from_parent(parent) + + if ( + ( + file_path.name == "arithgroup_generic.py" + and file_path.parent.name == "arithgroup" + ) + or ( + file_path.name == "pari.py" + and file_path.parent.name == "lfunctions" + ) + or ( + file_path.name == "permgroup_named.py" + and file_path.parent.name == "perm_gps" + ) + or ( + file_path.name == "finitely_generated.py" + and file_path.parent.name == "matrix_gps" + ) + or ( + file_path.name == "libgap_mixin.py" + and file_path.parent.name == "groups" + ) + or ( + file_path.name == "finitely_presented.py" + and file_path.parent.name == "groups" + ) + or ( + file_path.name == "classical_geometries.py" + and file_path.parent.name == "generators" + ) + ): + # Fails with "Fatal Python error" + return IgnoreCollector.from_parent(parent) + + return SageDoctestModule.from_parent(parent, path=file_path) + + +def pytest_addoption(parser): + # Add a command line option to run doctests + # (we don't use the built-in --doctest-modules option because then doctests are collected twice) + group = parser.getgroup("collect") + group.addoption( + "--doctest", + action="store_true", + default=False, + help="Run doctests in all .py modules", + dest="doctest", + ) + + +# Monkey patch exception printing to replace the full qualified name of the exception by its short name +# TODO: Remove this hack once migration to pytest is complete +import traceback + +old_format_exception_only = traceback.format_exception_only + + +def format_exception_only(etype: type, value: BaseException) -> list[str]: + formatted_exception = old_format_exception_only(etype, value) + exception_name = etype.__name__ + if etype.__module__: + exception_full_name = etype.__module__ + "." + etype.__qualname__ + else: + exception_full_name = etype.__qualname__ + + for i, line in enumerate(formatted_exception): + if line.startswith(exception_full_name): + formatted_exception[i] = line.replace( + exception_full_name, exception_name, 1 + ) + return formatted_exception + + +# Initialize Sage-specific doctest stuff +init_sage() + +# Monkey patch doctest to use our custom printer etc +old_run = doctest.DocTestRunner.run + + +def doctest_run( + self: doctest.DocTestRunner, + test: doctest.DocTest, + compileflags: Optional[int] = None, + out: Any = None, + clear_globs: bool = True, +) -> doctest.TestResults: + from sage.repl.rich_output import get_display_manager + from sage.repl.user_globals import set_globals + + traceback.format_exception_only = format_exception_only + + # Display warnings in doctests + warnings.showwarning = showwarning_with_traceback + setattr(sys, "__displayhook__", get_display_manager().displayhook) + + # Ensure that injecting globals works as expected in doctests + set_globals(test.globs) + return old_run(self, test, compileflags, out, clear_globs) + + +doctest.DocTestRunner.run = doctest_run + + +@pytest.fixture(autouse=True, scope="session") +def add_imports(doctest_namespace: dict[str, Any]): + """ + Add global imports for doctests. + + See `pytest documentation `. + """ + # Inject sage.all into each doctest + import sage.repl.ipython_kernel.all_jupyter + + dict_all = sage.repl.ipython_kernel.all_jupyter.__dict__ + + # Remove '__package__' item from the globals since it is not + # always in the globals in an actual Sage session. + dict_all.pop("__package__", None) + + sage_namespace = dict(dict_all) + sage_namespace["__name__"] = "__main__" + + doctest_namespace.update(**sage_namespace) From 124db5bf4b685c6357fa4b574eb22fd126303585 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 15 Aug 2025 09:30:11 +0200 Subject: [PATCH 10/13] fix pdf documentation --- .../function_field/drinfeld_modules/drinfeld_module.py | 10 +++++----- .../drinfeld_modules/finite_drinfeld_module.py | 10 +++++----- 2 files changed, 10 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 eaf958ff0ee..c564cdd4935 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -127,8 +127,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` -- (default: ``'τ'``) the name of the Ore polynomial ring - generator + - ``name`` -- (default: `\tau`) the name of the Ore + polynomial ring generator .. RUBRIC:: Construction @@ -163,7 +163,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): False In those examples, we used a list of coefficients (``[z, 1, 1]``) to - represent the generator `\phi_T = z + τ + τ^2`. One can also use + represent the generator `\phi_T = z + \tau + \tau^2`. One can also use regular Ore polynomials:: sage: ore_polring = phi.ore_polring() @@ -533,7 +533,7 @@ def __classcall_private__(cls, function_ring, gen, name='τ'): - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` -- (default: ``'τ'``) the name of the variable of + - ``name`` -- (default: `\tau`) the name of the variable of the Ore polynomial OUTPUT: a DrinfeldModule or DrinfeldModule_finite @@ -645,7 +645,7 @@ def __init__(self, gen, category): - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` -- (default: ``'τ'``) the name of the variable of + - ``name`` -- (default: `\tau`) the name of the variable of the Ore polynomial ring 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 15d616b5d2f..c18aa88664f 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 @@ -138,7 +138,7 @@ def __init__(self, gen, category): - ``gen`` -- the generator of the Drinfeld module as a list of coefficients or an Ore polynomial - - ``name`` -- (default: ``'τ'``) the name of the variable of + - ``name`` -- (default: `\tau`) the name of the variable of the Ore polynomial ring TESTS:: @@ -235,7 +235,7 @@ def frobenius_endomorphism(self): 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 `τ^q`. + defining Ore polynomial is `\tau^q`. EXAMPLES:: @@ -273,14 +273,14 @@ def frobenius_charpoly(self, var='X', algorithm=None): Let `\chi = X^r + \sum_{i=0}^{r-1} A_{i}(T)X^{i}` be the characteristic polynomial of the Frobenius endomorphism, and - let `τ^n` be the Ore polynomial that defines the Frobenius + let `\tau^n` be the Ore polynomial that defines the Frobenius endomorphism of `\phi`; by definition, `n` is the degree of `K` over the base field `\mathbb{F}_q`. Then we have .. MATH:: - \chi(τ^n)(\phi(T)) - = τ^{nr} + \sum_{i=1}^{r} \phi_{A_{i}}τ^{n(i)} + \chi(\tau^n)(\phi(T)) + = \tau^{nr} + \sum_{i=1}^{r} \phi_{A_{i}}\tau^{n(i)} = 0, with `\deg(A_i) \leq \frac{n(r-i)}{r}`. From 5dc6b4588bbcbfc4642565ae953ab6867ed82152 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 15 Aug 2025 10:59:05 +0200 Subject: [PATCH 11/13] =?UTF-8?q?default:=20``'=CF=84'``?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../function_field/drinfeld_modules/drinfeld_module.py | 6 +++--- .../drinfeld_modules/finite_drinfeld_module.py | 2 +- 2 files 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 c564cdd4935..1ab7e626232 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -127,7 +127,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` -- (default: `\tau`) the name of the Ore + - ``name`` -- (default: ``'τ'``) the name of the Ore polynomial ring generator .. RUBRIC:: Construction @@ -533,7 +533,7 @@ def __classcall_private__(cls, function_ring, gen, name='τ'): - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` -- (default: `\tau`) the name of the variable of + - ``name`` -- (default: ``'τ'``) the name of the variable of the Ore polynomial OUTPUT: a DrinfeldModule or DrinfeldModule_finite @@ -645,7 +645,7 @@ def __init__(self, gen, category): - ``gen`` -- the generator of the Drinfeld module; as a list of coefficients or an Ore polynomial - - ``name`` -- (default: `\tau`) the name of the variable of + - ``name`` -- (default: ``'τ'``) the name of the variable of the Ore polynomial ring 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 c18aa88664f..6a9fc657ab3 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 @@ -138,7 +138,7 @@ def __init__(self, gen, category): - ``gen`` -- the generator of the Drinfeld module as a list of coefficients or an Ore polynomial - - ``name`` -- (default: `\tau`) the name of the variable of + - ``name`` -- (default: ``'τ'``) the name of the variable of the Ore polynomial ring TESTS:: From 9308cc6f77000414a405d72be7e890e0bbf85ac8 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 16 Aug 2025 07:29:54 +0200 Subject: [PATCH 12/13] fix doctest --- .../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 4f5f6ca20ba..8753c7738c8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -2082,9 +2082,9 @@ def frobenius_relative(self, n=1): Return the `n`-th iterate relative Frobenius of this Drinfeld module. By definition, the relative Frobenius is the isogeny represented by - the Ore polynomial `tau^d` where `d` is the degree of the characteristic + the Ore polynomial `\tau^d` where `d` is the degree of the characteristic of this Drinfeld module (which is also the degree of `\gamma(T)` over - `\mathbb F_q`, where `\gamma` is the base morphism `\mathbb F_q[T] \to F`). + `\mathbb F_q`, where `\gamma` is the base morphism `\mathbb F_q[T] \to K`). INPUT: @@ -2107,7 +2107,7 @@ def frobenius_relative(self, n=1): To: Drinfeld module defined by T |--> (3*z^2 + 1)*τ^2 + (3*z^2 + 1)*τ + 1 Defn: τ^2 - If `F` is finite and `n` is the degree of `F` over `\FF_q(\gamma(T))`, + If `K` is finite and `n` is the degree of `K` over `\mathbb F_q(\gamma(T))`, we obtain the Frobenius endomorphism:: sage: phi.frobenius_relative(3) == phi.frobenius_endomorphism() From b4b4d901905e289e167b23c739363d97ad5a9607 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 16 Aug 2025 15:32:44 +0200 Subject: [PATCH 13/13] remove encoding declaration --- 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 aa80f8cc3b8..670ac3796b4 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- # sage_setup: distribution = sagemath-categories # sage.doctest: needs sage.rings.finite_rings r"""