diff --git a/src/sage/modules/finite_drinfeld_module.py b/src/sage/modules/finite_drinfeld_module.py index 68ee9630579..3f8f9b20a8c 100644 --- a/src/sage/modules/finite_drinfeld_module.py +++ b/src/sage/modules/finite_drinfeld_module.py @@ -36,6 +36,7 @@ from sage.categories.action import Action from sage.categories.homset import Hom from sage.misc.latex import latex +from sage.rings.integer import Integer from sage.rings.morphism import RingHomomorphism_im_gens from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing @@ -57,68 +58,75 @@ class FiniteDrinfeldModule(RingHomomorphism_im_gens): First, create base objects:: - sage: Fq = GF(7^2) + sage: Fq. = GF(3^2) sage: FqX. = Fq[] - sage: L = Fq.extension(3) - sage: frobenius = L.frobenius_endomorphism(2) - sage: Ltau. = OrePolynomialRing(L, frobenius) + sage: p = X^3 + (z2 + 2)*X^2 + (6*z2 + 1)*X + 3*z2 + 5 + sage: L = Fq.extension(6) + sage: Ltau. = OrePolynomialRing(L, L.frobenius_endomorphism(2)) + sage: omega = p.roots(L, multiplicities=False)[0] + sage: phi_X = omega + t + t^2 Then we instanciate the Drinfeld module:: - sage: phi_X = 1 + t^2 - sage: phi = FiniteDrinfeldModule(FqX, phi_X) + sage: phi = FiniteDrinfeldModule(FqX, phi_X, p) sage: phi - Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z6 of size 7^6 generated by t^2 + 1. + Finite Drinfeld module: + Polring: Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 + Ore polring: Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) + Generator: t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 + Characteristic: X^3 + (z2 + 2)*X^2 + X + 2 There are getters for the base objects:: sage: phi.polring() - Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 + Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 sage: phi.ore_polring() - Ore Polynomial Ring in t over Finite Field in z6 of size 7^6 twisted by z6 |--> z6^(7^2) + Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) sage: phi.gen() - t^2 + 1 + t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12 And the class inherits `RingHomomorphism_im_gens`, so that one can use:: sage: phi.domain() - Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 + Univariate Polynomial Ring in X over Finite Field in z2 of size 3^2 sage: phi.codomain() - Ore Polynomial Ring in t over Finite Field in z6 of size 7^6 twisted by z6 |--> z6^(7^2) + Ore Polynomial Ring in t over Finite Field in z12 of size 3^12 twisted by z12 |--> z12^(3^2) sage: phi.im_gens() - [t^2 + 1] + [t^2 + t + z12^11 + z12^10 + z12^9 + 2*z12^5 + 2*z12^4 + z12^3 + 2*z12] - The rank of the Drinfeld module is retrieved with:: + We can retrieve basic quantities: sage: phi.rank() 2 + sage: phi.j() + 1 .. RUBRIC:: The module law induced by a Drinfeld module The most important feature of Drinfeld modules is that they induce - an `\Fq[X]`-module law on the algebraic closure `\Fqbar` of `\Fq`. - We implement this action for any finite field extension `M` of `L`. - The `_get_action_` method returns an `Action` object:: + an `\Fq[X]`-module law on any subextension of the algebraic closure + `\Fqbar` of `\Fq`. For this, we created the class + `FiniteDrinfeldModuleAction`, which inherits `Action`. For the sake + of simplicity, `phi` will only act on the base field (`L`) of its + Ore polynomial ring. If you want to act on a bigger field, you can + use the method `change_ring`. sage: M = L.extension(2) - sage: action = phi._get_action_(M) - sage: action + sage: phi_M = phi.change_ring(M) # todo: not tested + sage: action = phi._get_action_() # todo: not tested + sage: action # todo: not tested Action on Finite Field in z12 of size 7^12 induced by the Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z6 of size 7^6 generated by t^2 + 1. - sage: x = M.gen() - sage: g = X^3 + X + 5 - sage: action(g, x) + + The calculation of the action is simple: + + sage: x = M.gen() # todo: not tested + sage: g = X^3 + X + 5 # todo: not tested + sage: action(g, x) # todo: not tested ... 6*z12^11 + 5*z12^9 + 5*z12^8 + 2*z12^7 + 6*z12^6 + z12^5 + 6*z12^4 + 2*z12^3 + 3*z12^2 + 5*z12 + 4 - Furthermore, it can be useful to embed a Drinfeld module into a - larger Ore polynomial ring:: - - sage: M = L.extension(2) - sage: psi = phi.change_ring(M); psi - Finite Drinfeld module from Univariate Polynomial Ring in X over Finite Field in z2 of size 7^2 over Finite Field in z12 of size 7^12 generated by t^2 + 1. - .. NOTE:: The general definition of a Drinfeld module is out of the scope @@ -136,35 +144,37 @@ class FiniteDrinfeldModule(RingHomomorphism_im_gens): :mod:`sage.rings.polynomial.ore_polynomial_ring` """ - def __init__(self, polring, gen): - # VERIFICATIONS - # Check `polring` is an Fq[X]: - # See docstrings of `PolynomialRing_dense_finite_field` and - # `is_PolynomialRing`. - isinstance(polring, PolynomialRing_dense_finite_field) - # Check `gen` is an Ore polynomial: + def __init__(self, polring, gen, characteristic): + # Verifications + if not isinstance(polring, PolynomialRing_dense_finite_field): + raise TypeError('First argument must be a polynomial ring') + if not characteristic in polring: + raise TypeError('The characteristic must be in the polynomial ' \ + 'ring') + if not characteristic.is_prime(): + raise ValueError('The characteristic must be irreducible') if not isinstance(gen, OrePolynomial): raise TypeError('The generator must be an Ore polynomial') - # Now we can define those for convenience: + # We define those for convenience before continuing FqX = polring Ltau = gen.parent() Fq = FqX.base_ring() L = Ltau.base_ring() - # Check the Ore polynomial ring is an L{tau} with L a finite - # field extension of Fq: _check_base_fields(Fq, L) if not Ltau.twisting_derivation() is None: raise ValueError('The Ore polynomial ring should have no ' \ 'derivation') - # Check the frobenius is x -> x^q: if Ltau.twisting_morphism().power() != Fq.degree(): raise ValueError('The twisting morphism of the Ore polynomial ' \ 'ring must be the Frobenius endomorphism of the base ' \ 'field of the polynomial ring') - # The generator is not constant: + if characteristic(gen[0]) != 0: + raise ValueError('The constant coefficient of the generator ' \ + 'must be a root of the characteristic') if gen.is_constant(): raise ValueError('The generator must not be constant') - # ACTUAL WORK + # Finally + self.__characteristic = characteristic super().__init__(Hom(FqX, Ltau), gen) ########### @@ -183,7 +193,30 @@ def change_ring(self, R): new_frobenius = R.frobenius_endomorphism(self.frobenius().power()) new_ore_polring = OrePolynomialRing(R, new_frobenius, names=self.ore_polring().variable_names()) - return FiniteDrinfeldModule(self.polring(), new_ore_polring(self.gen())) + return FiniteDrinfeldModule(self.polring(), + new_ore_polring(self.gen()), self.characteristic()) + + def delta(self): + if self.rank() != 2: + raise ValueError('The rank must equal 2') + return self.gen()[2] + + def g(self): + if self.rank() != 2: + raise ValueError('The rank must equal 2') + return self.gen()[1] + + def height(self): + return Integer(1) + + def j(self): + if self.rank() != 2: + raise ValueError('The j-invariant is only defined for rank 2 ' \ + 'Drinfeld modules') + g = self.gen()[1] + delta = self.gen()[2] + q = self.polring().base_ring().order() + return (g**(q+1)) / delta def rank(self): return self.gen().degree() @@ -196,8 +229,7 @@ def _get_action_(self): return FiniteDrinfeldModuleAction(self) def _latex_(self): - return f'\\text{{Finite{{ }}{latex(self.polring())}-Drinfeld{{ }}' \ - f'module{{ }}defined{{ }}by{{ }}}}\n' \ + return f'\\text{{Finite{{ }}Drinfeld{{ }}module{{ }}defined{{ }}by{{ }}}}\n' \ f'\\begin{{align}}\n' \ f' {latex(self.polring())}\n' \ f' &\\to {latex(self.ore_polring())} \\\\\n' \ @@ -208,13 +240,22 @@ def _latex_(self): f'{latex(self.characteristic())}' def _repr_(self): - return f'Finite Drinfeld module from {self.polring()} over ' \ - f'{self.ore_polring().base_ring()} generated by {self.gen()}.' + return f'Finite Drinfeld module:\n' \ + f' Polring: {self.polring()}\n' \ + f' Ore polring: {self.ore_polring()}\n' \ + f' Generator: {self.gen()}\n' \ + f' Characteristic: {self.characteristic()}' ########### # Getters # ########### + def characteristic(self): + return self.__characteristic + + def constant_term(self): + return self.gen()[0] + def frobenius(self): return self.ore_polring().twisting_morphism()