diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 67ea592dc4b..c3a956d1aed 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -5240,6 +5240,9 @@ REFERENCES: .. [Mur1983] \G. E. Murphy. *The idempotents of the symmetric group and Nakayama's conjecture*. J. Algebra **81** (1983). 258-265. +.. [Mus2023] Yossef Musleh. *Algorithms for Drinfeld Modules*. + PhD thesis, University of Waterloo, 2023. + .. [Muth2019] Robert Muth. *Super RSK correspondence with symmetry*. Electron. J. Combin. **26** (2019), no. 2, Paper 2.27, 29 pp. https://www.combinatorics.org/ojs/index.php/eljc/article/view/v26i2p27, @@ -6914,6 +6917,9 @@ REFERENCES: *A new keystream generator MUGI*; in FSE, (2002), pp. 179-194. +.. [Wes2022] \B. Wesolowski. 2022. *Computing isogenies between finite Drinfeld modules*. + Cryptology ePrint Archive, Paper 2022/438. https://eprint.iacr.org/2022/438 + .. [Whi1932] \H. Whitney, *Congruent graphs and the connectivity of graphs*, American Journal of Mathematics, pages 150--168, 1932, diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index e5000de710c..35cd7f2555f 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -123,7 +123,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 +135,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 +207,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 +216,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 +227,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 +507,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 +534,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 @@ -769,7 +769,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 @@ -795,7 +795,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:: @@ -824,8 +824,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 76b0bd31a9d..0a1a4389cec 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 @@ -59,7 +59,7 @@ class DrinfeldModule_charzero(DrinfeldModule): sage: A. = GF(3)[] sage: phi = DrinfeldModule(A, [T, 1]) sage: phi - Drinfeld module defined by T |--> t + T + Drinfeld module defined by T |--> τ + T :: @@ -107,7 +107,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:: @@ -116,7 +116,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): @@ -448,7 +448,7 @@ class DrinfeldModule_rational(DrinfeldModule_charzero): sage: Fq = GF(q) sage: A. = Fq[] sage: C = DrinfeldModule(A, [T, 1]); C - Drinfeld module defined by T |--> t + T + Drinfeld module defined by T |--> τ + T sage: type(C) """ @@ -556,7 +556,7 @@ def class_polynomial(self): sage: Fq = GF(q) sage: A. = Fq[] 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 51dc58e83ef..0f158099d15 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 When the given coefficients naturally live in a ring, the Drinfeld module is constructed over the fraction field:: @@ -99,7 +99,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: A. = Fq[] sage: psi = DrinfeldModule(A, [T, T+1]) sage: psi - Drinfeld module defined by T |--> (T + 1)*t + T + Drinfeld module defined by T |--> (T + 1)*τ + T sage: psi.base() Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 7^2 over its base @@ -129,8 +129,8 @@ 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 - generator + - ``name`` -- (default: ``'τ'``) the name of the Ore + polynomial ring generator .. RUBRIC:: Construction @@ -142,7 +142,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:: @@ -154,7 +154,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: psi = DrinfeldModule(A, [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 :: @@ -164,15 +164,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 + \tau + \tau^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 @@ -180,12 +180,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 @@ -205,7 +205,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 @@ -244,7 +244,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) :: @@ -254,7 +254,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): :: sage: phi.gen() # phi_T - t^2 + t + z + τ^2 + τ + z sage: phi.gen() == phi(T) True @@ -268,9 +268,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:: @@ -310,9 +310,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 @@ -325,23 +325,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 @@ -367,11 +367,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 @@ -383,7 +383,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 @@ -404,7 +404,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:: @@ -511,7 +511,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. @@ -524,8 +524,8 @@ 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 - ring gen + - ``name`` -- (default: ``'τ'``) the name of the variable of + the Ore polynomial OUTPUT: a DrinfeldModule or DrinfeldModule_finite @@ -636,8 +636,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:: @@ -765,7 +765,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} :: @@ -792,7 +792,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}' @@ -859,7 +859,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:: @@ -1287,7 +1287,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:: @@ -1299,7 +1299,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 @@ -1764,9 +1764,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) @@ -1790,7 +1790,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 @@ -1880,8 +1880,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 @@ -1944,7 +1944,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 @@ -1952,37 +1952,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 @@ -1996,7 +1996,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 @@ -2043,16 +2043,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 1369d1a213b..f41ca93dcbb 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:: @@ -138,8 +138,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:: @@ -236,7 +236,6 @@ def frobenius_endomorphism(self): `tau^n`, where `n` is the degree of the base field `K` over `\mathbb F_q`. - EXAMPLES:: sage: Fq = GF(343) @@ -244,8 +243,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 +272,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 `\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(t^n)(\phi(T)) - = t^{nr} + \sum_{i=1}^{r} \phi_{A_{i}}t^{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}`. @@ -340,7 +339,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 +934,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 +1090,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 1ccb77cd3e3..4ad5bf0673b 100644 --- a/src/sage/rings/function_field/drinfeld_modules/homset.py +++ b/src/sage/rings/function_field/drinfeld_modules/homset.py @@ -8,6 +8,7 @@ AUTHORS: - Antoine Leudière (2022-04) +- Xavier Caruso, Yossef Musleh (2025-08): added computation of bases of homsets """ # ***************************************************************************** @@ -22,11 +23,18 @@ import operator +from sage.categories.finite_fields import FiniteFields from sage.categories.drinfeld_modules import DrinfeldModules from sage.categories.homset import Homset from sage.categories.action import Action +from sage.misc.cachefunc import cached_method from sage.misc.latex import latex +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.matrix.constructor import Matrix +from sage.matrix.special import identity_matrix from sage.rings.function_field.drinfeld_modules.morphism import DrinfeldModuleMorphism +from sage.structure.parent import Parent +from sage.functions.log import logb class DrinfeldModuleMorphismAction(Action): @@ -41,16 +49,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 +68,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 +122,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 +155,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 +168,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 +193,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:: @@ -279,6 +287,7 @@ def __init__(self, X, Y, category=None, check=True): self.register_action(DrinfeldModuleMorphismAction(A, self, False, operator.mul)) if X is Y: self.register_coercion(A) + self._basis = None def _latex_(self): r""" @@ -293,7 +302,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 +322,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 +346,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,35 +394,633 @@ 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 # seems to work, but I don't know what I'm doing. return DrinfeldModuleMorphism(self, *args, **kwds) + + def an_element(self, degree=None): + r""" + Return an element in this homset. + + INPUT: + + - ``degree`` (default: ``None``) -- an integer; if given, + return an isogeny of this degree + + EXAMPLES:: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: phi = DrinfeldModule(A, [z, z^2 + z + 1, z^2 + z]) + sage: psi = DrinfeldModule(A, [z, z + 1, z^2 + z + 1]) + sage: H = Hom(phi, psi) + sage: H.an_element() + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: z^2*τ^3 + + We can also ask for an isogeny with a required degree:: + + sage: H.an_element(degree=2) + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: (z^2 + 1)*τ^2 + τ + z + 1 + + If there is no isogeny with the required degree, an error is raised:: + + sage: H.an_element(degree=1) + Traceback (most recent call last): + ... + ValueError: no isogeny of given degree + + Below, `\phi` and `\psi` are not isogenous, so :meth:`an_element` + returns the zero morphism (which is the unique element in the + homset):: + + sage: psi = DrinfeldModule(A, [z, z^2, z^3]) + sage: H = Hom(phi, psi) + sage: H.an_element() + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z + 1)*τ^2 + z^2*τ + z + Defn: 0 + + When the base is not a finite field, computing isogenies is not + implemented so far, so this method always returns the zero morphism:: + + sage: phi = DrinfeldModule(A, [T, 1]) + sage: End(phi).an_element() + Endomorphism of Drinfeld module defined by T |--> τ + T + Defn: 0 + sage: End(phi).an_element(degree=1) + Traceback (most recent call last): + ... + NotImplementedError: computing isogenies are currently only implemented over finite fields + """ + if self.base() not in FiniteFields(): + if degree is None: + return self.zero() + else: + raise NotImplementedError("computing isogenies are currently only implemented over finite fields") + if degree is None: + basis = self._A_basis() + if len(basis) == 0: + return self.zero() + return basis[0] + else: + basis = self._Fq_basis(degree=degree) + for isogeny in basis: + if isogeny.degree() == degree: + return isogeny + raise ValueError("no isogeny of given degree") + + def zero(self): + r""" + Return the zero morphism is this homset. + + EXAMPLES:: + + sage: Fq = GF(5) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: phi = DrinfeldModule(A, [z, z^2 + z + 1, z^2 + z]) + sage: psi = DrinfeldModule(A, [z, z + 1, z^2 + z + 1]) + sage: H = Hom(phi, psi) + sage: H.zero() + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: 0 + """ + return self(self.domain().ore_polring().zero()) + + @cached_method + def _A_basis(self): + r""" + Return a basis of this homset over the underlying + function ring. + + Currently, it only works over finite fields. + + This method should not be called directly; call + :meth:`basis` instead. + + ALGORITHM: + + The isogenies `u : \phi \to \psi' correspond to + the vectors `u \in M(\phi)` such that + + .. MATH:: + + \sum_{k=0}^r g_k \tau^k(u) = T u + + where the `g_k` are the coefficients of `\psi_T` + + The algorithm consists in solving the above system + viewed as a linear system over `\mathbb F_q[T]`. + + We refer to [Mus2023]_, Section 7.3 for more details. + + TESTS:: + + sage: A. = GF(5)[] + sage: phi = DrinfeldModule(A, [T, 1]) + sage: End(phi).basis() + Traceback (most recent call last): + ... + NotImplementedError: computing basis of homsets are currently only implemented over finite fields + """ + phi = self.domain() + psi = self.codomain() + r = phi.rank() + if psi.rank() != r: + return [] + Fo = phi.base_over_constants_field() + Fq = Fo.base() + F = Fo.backend() + d = Fo.degree(Fq) + q = Fq.cardinality() + A = phi.function_ring() + T = A.gen() + + AF = PolynomialRing(F, name='T') + TF = AF.gen() + + Frob = lambda x: x**q + FrobT = lambda P: P.map_coefficients(Frob) + + phiT = phi.gen() + psiT = psi.gen() + + # We compute the tau^i in M(phi) + lc = ~(phiT[r]) + xT = [-AF(lc*phiT[i]) for i in range(r)] + xT[0] += lc*TF + taus = [] + for i in range(r): + taui = r * [AF.zero()] + taui[i] = AF.one() + taus.append(taui) + for i in range(r): + s = FrobT(taui[-1]) + taui = [s*xT[0]] + [FrobT(taui[j-1]) + s*xT[j] for j in range(1,r)] + taus.append(taui) + + # We precompute the Frob^k(z^i) + z = F(Fo.gen()) + zs = [] + zq = z + for k in range(r+1): + x = F.one() + for i in range(d): + zs.append(x) + x *= zq + zq = zq ** q + + # We compute the linear system to solve + rows = [] + for i in range(d): + for j in range(r): + # For x = z^i * tau^j, we compute + # sum(g_k*tau^k(x), k=0..r) - T*x + # = sum(g_k*Frob^k(z^i)*tau^(k+j), k=0..r) - T*x + row = r * [AF.zero()] + for k in range(r+1): + s = psiT[k] * zs[k*d + i] + for l in range(r): + row[l] += s*taus[k+j][l] + row[j] -= zs[i] * TF + # We write it in the A-basis + rowA = [] + for c in row: + c0 = Fo(c[0]).vector() + c1 = Fo(c[1]).vector() + rowA += [c0[k] + T*c1[k] for k in range(d)] + rows.append(rowA) + M = Matrix(rows) + + # We solve the linear system + ker = M.minimal_kernel_basis() + + # We reconstruct the isogenies + isogenies = [] + S = phi.ore_polring() + t = S.gen() + for row in range(ker.nrows()): + u = S.zero() + for i in range(d): + for j in range(r): + a = ker[row, i*r + j] + u += zs[i] * t**j * sum(a[k] * phiT**k for k in range(a.degree() + 1)) + isogenies.append(self(u)) + + return isogenies + + def _Fq_basis(self, degree): + r""" + Return a `\mathbb{F}_q`-basis of the space of morphisms + in this homset of degree at most ``degree``. + + Currently, it only works over finite fields. + + This method should not be called directly; call + :meth:`basis` instead. + + INPUT: + + - ``degree`` -- an integer + + ALGORITHM: + + We return the basis of the kernel of a matrix derived from the + constraint that `f \phi_T = \psi_T f`. See [Wes2022]_ for + details on this algorithm. + + TESTS:: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: psi = DrinfeldModule(A, [z, z + 1, z^2 + z + 1]) + sage: phi = DrinfeldModule(A, [z, z^2 + z + 1, z^2 + z]) + sage: H = Hom(phi, psi) + sage: B = H._Fq_basis(3) + sage: M = B[0] + sage: M_poly = M.ore_polynomial() + sage: M_poly*phi.gen() - psi.gen()*M_poly + 0 + + :: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(A, [z, 1]) + sage: psi = DrinfeldModule(A, [z, 1, 1]) + sage: hom = Hom(phi, psi) + sage: hom._Fq_basis(4) + [] + """ + domain, codomain = self.domain(), self.codomain() + Fq = domain._Fq + K = domain.base_over_constants_field() + q = Fq.cardinality() + r = domain.rank() + if codomain.rank() != r: + return [] + n = K.degree(Fq) + # shorten name for readability + d = degree + K_basis = K.basis_over(Fq) + phiT = domain.coefficients(sparse=False) + psiT = codomain.coefficients(sparse=False) + + # We precompute the matrices of the iterates of + # the Frobenius of K/Fq + frob_matrices = [identity_matrix(Fq, n)] + [Matrix(Fq, n) for _ in range(d + r)] + for i, elem in enumerate(K_basis): + for k in range(1, d+r+1): + elem = elem ** q + v = elem.vector() + for j in range(n): + frob_matrices[k][i,j] = v[j] + + # We write the linear system and solve it + sys = Matrix(Fq, (d + r + 1)*n, (d + 1)*n) + for k in range(0, d + r + 1): + for i in range(max(0, k - r), min(k, d) + 1): + # We represent multiplication and Frobenius + # as operators acting on K as a vector space + # over Fq + oper = K(phiT[k-i] ** (q**i)).matrix() \ + - frob_matrices[k-i] * K(psiT[k-i]).matrix() + for j in range(n): + for l in range(n): + sys[k*n + j, i*n + l] = oper[l, j] + sol = sys.right_kernel().basis() + + # Reconstruct the Ore polynomial from the coefficients + basis = [] + tau = domain.ore_polring().gen() + for basis_elem in sol: + ore_poly = sum([sum([K_basis[j].backend() * basis_elem[i*n + j] + for j in range(n)])*(tau**i) + for i in range(d + 1)]) + basis.append(self(ore_poly)) + + return basis + + def basis(self, degree=None): + r""" + Return a basis of this homset. + + INPUT: + + - ``degree`` -- an integer or ``None`` (default: ``None``) + + If ``degree`` is ``None``, a basis over the underlying + function ring is returned. + Otherwise, a `\mathbb F_q`-basis of the set of morphisms of + degree at most ``degree`` is returned. + + ALGORITHM: + + We reformulate the problem as a linear system over `\mathbb F_q` + or `A = \mathbb F_q[T]` and solve it. + We refer to [Wes2022]_ and [Mus2023]_, Section 7.3 for more details. + + EXAMPLES:: + + sage: Fq = GF(5) + sage: A. = Fq[] + sage: K. = Fq.extension(2) + sage: phi = DrinfeldModule(A, [z, 0, 1, z]) + sage: End(phi).basis() + [Identity morphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z, + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: τ^2, + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: 2*τ^4 + z*τ^3 + z] + + If we specify a degree, a basis over `\mathbb F_q` is computed:: + + sage: End(phi).basis(degree=5) + [Identity morphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z, + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: z*τ^3 + z, + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: τ^2, + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: z*τ^5 + z*τ^2, + Endomorphism of Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + Defn: τ^4] + + Here is another example where the domain and the codomain differ:: + + sage: psi = DrinfeldModule(A, [z, 3*z + 1, 2*z, 4*z + 1]) + sage: H = Hom(phi, psi) + sage: H.basis() + [Drinfeld Module morphism: + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (4*z + 1)*τ^3 + 2*z*τ^2 + (3*z + 1)*τ + z + Defn: τ + 1, + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (4*z + 1)*τ^3 + 2*z*τ^2 + (3*z + 1)*τ + z + Defn: 3*τ^3 + (z + 2)*τ^2 + 4*z*τ + z + 4, + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (4*z + 1)*τ^3 + 2*z*τ^2 + (3*z + 1)*τ + z + Defn: (z + 4)*τ^2 + 4*z*τ + z + 4] + + sage: H.basis(degree=2) + [Drinfeld Module morphism: + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (4*z + 1)*τ^3 + 2*z*τ^2 + (3*z + 1)*τ + z + Defn: τ + 1, + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> z*τ^3 + τ^2 + z + To: Drinfeld module defined by T |--> (4*z + 1)*τ^3 + 2*z*τ^2 + (3*z + 1)*τ + z + Defn: (z + 4)*τ^2 + (4*z + 1)*τ + z] + + We can check that `\phi` and `\psi` are not isomorphic by checking + that there is no isogeny of degree `0` between them:: + + sage: H.basis(degree=0) + [] + + When `\phi` and `\psi` are not isogenous, an empty list is returned:: + + sage: psi = DrinfeldModule(A, [z, 3*z, 2*z, 4*z]) + sage: Hom(phi, psi).basis() + [] + + Currently, this method only works over finite fields:: + + sage: A. = GF(5)[] + sage: phi = DrinfeldModule(A, [T, 1]) + sage: End(phi).basis() + Traceback (most recent call last): + ... + NotImplementedError: computing basis of homsets are currently only implemented over finite fields + """ + if self.base() not in FiniteFields(): + raise NotImplementedError("computing basis of homsets are currently only implemented over finite fields") + if degree is None: + return self._A_basis() + else: + return self._Fq_basis(degree) + + def basis_over_frobenius(self): + r""" + Return a basis of this homser over `\mathbb F_q[\tau^n]` where + `n = [K:\mathbb F_q]` (and thus `\tau^n` is to the Frobenius endomorphism). + + ALGORITHM: + + We return the basis of the kernel of a matrix derived from the + constraint that `\iota \phi_T = \psi_T \iota` for any morphism + `iota: \phi \to \psi`. + We refer to [Mus2023]_, Section 7.3 for more details. + + EXAMPLES:: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: phi = DrinfeldModule(A, [z, z^2 + z + 1, z^2 + z]) + sage: psi = DrinfeldModule(A, [z, z + 1, z^2 + z + 1]) + sage: H = Hom(phi, psi) + sage: H.basis_over_frobenius() + [Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: (z^2 + 1)*τ^2 + τ + z + 1, + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: (z^2 + 1)*τ^5 + (z + 1)*τ^4 + z*τ^3 + τ^2 + (z^2 + z)*τ + z, + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: z^2] + + :: + + sage: Fq = GF(5) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: phi = DrinfeldModule(A, [z, 3*z, 4*z]) + sage: chi = DrinfeldModule(A, [z, 2*z^2 + 3, 4*z^2 + 4*z]) + sage: H = Hom(phi, chi) + sage: H.basis_over_frobenius() + [] + + TESTS:: + + sage: Fq = GF(4) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: psi = DrinfeldModule(A, [z, z^5 + z^3 + 1, 1]) + sage: phi = DrinfeldModule(A, [z, z^4 + z^3 + 1, 1]) + sage: H = Hom(phi, psi) + sage: basis = H.basis_over_frobenius() + sage: basis + [... Defn: τ^2 + (z^5 + z^4 + z^3 + z^2)*τ + z^5 + z^4 + z^3 + 1, + ... Defn: (z^3 + z^2 + z + 1)*τ^2 + (z^5 + z^4 + z^3 + 1)*τ + z^4 + z^3 + z, + ... Defn: (z^3 + z^2 + z + 1)*τ^5 + (z^5 + z^4 + z^3)*τ^4 + z^2*τ^3 + (z^3 + 1)*τ^2 + (z^5 + z^4 + z^2 + 1)*τ + z^2] + sage: basis[2].ore_polynomial()*phi.gen() - psi.gen()*basis[2].ore_polynomial() + 0 + + :: + + sage: A. = GF(5)[] + sage: phi = DrinfeldModule(A, [T, 1]) + sage: End(phi).basis_over_frobenius() + Traceback (most recent call last): + ... + ValueError: basis over Frobenius only makes sense for Drinfeld module defined over finite fields + """ + if self.base() not in FiniteFields(): + raise ValueError("basis over Frobenius only makes sense for Drinfeld module defined over finite fields") + Fq = self.domain()._Fq + K = self.domain().base_over_constants_field() + r = self.domain().rank() + n = K.degree(Fq) + q = Fq.cardinality() + K_basis = K.basis_over(Fq) + phiT = self.domain().coefficients(sparse=False) + psiT = self.codomain().coefficients(sparse=False) + + # The commutative polynomial ring in tau^n. + poly_taun = PolynomialRing(Fq, 'taun') + taun = poly_taun.gen() + + sys = Matrix(poly_taun, n**2, n**2) + + # Build a linear system over the commutative polynomial ring + # Fq[tau^n]. The kernel of this system consists of all + # morphisms from domain to codomain. + for j in range(n): + for k in range(n): + for i in range(r+1): + # Coefficients of tau^{i + k} coming from the + # relation defining morphisms of Drinfeld modules + # These are elements of K, expanded in terms of + # K_basis. + poly = K(phiT[i]**(q**k) * K_basis[j] + - psiT[i] * K_basis[j]**(q**i)).polynomial() + deg = (i + k) // n + row = n * (i + k - n*deg) + col = k*n + j + for b in range(poly.degree() + 1): + sys[row + b, col] += poly[b] * taun**deg + + sol = sys.right_kernel().basis() + + # Reconstruct basis as skew polynomials. + basis = [] + tau = self.domain().ore_variable() + for basis_vector in sol: + basis_poly = 0 + for i in range(n): + for j in range(n): + basis_poly += basis_vector[n*i + j].subs(tau**n) * K_basis[j].backend() * tau**i + basis.append(self(basis_poly)) + + return basis + + def random_element(self, degree=None): + r""" + Return a random morphism in this homset. + + INPUT: + + - ``degree`` (default: ``None``) -- the maximum degree of + the morphism + + EXAMPLES:: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(3) + sage: psi = DrinfeldModule(A, [z, z + 1, z^2 + z + 1]) + sage: phi = DrinfeldModule(A, [z, z^2 + z + 1, z^2 + z]) + sage: H = Hom(phi, psi) + sage: H.random_element() # random + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: z*τ^7 + (z^2 + 1)*τ^6 + τ^5 + z^2*τ^4 + (z^2 + z + 1)*τ^3 + τ^2 + (z^2 + z)*τ + z + + When ``degree`` is given, a uniformly distributed random isogeny + of degree *at most* the given value is outputted:: + + sage: H.random_element(3) # random + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: (z^2 + 1)*τ^2 + τ + z + 1 + + For producing a random isogeny with accurate degree, we can + proceed as follows:: + + sage: H.an_element(3) + H.random_element(2) # random + Drinfeld Module morphism: + From: Drinfeld module defined by T |--> (z^2 + z)*τ^2 + (z^2 + z + 1)*τ + z + To: Drinfeld module defined by T |--> (z^2 + z + 1)*τ^2 + (z + 1)*τ + z + Defn: z^2*τ^3 + (z^2 + 1)*τ^2 + τ + z^2 + z + 1 + + Currently, this method only works over finite fields:: + + sage: phi = DrinfeldModule(A, [T, 1]) + sage: End(phi).random_element() + Traceback (most recent call last): + ... + NotImplementedError: computing isogenies are currently only implemented over finite fields + """ + if self.base() not in FiniteFields(): + raise NotImplementedError("computing isogenies are currently only implemented over finite fields") + domain = self.domain() + if degree is None: + scalars = domain._function_ring + basis = self._A_basis() + else: + scalars = domain._Fq + basis = self._Fq_basis(degree) + isog = self.zero() + for u in basis: + isog += scalars.random_element() * u + return isog diff --git a/src/sage/rings/function_field/drinfeld_modules/morphism.py b/src/sage/rings/function_field/drinfeld_modules/morphism.py index beff948f980..ee119d5e515 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 :: @@ -427,6 +427,26 @@ def ore_polynomial(self): """ return self._ore_polynomial + def degree(self): + r""" + Return the degree of this isogeny, that is, by definition, + the `\tau`-degree of its defining Ore polynomial. + + EXAMPLES:: + + sage: Fq = GF(2) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + sage: phi = DrinfeldModule(A, [z6, 1, 1]) + sage: f = phi.frobenius_endomorphism() + sage: f + Endomorphism of Drinfeld module defined by T |--> τ^2 + τ + z6 + Defn: τ^6 + sage: f.degree() + 6 + """ + return self._ore_polynomial.degree() + @coerce_binop def __add__(self, other): r""" @@ -443,21 +463,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 +493,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 +515,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 +526,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 +584,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 +641,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 +702,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 +780,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 +820,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