Drinfeld modules#
This module provides the class
sage.rings.function_field.drinfeld_module.drinfeld_module.DrinfeldModule
.
For finite Drinfeld modules and their theory of complex multiplication, see
class
sage.rings.function_field.drinfeld_module.finite_drinfeld_module.DrinfeldModule
.
AUTHORS:
Antoine Leudière (2022-04): initial version
Xavier Caruso (2022-06): initial version
David Ayotte (2023-03): added basic \(j\)-invariants
- class sage.rings.function_field.drinfeld_modules.drinfeld_module.DrinfeldModule(gen, category)[source]#
Bases:
Parent
,UniqueRepresentation
This class implements Drinfeld \(\mathbb{F}_q[T]\)-modules.
Let \(\mathbb{F}_q[T]\) be a polynomial ring with coefficients in a finite field \(\mathbb{F}_q\) and let \(K\) be a field. Fix a ring morphism \(\gamma: \mathbb{F}_q[T] \to K\); we say that \(K\) is an \(\mathbb{F}_q[T]\)-field. Let \(K\{\tau\}\) be the ring of Ore polynomials with coefficients in \(K\), whose multiplication is given by the rule \(\tau \lambda = \lambda^q \tau\) for any \(\lambda \in K\).
A Drinfeld \(\mathbb{F}_q[T]\)-module over the base \(\mathbb{F}_q[T]\)-field \(K\) is an \(\mathbb{F}_q\)-algebra morphism \(\phi: \mathbb{F}_q[T] \to K\{\tau\}\) such that \(\mathrm{Im}(\phi) \not\subset K\) and \(\phi\) agrees with \(\gamma\) on \(\mathbb{F}_q\).
For \(a\) in \(\mathbb{F}_q[T]\), \(\phi(a)\) is denoted \(\phi_a\).
The Drinfeld \(\mathbb{F}_q[T]\)-module \(\phi\) is uniquely determined by the image \(\phi_T\) of \(T\); this serves as input of the class.
Note
See also
sage.categories.drinfeld_modules
.The base morphism is the morphism \(\gamma: \mathbb{F}_q[T] \to K\). The monic polynomial that generates the kernel of \(\gamma\) is called the \(\mathbb{F}_q[T]\)-characteristic, or function-field characteristic, of the base field. We say that \(\mathbb{F}_q[T]\) is the function ring of \(\phi\); \(K\{\tau\}\) is the Ore polynomial ring. Further, the generator is \(\phi_T\) and the constant coefficient is the constant coefficient of \(\phi_T\).
A Drinfeld module is said to be finite if the field \(K\) is. Despite an emphasis on this case, the base field can be any extension of \(\mathbb{F}_q\):
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z> = Fq.extension(6) sage: phi = DrinfeldModule(A, [z, 4, 1]) sage: phi Drinfeld module defined by T |--> t^2 + 4*t + z
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z',)); (z,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [z, Integer(4), Integer(1)]) >>> phi Drinfeld module defined by T |--> t^2 + 4*t + z
sage: Fq = GF(49) sage: A.<T> = Fq[] sage: K = Frac(A) sage: psi = DrinfeldModule(A, [K(T), T+1]) sage: psi Drinfeld module defined by T |--> (T + 1)*t + T
>>> from sage.all import * >>> Fq = GF(Integer(49)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Frac(A) >>> psi = DrinfeldModule(A, [K(T), T+Integer(1)]) >>> psi Drinfeld module defined by T |--> (T + 1)*t + T
Note
Finite Drinfeld modules are implemented in the class
sage.rings.function_field.drinfeld_modules.finite_drinfeld_module
.Classical references on Drinfeld modules include [Gos1998], [Rosen2002], [VS06] and [Gek1991].
Note
Drinfeld modules are defined in a larger setting, in which the polynomial ring \(\mathbb{F}_q[T]\) is replaced by a more general function ring: the ring of functions in \(k\) that are regular outside \(\infty\), where \(k\) is a function field over \(\mathbb{F}_q\) with transcendence degree \(1\) and \(\infty\) is a fixed place of \(k\). This is out of the scope of this implementation.
INPUT:
function_ring
– a univariate polynomial ring whose base field is a finite fieldgen
– the generator of the Drinfeld module; as a list of coefficients or an Ore polynomialname
(default:'t'
) – the name of the Ore polynomial ring generator
Construction
A Drinfeld module object is constructed by giving the function ring and the generator:
sage: Fq.<z2> = GF(3^2) sage: A.<T> = Fq[] sage: K.<z> = Fq.extension(6) sage: phi = DrinfeldModule(A, [z, 1, 1]) sage: phi Drinfeld module defined by T |--> t^2 + t + z
>>> from sage.all import * >>> Fq = GF(Integer(3)**Integer(2), names=('z2',)); (z2,) = Fq._first_ngens(1) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z',)); (z,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [z, Integer(1), Integer(1)]) >>> phi Drinfeld module defined by T |--> t^2 + t + z
Note
Note that the definition of the base field is implicit; it is automatically defined as the compositum of all the parents of the coefficients.
The above Drinfeld module is finite; it can also be infinite:
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
>>> from sage.all import * >>> L = Frac(A) >>> psi = DrinfeldModule(A, [L(T), Integer(1), T**Integer(3) + T + Integer(1)]) >>> psi Drinfeld module defined by T |--> (T^3 + T + 1)*t^2 + t + T
sage: phi.is_finite() True sage: psi.is_finite() False
>>> from sage.all import * >>> phi.is_finite() True >>> psi.is_finite() 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 regular Ore polynomials:sage: ore_polring = phi.ore_polring() sage: t = ore_polring.gen() sage: rho_T = z + t^3 sage: rho = DrinfeldModule(A, rho_T) sage: rho Drinfeld module defined by T |--> t^3 + z sage: rho(T) == rho_T True
>>> from sage.all import * >>> ore_polring = phi.ore_polring() >>> t = ore_polring.gen() >>> rho_T = z + t**Integer(3) >>> rho = DrinfeldModule(A, rho_T) >>> rho Drinfeld module defined by T |--> t^3 + z >>> rho(T) == rho_T True
Images under the Drinfeld module are computed by calling the object:
sage: phi(T) # phi_T, the generator of the Drinfeld module t^2 + t + z sage: phi(T^3 + T + 1) # phi_(T^3 + T + 1) t^6 + (z^11 + z^9 + 2*z^6 + 2*z^4 + 2*z + 1)*t^4 + (2*z^11 + 2*z^10 + z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^3)*t^3 + (2*z^11 + z^10 + z^9 + 2*z^7 + 2*z^6 + z^5 + z^4 + 2*z^3 + 2*z + 2)*t^2 + (2*z^11 + 2*z^8 + 2*z^6 + z^5 + z^4 + 2*z^2)*t + z^3 + z + 1 sage: phi(1) # phi_1 1
>>> from sage.all import * >>> phi(T) # phi_T, the generator of the Drinfeld module t^2 + t + z >>> phi(T**Integer(3) + T + Integer(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 >>> phi(Integer(1)) # phi_1 1
The category of Drinfeld modules
Drinfeld modules have their own category (see class
sage.categories.drinfeld_modules.DrinfeldModules
):sage: phi.category() Category of Drinfeld modules over Finite Field in z of size 3^12 over its base sage: phi.category() is psi.category() False sage: phi.category() is rho.category() True
>>> from sage.all import * >>> phi.category() Category of Drinfeld modules over Finite Field in z of size 3^12 over its base >>> phi.category() is psi.category() False >>> phi.category() is rho.category() True
One can use the category to directly create new objects:
sage: cat = phi.category() sage: cat.object([z, 0, 0, 1]) Drinfeld module defined by T |--> t^3 + z
>>> from sage.all import * >>> cat = phi.category() >>> cat.object([z, Integer(0), Integer(0), Integer(1)]) Drinfeld module defined by T |--> t^3 + z
The base field of a Drinfeld module
The base field of the Drinfeld module is retrieved using
base()
:sage: phi.base() Finite Field in z of size 3^12 over its base
>>> from sage.all import * >>> phi.base() Finite Field in z of size 3^12 over its base
The base morphism is retrieved using
base_morphism()
:sage: phi.base_morphism() Ring morphism: From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 over its base Defn: T |--> z
>>> from sage.all import * >>> phi.base_morphism() Ring morphism: From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 over its base Defn: T |--> z
Note that the base field is not the field \(K\). Rather, it is a ring extension (see
sage.rings.ring_extension.RingExtension
) whose underlying ring is \(K\) and whose base is the base morphism:sage: phi.base() is K False
>>> from sage.all import * >>> phi.base() is K False
Getters
One can retrieve basic properties:
sage: phi.base_morphism() Ring morphism: From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 over its base Defn: T |--> z
>>> from sage.all import * >>> phi.base_morphism() Ring morphism: From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Finite Field in z of size 3^12 over its base Defn: T |--> z
sage: phi.ore_polring() # K{t} Ore Polynomial Ring in t over Finite Field in z of size 3^12 over its base twisted by Frob^2
>>> from sage.all import * >>> phi.ore_polring() # K{t} Ore Polynomial Ring in t over Finite Field in z of size 3^12 over its base twisted by Frob^2
sage: phi.function_ring() # Fq[T] Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2
>>> from sage.all import * >>> phi.function_ring() # Fq[T] Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2
sage: phi.gen() # phi_T t^2 + t + z sage: phi.gen() == phi(T) True
>>> from sage.all import * >>> phi.gen() # phi_T t^2 + t + z >>> phi.gen() == phi(T) True
sage: phi.constant_coefficient() # Constant coefficient of phi_T z
>>> from sage.all import * >>> phi.constant_coefficient() # Constant coefficient of phi_T z
sage: phi.morphism() # The Drinfeld module as a morphism Ring morphism: From: Univariate Polynomial Ring in T over Finite Field in z2 of size 3^2 To: Ore Polynomial Ring in t over Finite Field in z of size 3^12 over its base twisted by Frob^2 Defn: T |--> t^2 + t + z
>>> from sage.all import * >>> 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 over its base twisted by Frob^2 Defn: T |--> t^2 + t + z
One can compute the rank and height:
sage: phi.rank() 2 sage: phi.height() 1
>>> from sage.all import * >>> phi.rank() 2 >>> phi.height() 1
As well as the j-invariant:
sage: phi.j_invariant() # j-invariant 1
>>> from sage.all import * >>> phi.j_invariant() # j-invariant 1
A Drinfeld \(\mathbb{F}_q[T]\)-module can be seen as an Ore polynomial with positive degree and constant coefficient \(\gamma(T)\), where \(\gamma\) is the base morphism. This analogy is the motivation for the following methods:
sage: phi.coefficients() [z, 1, 1]
>>> from sage.all import * >>> phi.coefficients() [z, 1, 1]
sage: phi.coefficient(1) 1
>>> from sage.all import * >>> phi.coefficient(Integer(1)) 1
Morphisms and isogenies
A morphism of Drinfeld modules \(\phi \to \psi\) is an Ore polynomial \(f \in K\{\tau\}\) such that \(f \phi_a = \psi_a f\) for every \(a\) in the function ring. In our case, this is equivalent to \(f \phi_T = \psi_T f\). An isogeny is a nonzero morphism.
Use the
in
syntax to test if an Ore polynomial defines a morphism:sage: phi(T) in Hom(phi, phi) True sage: t^6 in Hom(phi, phi) True sage: t^5 + 2*t^3 + 1 in Hom(phi, phi) False sage: 1 in Hom(phi, rho) False sage: 1 in Hom(phi, phi) True sage: 0 in Hom(phi, rho) True
>>> from sage.all import * >>> phi(T) in Hom(phi, phi) True >>> t**Integer(6) in Hom(phi, phi) True >>> t**Integer(5) + Integer(2)*t**Integer(3) + Integer(1) in Hom(phi, phi) False >>> Integer(1) in Hom(phi, rho) False >>> Integer(1) in Hom(phi, phi) True >>> Integer(0) in Hom(phi, rho) True
To create a SageMath object representing the morphism, call the homset (
hom
):sage: hom = Hom(phi, phi) sage: frobenius_endomorphism = hom(t^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 sage: identity_morphism Identity morphism of Drinfeld module defined by T |--> t^2 + t + z sage: zero_morphism Endomorphism of Drinfeld module defined by T |--> t^2 + t + z Defn: 0
>>> from sage.all import * >>> hom = Hom(phi, phi) >>> frobenius_endomorphism = hom(t**Integer(6)) >>> identity_morphism = hom(Integer(1)) >>> zero_morphism = hom(Integer(0)) >>> frobenius_endomorphism Endomorphism of Drinfeld module defined by T |--> t^2 + t + z Defn: t^6 >>> identity_morphism Identity morphism of Drinfeld module defined by T |--> t^2 + t + z >>> zero_morphism Endomorphism of Drinfeld module defined by T |--> t^2 + t + z Defn: 0
The underlying Ore polynomial is retrieved with the method
ore_polynomial()
:sage: frobenius_endomorphism.ore_polynomial() t^6 sage: identity_morphism.ore_polynomial() 1
>>> from sage.all import * >>> frobenius_endomorphism.ore_polynomial() t^6 >>> identity_morphism.ore_polynomial() 1
One checks if a morphism is an isogeny, endomorphism or isomorphism:
sage: frobenius_endomorphism.is_isogeny() True sage: identity_morphism.is_isogeny() True sage: zero_morphism.is_isogeny() False sage: frobenius_endomorphism.is_isomorphism() False sage: identity_morphism.is_isomorphism() True sage: zero_morphism.is_isomorphism() False
>>> from sage.all import * >>> frobenius_endomorphism.is_isogeny() True >>> identity_morphism.is_isogeny() True >>> zero_morphism.is_isogeny() False >>> frobenius_endomorphism.is_isomorphism() False >>> identity_morphism.is_isomorphism() True >>> zero_morphism.is_isomorphism() False
The Vélu formula
Let
P
be a nonzero Ore polynomial. We can decide ifP
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: psi = phi.velu(P) sage: psi Drinfeld module defined by T |--> (2*z^11 + 2*z^9 + z^6 + 2*z^5 + 2*z^4 + 2*z^2 + 1)*t^2 + (2*z^11 + 2*z^10 + 2*z^9 + z^8 + 2*z^7 + 2*z^6 + z^5 + 2*z^4 + 2*z^2 + 2*z)*t + z sage: P in Hom(phi, psi) True sage: P * phi(T) == psi(T) * P True
>>> from sage.all import * >>> P = (Integer(2)*z**Integer(6) + z**Integer(3) + Integer(2)*z**Integer(2) + z + Integer(2))*t + z**Integer(11) + Integer(2)*z**Integer(10) + Integer(2)*z**Integer(9) + Integer(2)*z**Integer(8) + z**Integer(7) + Integer(2)*z**Integer(6) + z**Integer(5) + z**Integer(3) + z**Integer(2) + z >>> psi = phi.velu(P) >>> 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 >>> P in Hom(phi, psi) True >>> P * phi(T) == psi(T) * P True
If the input does not define an isogeny, an exception is raised:
sage: phi.velu(0) Traceback (most recent call last): ... ValueError: the input does not define an isogeny sage: phi.velu(t) Traceback (most recent call last): ... ValueError: the input does not define an isogeny
>>> from sage.all import * >>> phi.velu(Integer(0)) Traceback (most recent call last): ... ValueError: the input does not define an isogeny >>> phi.velu(t) Traceback (most recent call last): ... ValueError: the input does not define an isogeny
The action of a Drinfeld module
The \(\mathbb{F}_q[T]\)-Drinfeld module \(\phi\) induces a special left \(\mathbb{F}_q[T]\)-module structure on any field extension \(L/K\). Let \(x \in L\) and \(a\) be in the function ring; the action is defined as \((a, x) \mapsto \phi_a(x)\). The method
action()
returns asage.rings.function_field.drinfeld_modules.action.Action
object representing the Drinfeld module action.Note
In this implementation, \(L\) is \(K\):
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
>>> from sage.all import * >>> action = phi.action() >>> 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
The action on elements is computed by calling the action object:
sage: P = T + 1 sage: a = z sage: action(P, a) ... z^9 + 2*z^8 + 2*z^7 + 2*z^6 + 2*z^3 + z^2 sage: action(0, K.random_element()) 0 sage: action(A.random_element(), 0) 0
>>> from sage.all import * >>> P = T + Integer(1) >>> a = z >>> action(P, a) ... z^9 + 2*z^8 + 2*z^7 + 2*z^6 + 2*z^3 + z^2 >>> action(Integer(0), K.random_element()) 0 >>> action(A.random_element(), Integer(0)) 0
Warning
The class
DrinfeldModuleAction
may be replaced later on. See issues #34833 and #34834.- action()[source]#
Return the action object (
sage.rings.function_field.drinfeld_modules.action.Action
) that represents the module action, on the base codomain, that is induced by the Drinfeld module.OUTPUT: a Drinfeld module action object
EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: action = phi.action() sage: action Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> action = phi.action() >>> action Action on Finite Field in z12 of size 5^12 over its base induced by Drinfeld module defined by T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12
The action on elements is computed as follows:
sage: P = T^2 + T + 1 sage: a = z12 + 1 sage: action(P, a) 3*z12^11 + 2*z12^10 + 3*z12^9 + 3*z12^7 + 4*z12^5 + z12^4 + z12^3 + 2*z12 + 1 sage: action(0, a) 0 sage: action(P, 0) 0
>>> from sage.all import * >>> P = T**Integer(2) + T + Integer(1) >>> a = z12 + Integer(1) >>> action(P, a) 3*z12^11 + 2*z12^10 + 3*z12^9 + 3*z12^7 + 4*z12^5 + z12^4 + z12^3 + 2*z12 + 1 >>> action(Integer(0), a) 0 >>> action(P, Integer(0)) 0
- basic_j_invariant_parameters(coeff_indices=None, nonzero=False)[source]#
Return the list of basic \(j\)-invariant parameters.
See the method
j_invariant()
for definitions.INPUT:
coeff_indices
(list or tuple, or NoneType; default:None
) – indices of the Drinfeld module generator coefficients to be considered in the computation. If the parameter isNone
(default), all the coefficients are involved.nonzero
(boolean, default:False
) – if this flag is set toTrue
, then only the parameters for which the corresponding basic \(j\)-invariant is nonzero are returned.
Warning
The usage of this method can be computationally expensive e.g. if the rank is greater than four, or if \(q\) is large. Setting the
nonzero
flag toTrue
can speed up the computation considerably if the Drinfeld module generator possesses multiple zero coefficients.EXAMPLES:
sage: A = GF(5)['T'] sage: K.<T> = Frac(A) sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) sage: phi.basic_j_invariant_parameters() [((1,), (31, 1)), ((1, 2), (1, 5, 1)), ((1, 2), (7, 4, 1)), ((1, 2), (8, 9, 2)), ((1, 2), (9, 14, 3)), ((1, 2), (10, 19, 4)), ((1, 2), (11, 24, 5)), ((1, 2), (12, 29, 6)), ((1, 2), (13, 3, 1)), ((1, 2), (15, 13, 3)), ((1, 2), (17, 23, 5)), ((1, 2), (19, 2, 1)), ((1, 2), (20, 7, 2)), ((1, 2), (22, 17, 4)), ((1, 2), (23, 22, 5)), ((1, 2), (25, 1, 1)), ((1, 2), (27, 11, 3)), ((1, 2), (29, 21, 5)), ((1, 2), (31, 31, 7)), ((2,), (31, 6))]
>>> from sage.all import * >>> A = GF(Integer(5))['T'] >>> K = Frac(A, names=('T',)); (T,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [T, Integer(0), T+Integer(1), T**Integer(2) + Integer(1)]) >>> phi.basic_j_invariant_parameters() [((1,), (31, 1)), ((1, 2), (1, 5, 1)), ((1, 2), (7, 4, 1)), ((1, 2), (8, 9, 2)), ((1, 2), (9, 14, 3)), ((1, 2), (10, 19, 4)), ((1, 2), (11, 24, 5)), ((1, 2), (12, 29, 6)), ((1, 2), (13, 3, 1)), ((1, 2), (15, 13, 3)), ((1, 2), (17, 23, 5)), ((1, 2), (19, 2, 1)), ((1, 2), (20, 7, 2)), ((1, 2), (22, 17, 4)), ((1, 2), (23, 22, 5)), ((1, 2), (25, 1, 1)), ((1, 2), (27, 11, 3)), ((1, 2), (29, 21, 5)), ((1, 2), (31, 31, 7)), ((2,), (31, 6))]
Use the
nonzero=True
flag to display only the parameters whose \(j\)-invariant value is nonzero:sage: phi.basic_j_invariant_parameters(nonzero=True) [((2,), (31, 6))]
>>> from sage.all import * >>> phi.basic_j_invariant_parameters(nonzero=True) [((2,), (31, 6))]
One can specify the list of coefficients indices to be considered in the computation:
sage: A = GF(2)['T'] sage: K.<T> = Frac(A) sage: phi = DrinfeldModule(A, [T, T, 1, T]) sage: phi.basic_j_invariant_parameters([1, 2]) [((1,), (7, 1)), ((1, 2), (1, 2, 1)), ((1, 2), (4, 1, 1)), ((1, 2), (5, 3, 2)), ((1, 2), (6, 5, 3)), ((1, 2), (7, 7, 4)), ((2,), (7, 3))]
>>> from sage.all import * >>> A = GF(Integer(2))['T'] >>> K = Frac(A, names=('T',)); (T,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [T, T, Integer(1), T]) >>> phi.basic_j_invariant_parameters([Integer(1), Integer(2)]) [((1,), (7, 1)), ((1, 2), (1, 2, 1)), ((1, 2), (4, 1, 1)), ((1, 2), (5, 3, 2)), ((1, 2), (6, 5, 3)), ((1, 2), (7, 7, 4)), ((2,), (7, 3))]
- basic_j_invariants(nonzero=False)[source]#
Return a dictionary whose keys are all the basic \(j\)-invariants parameters and values are the corresponding \(j\)-invariant.
See the method
j_invariant()
for definitions.INPUT:
nonzero
(boolean, default:False
) – if this flag is set toTrue
, then only the parameters for which the corresponding basic \(j\)-invariant is nonzero are returned.
Warning
The usage of this method can be computationally expensive e.g. if the rank is greater than four, or if \(q\) is large. Setting the
nonzero
flag toTrue
can speed up the computation considerably if the Drinfeld module generator possesses multiple zero coefficients.EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.basic_j_invariants() {((1,), (26, 1)): z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2}
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> phi.basic_j_invariants() {((1,), (26, 1)): z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2}
sage: phi = DrinfeldModule(A, [p_root, 0, 1, z12]) sage: phi.basic_j_invariants(nonzero=True) {((2,), (651, 26)): z12^11 + 3*z12^10 + 4*z12^9 + 3*z12^8 + z12^7 + 2*z12^6 + 3*z12^4 + 2*z12^3 + z12^2 + 4*z12}
>>> from sage.all import * >>> phi = DrinfeldModule(A, [p_root, Integer(0), Integer(1), z12]) >>> phi.basic_j_invariants(nonzero=True) {((2,), (651, 26)): z12^11 + 3*z12^10 + 4*z12^9 + 3*z12^8 + z12^7 + 2*z12^6 + 3*z12^4 + 2*z12^3 + z12^2 + 4*z12}
sage: A = GF(5)['T'] sage: K.<T> = Frac(A) sage: phi = DrinfeldModule(A, [T, T + 2, T+1, 1]) sage: J_phi = phi.basic_j_invariants(); J_phi {((1,), (31, 1)): T^31 + 2*T^30 + 2*T^26 + 4*T^25 + 2*T^6 + 4*T^5 + 4*T + 3, ((1, 2), (1, 5, 1)): T^6 + 2*T^5 + T + 2, ((1, 2), (7, 4, 1)): T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3, ((1, 2), (8, 9, 2)): T^17 + 2*T^15 + T^14 + 4*T^13 + 4*T^11 + 4*T^10 + 3*T^9 + 2*T^8 + 3*T^7 + 2*T^6 + 3*T^5 + 2*T^4 + 3*T^3 + 4*T^2 + 3*T + 1, ((1, 2), (9, 14, 3)): T^23 + 2*T^22 + 2*T^21 + T^19 + 4*T^18 + T^17 + 4*T^16 + T^15 + 4*T^14 + 2*T^12 + 4*T^11 + 4*T^10 + 2*T^8 + 4*T^7 + 4*T^6 + 2*T^4 + T^2 + 2*T + 2, ((1, 2), (10, 19, 4)): T^29 + 4*T^28 + T^27 + 4*T^26 + T^25 + 2*T^24 + 3*T^23 + 2*T^22 + 3*T^21 + 2*T^20 + 4*T^19 + T^18 + 4*T^17 + T^16 + 4*T^15 + T^9 + 4*T^8 + T^7 + 4*T^6 + T^5 + 4*T^4 + T^3 + 4*T^2 + T + 4, ... ((2,), (31, 6)): T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1} sage: J_phi[((1, 2), (7, 4, 1))] T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3
>>> from sage.all import * >>> A = GF(Integer(5))['T'] >>> K = Frac(A, names=('T',)); (T,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [T, T + Integer(2), T+Integer(1), Integer(1)]) >>> J_phi = phi.basic_j_invariants(); J_phi {((1,), (31, 1)): T^31 + 2*T^30 + 2*T^26 + 4*T^25 + 2*T^6 + 4*T^5 + 4*T + 3, ((1, 2), (1, 5, 1)): T^6 + 2*T^5 + T + 2, ((1, 2), (7, 4, 1)): T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3, ((1, 2), (8, 9, 2)): T^17 + 2*T^15 + T^14 + 4*T^13 + 4*T^11 + 4*T^10 + 3*T^9 + 2*T^8 + 3*T^7 + 2*T^6 + 3*T^5 + 2*T^4 + 3*T^3 + 4*T^2 + 3*T + 1, ((1, 2), (9, 14, 3)): T^23 + 2*T^22 + 2*T^21 + T^19 + 4*T^18 + T^17 + 4*T^16 + T^15 + 4*T^14 + 2*T^12 + 4*T^11 + 4*T^10 + 2*T^8 + 4*T^7 + 4*T^6 + 2*T^4 + T^2 + 2*T + 2, ((1, 2), (10, 19, 4)): T^29 + 4*T^28 + T^27 + 4*T^26 + T^25 + 2*T^24 + 3*T^23 + 2*T^22 + 3*T^21 + 2*T^20 + 4*T^19 + T^18 + 4*T^17 + T^16 + 4*T^15 + T^9 + 4*T^8 + T^7 + 4*T^6 + T^5 + 4*T^4 + T^3 + 4*T^2 + T + 4, ... ((2,), (31, 6)): T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1} >>> J_phi[((Integer(1), Integer(2)), (Integer(7), Integer(4), Integer(1)))] T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3
- coefficient(n)[source]#
Return the \(n\)-th coefficient of the generator.
INPUT:
n
– a nonnegative integer
OUTPUT: an element in the base codomain
EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.coefficient(0) 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi.coefficient(0) == p_root True sage: phi.coefficient(1) z12^3 sage: phi.coefficient(2) z12^5 sage: phi.coefficient(5) Traceback (most recent call last): ... ValueError: input must be >= 0 and <= rank
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> phi.coefficient(Integer(0)) 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 >>> phi.coefficient(Integer(0)) == p_root True >>> phi.coefficient(Integer(1)) z12^3 >>> phi.coefficient(Integer(2)) z12^5 >>> phi.coefficient(Integer(5)) Traceback (most recent call last): ... ValueError: input must be >= 0 and <= rank
- coefficients(sparse=True)[source]#
Return the coefficients of the generator, as a list.
If the flag
sparse
isTrue
(default), only return the nonzero coefficients; otherwise, return all of them.INPUT:
sparse
– a boolean
EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.coefficients() [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, z12^3, z12^5]
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> phi.coefficients() [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, z12^3, z12^5]
Careful, the method only returns the nonzero coefficients, unless otherwise specified:
sage: rho = DrinfeldModule(A, [p_root, 0, 0, 0, 1]) sage: rho.coefficients() [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, 1] sage: rho.coefficients(sparse=False) [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, 0, 0, 0, 1]
>>> from sage.all import * >>> rho = DrinfeldModule(A, [p_root, Integer(0), Integer(0), Integer(0), Integer(1)]) >>> rho.coefficients() [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, 1] >>> rho.coefficients(sparse=False) [2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12, 0, 0, 0, 1]
- gen()[source]#
Return the generator of the Drinfeld module.
EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.gen() == phi(T) True
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> phi.gen() == phi(T) True
- height()[source]#
Return the height of the Drinfeld module if the function field characteristic is a prime ideal; raise ValueError otherwise.
The height of a Drinfeld module is defined when the function field characteristic is a prime ideal. In our case, this ideal is even generated by a monic polynomial \(\mathfrak{p}\) in the function field. Write \(\phi_\mathfrak{p} = a_s \tau^s + \dots + \tau^{r*\deg(\mathfrak{p})}\). The height of the Drinfeld module is the well-defined positive integer \(h = \frac{s}{\deg(\mathfrak{p})}\).
Note
See [Gos1998], Definition 4.5.8 for the general definition.
EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.height() == 1 True sage: phi.is_ordinary() True
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> phi.height() == Integer(1) True >>> phi.is_ordinary() True
sage: Fq = GF(343) sage: A.<T> = Fq[] sage: K.<z6> = Fq.extension(2) sage: phi = DrinfeldModule(A, [1, 0, z6]) sage: phi.height() 2 sage: phi.is_supersingular() True
>>> from sage.all import * >>> Fq = GF(Integer(343)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(2), names=('z6',)); (z6,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [Integer(1), Integer(0), z6]) >>> phi.height() 2 >>> phi.is_supersingular() True
In characteristic zero, height is not defined:
sage: L = A.fraction_field() sage: phi = DrinfeldModule(A, [L(T), L(1)]) sage: phi.height() Traceback (most recent call last): ... ValueError: height is only defined for prime function field characteristic
>>> from sage.all import * >>> L = A.fraction_field() >>> phi = DrinfeldModule(A, [L(T), L(Integer(1))]) >>> phi.height() Traceback (most recent call last): ... ValueError: height is only defined for prime function field characteristic
- hom(x, codomain=None)[source]#
Return the homomorphism defined by
x
having this Drinfeld module as domain.We recall that a homomorphism \(f : \phi \to \psi\) between two Drinfeld modules is defined by an Ore polynomial \(u\), which is subject to the relation \(phi_T u = u \psi_T\).
INPUT:
x
– an element of the ring of functions, or an Ore polynomialcodomain
– a Drinfeld module orNone
(default:None
)
EXAMPLES:
sage: Fq = GF(5) sage: A.<T> = Fq[] sage: K.<z> = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) sage: phi Drinfeld module defined by T |--> z*t^3 + t^2 + z
>>> from sage.all import * >>> Fq = GF(Integer(5)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(3), names=('z',)); (z,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [z, Integer(0), Integer(1), z]) >>> phi Drinfeld module defined by T |--> z*t^3 + t^2 + z
An important class of endomorphisms of a Drinfeld module \(\phi\) is given by scalar multiplications, that are endomorphisms corresponding to the Ore polynomials \(\phi_a\) with \(a\) in the function 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
>>> from sage.all import * >>> phi.hom(T) Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z Defn: z*t^3 + t^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
>>> from sage.all import * >>> phi.hom(T**Integer(2) + Integer(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
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
>>> from sage.all import * >>> t = phi.ore_variable() >>> phi.hom(t**Integer(3)) Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z Defn: t^3
If the input Ore polynomial defines a morphism to another Drinfeld module, the latter is determined automatically:
sage: phi.hom(t + 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 sage.all import * >>> phi.hom(t + Integer(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
- is_finite()[source]#
Return
True
if this Drinfeld module is finite,False
otherwise.EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.is_finite() True sage: B.<Y> = Fq[] sage: L = Frac(B) sage: psi = DrinfeldModule(A, [L(2), L(1)]) sage: psi.is_finite() False
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> phi.is_finite() True >>> B = Fq['Y']; (Y,) = B._first_ngens(1) >>> L = Frac(B) >>> psi = DrinfeldModule(A, [L(Integer(2)), L(Integer(1))]) >>> psi.is_finite() False
- is_isomorphic(other, absolutely=False)[source]#
Return
True
if this Drinfeld module is isomorphic toother
; returnFalse
otherwise.INPUT:
absolutely
– a boolean (default:False
); ifTrue
, check the existence of an isomorphism defined on the base field; ifFalse
, check over an algebraic closure.
EXAMPLES:
sage: Fq = GF(5) sage: A.<T> = Fq[] sage: K.<z> = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) sage: t = phi.ore_variable()
>>> from sage.all import * >>> Fq = GF(Integer(5)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(3), names=('z',)); (z,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [z, Integer(0), Integer(1), z]) >>> t = phi.ore_variable()
We create a second Drinfeld module, which is isomorphic to \(\phi\) and then check that they are indeed isomorphic:
sage: psi = phi.velu(z) sage: phi.is_isomorphic(psi) True
>>> from sage.all import * >>> psi = phi.velu(z) >>> phi.is_isomorphic(psi) True
In the example below, \(\phi\) and \(\psi\) are isogenous but not isomorphic:
sage: psi = phi.velu(t + 1) sage: phi.is_isomorphic(psi) False
>>> from sage.all import * >>> psi = phi.velu(t + Integer(1)) >>> phi.is_isomorphic(psi) False
Here is an example of two Drinfeld modules which are isomorphic on an algebraic closure but not on the base field:
sage: phi = DrinfeldModule(A, [z, 1]) sage: psi = DrinfeldModule(A, [z, z]) sage: phi.is_isomorphic(psi) False sage: phi.is_isomorphic(psi, absolutely=True) True
>>> from sage.all import * >>> phi = DrinfeldModule(A, [z, Integer(1)]) >>> psi = DrinfeldModule(A, [z, z]) >>> phi.is_isomorphic(psi) False >>> phi.is_isomorphic(psi, absolutely=True) True
On certain fields, testing isomorphisms over the base field may fail:
sage: L = A.fraction_field() sage: T = L.gen() sage: phi = DrinfeldModule(A, [T, 0, 1]) sage: psi = DrinfeldModule(A, [T, 0, T]) sage: psi.is_isomorphic(phi) Traceback (most recent call last): ... NotImplementedError: cannot solve the equation u^24 == T
>>> from sage.all import * >>> L = A.fraction_field() >>> T = L.gen() >>> phi = DrinfeldModule(A, [T, Integer(0), Integer(1)]) >>> psi = DrinfeldModule(A, [T, Integer(0), T]) >>> psi.is_isomorphic(phi) Traceback (most recent call last): ... NotImplementedError: cannot solve the equation u^24 == T
However, it never fails over the algebraic closure:
sage: psi.is_isomorphic(phi, absolutely=True) True
>>> from sage.all import * >>> psi.is_isomorphic(phi, absolutely=True) True
Note finally that when the constant coefficients of \(\phi_T\) and \(\psi_T\) differ, \(\phi\) and \(\psi\) do not belong to the same category and checking whether they are isomorphic does not make sense; in this case, an error is raised:
sage: phi = DrinfeldModule(A, [z, 0, 1]) sage: psi = DrinfeldModule(A, [z^2, 0, 1]) sage: phi.is_isomorphic(psi) Traceback (most recent call last): ... ValueError: Drinfeld modules are not in the same category
>>> from sage.all import * >>> phi = DrinfeldModule(A, [z, Integer(0), Integer(1)]) >>> psi = DrinfeldModule(A, [z**Integer(2), Integer(0), Integer(1)]) >>> phi.is_isomorphic(psi) Traceback (most recent call last): ... ValueError: Drinfeld modules are not in the same category
- j_invariant(parameter=None, check=True)[source]#
Return the \(j\)-invariant of the Drinfeld \(\mathbb{F}_q[T]\)-module for the given parameter.
Suppose that \(\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r\) with \(g_r \neq 0\). Then the \(((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r))\)-\(j\)-invariant of \(\phi\) is defined by
\[j_{k_1, \ldots, k_n}^{d_1, \ldots, d_n, d_r}(\phi) := \frac{1}{g_r^{d_r}}\prod_{i = 1}^n g_{k_i}^{d_i}\]where \(1\leqslant k_1 < k_2 < \ldots < k_n \leqslant r - 1\) and the integers \(d_i\) satisfy the weight-0 condition:
\[d_1 (q^{k_1} - 1) + d_2 (q^{k_2} - 1) + \cdots + d_{n} (q^{k_n} - 1) = d_r (q^r - 1).\]Furthermore, if \(\gcd(d_1,\ldots, d_n, d_r) = 1\) and
\[0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), \quad 1 \leq i \leq n,\]then the \(j\)-invariant is called basic. See the method
basic_j_invariant_parameters()
for computing the list of all basic \(j\)-invariant parameters.Note
In [Pap2023], Papikian follows a slightly different convention:
His \(j\)-invariants (see Definition 3.8.7) correspond to our basic \(j\)-invariants, as defined above.
His basic \(j\)-invariant (see Example 3.8.10) correspond to our \(j_k\)-invariants, as implemented in
jk_invariants()
.
We chose to follow Potemine’s convention, as he introduced those objects in [Pot1998]. Theorem 2.2 of [Pot1998] or Theorem 3.8.11 of [Pap2023] assert that two Drinfeld \(\mathbb F_q[T]\)-modules over \(K\) are isomorphic over the separable closure of \(K\) if and only if their basic \(j\)-invariants (as implemented here) coincide for any well-defined couple of tuples \(((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))\), .
INPUT:
parameter
(tuple or list, integer or NoneType; default:None
) – the \(j\)-invariant parameter:If
parameter
is a list or a tuple, then it must be of the form: \(((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))\), where the \(k_i\) and \(d_i\) are integers satisfying the weight-0 condition described above.If
parameter
is an integer \(k\) then the method returns thej
-invariant associated to the parameter \(((k,), (d_k, d_r))\);If
parameter
isNone
and the rank of the Drinfeld module is 2, then the method returns its usual \(j\)-invariant, that is the \(j\)-invariant for the parameter \(((1,), (q+1, 1))\).
check
(bool, default:True
) – if this flag is set toFalse
then the code will not check if the given parameter is valid and satisfy the weight-0 condition.
OUTPUT: the \(j\)-invariant of
self
for the given parameter.EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.j_invariant() z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2 sage: psi = DrinfeldModule(A, [p_root, 1, 1]) sage: psi.j_invariant() 1 sage: rho = DrinfeldModule(A, [p_root, 0, 1]) sage: rho.j_invariant() 0
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> phi.j_invariant() z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2 >>> psi = DrinfeldModule(A, [p_root, Integer(1), Integer(1)]) >>> psi.j_invariant() 1 >>> rho = DrinfeldModule(A, [p_root, Integer(0), Integer(1)]) >>> rho.j_invariant() 0
sage: A = GF(5)['T'] sage: K.<T> = Frac(A) sage: phi = DrinfeldModule(A, [T, T^2, 1, T + 1, T^3]) sage: phi.j_invariant(1) T^309 sage: phi.j_invariant(2) 1/T^3 sage: phi.j_invariant(3) (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93
>>> from sage.all import * >>> A = GF(Integer(5))['T'] >>> K = Frac(A, names=('T',)); (T,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [T, T**Integer(2), Integer(1), T + Integer(1), T**Integer(3)]) >>> phi.j_invariant(Integer(1)) T^309 >>> phi.j_invariant(Integer(2)) 1/T^3 >>> phi.j_invariant(Integer(3)) (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93
The parameter can either be a tuple or a list:
sage: Fq.<a> = GF(7) sage: A.<T> = Fq[] sage: phi = DrinfeldModule(A, [a, a^2 + a, 0, 3*a, a^2+1]) sage: J = phi.j_invariant(((1, 3), (267, 269, 39))); J 5 sage: J == (phi.coefficient(1)**267)*(phi.coefficient(3)**269)/(phi.coefficient(4)**39) True sage: phi.j_invariant([[3], [400, 57]]) 4 sage: phi.j_invariant([[3], [400, 57]]) == phi.j_invariant(3) True
>>> from sage.all import * >>> Fq = GF(Integer(7), names=('a',)); (a,) = Fq._first_ngens(1) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> phi = DrinfeldModule(A, [a, a**Integer(2) + a, Integer(0), Integer(3)*a, a**Integer(2)+Integer(1)]) >>> J = phi.j_invariant(((Integer(1), Integer(3)), (Integer(267), Integer(269), Integer(39)))); J 5 >>> J == (phi.coefficient(Integer(1))**Integer(267))*(phi.coefficient(Integer(3))**Integer(269))/(phi.coefficient(Integer(4))**Integer(39)) True >>> phi.j_invariant([[Integer(3)], [Integer(400), Integer(57)]]) 4 >>> phi.j_invariant([[Integer(3)], [Integer(400), Integer(57)]]) == phi.j_invariant(Integer(3)) True
The list of all basic \(j\)-invariant parameters can be retrieved using the method
basic_j_invariant_parameters()
:sage: A = GF(3)['T'] sage: K.<T> = Frac(A) sage: phi = DrinfeldModule(A, [T, T^2 + T + 1, 0, T^4 + 1, T - 1]) sage: param = phi.basic_j_invariant_parameters(nonzero=True) sage: phi.j_invariant(param[1]) T^13 + 2*T^12 + T + 2 sage: phi.j_invariant(param[2]) T^35 + 2*T^31 + T^27 + 2*T^8 + T^4 + 2
>>> from sage.all import * >>> A = GF(Integer(3))['T'] >>> K = Frac(A, names=('T',)); (T,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [T, T**Integer(2) + T + Integer(1), Integer(0), T**Integer(4) + Integer(1), T - Integer(1)]) >>> param = phi.basic_j_invariant_parameters(nonzero=True) >>> phi.j_invariant(param[Integer(1)]) T^13 + 2*T^12 + T + 2 >>> phi.j_invariant(param[Integer(2)]) T^35 + 2*T^31 + T^27 + 2*T^8 + T^4 + 2
- jk_invariants()[source]#
Return a dictionary whose keys are all the integers \(1 \leqslant k \leqslant r-1\) and the values are the corresponding \(j_k\)-invariants
Recall that the \(j_k\)-invariant of self is defined by:
\[j_k := \frac{g_k^{(q^r - 1)/(\mathrm{gcd}(k, r) - 1)}}{g_r^{(q^k - 1)/(\mathrm{gcd}(k, r) - 1)}}\]where \(g_i\) is the \(i\)-th coefficient of the generator of self.
EXAMPLES:
sage: A = GF(3)['T'] sage: K.<T> = Frac(A) sage: phi = DrinfeldModule(A, [T, 1, T+1, T^3, T^6]) sage: jk_inv = phi.jk_invariants(); jk_inv {1: 1/T^6, 2: (T^10 + T^9 + T + 1)/T^6, 3: T^42} sage: jk_inv[2] (T^10 + T^9 + T + 1)/T^6
>>> from sage.all import * >>> A = GF(Integer(3))['T'] >>> K = Frac(A, names=('T',)); (T,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [T, Integer(1), T+Integer(1), T**Integer(3), T**Integer(6)]) >>> jk_inv = phi.jk_invariants(); jk_inv {1: 1/T^6, 2: (T^10 + T^9 + T + 1)/T^6, 3: T^42} >>> jk_inv[Integer(2)] (T^10 + T^9 + T + 1)/T^6
sage: F = GF(7**2) sage: A = F['T'] sage: E.<z> = F.extension(4) sage: phi = DrinfeldModule(A, [z^2, 1, z+1, z^2, z, z+1]) sage: phi.jk_invariants() {1: 5*z^7 + 2*z^6 + 5*z^5 + 2*z^4 + 5*z^3 + z^2 + z + 2, 2: 3*z^7 + 4*z^6 + 5*z^5 + 6*z^4 + 4*z, 3: 5*z^7 + 6*z^6 + 6*z^5 + 4*z^3 + z^2 + 2*z + 1, 4: 3*z^6 + 2*z^5 + 4*z^4 + 2*z^3 + 4*z^2 + 6*z + 2}
>>> from sage.all import * >>> F = GF(Integer(7)**Integer(2)) >>> A = F['T'] >>> E = F.extension(Integer(4), names=('z',)); (z,) = E._first_ngens(1) >>> phi = DrinfeldModule(A, [z**Integer(2), Integer(1), z+Integer(1), z**Integer(2), z, z+Integer(1)]) >>> phi.jk_invariants() {1: 5*z^7 + 2*z^6 + 5*z^5 + 2*z^4 + 5*z^3 + z^2 + z + 2, 2: 3*z^7 + 4*z^6 + 5*z^5 + 6*z^4 + 4*z, 3: 5*z^7 + 6*z^6 + 6*z^5 + 4*z^3 + z^2 + 2*z + 1, 4: 3*z^6 + 2*z^5 + 4*z^4 + 2*z^3 + 4*z^2 + 6*z + 2}
- morphism()[source]#
Return the morphism object that defines the Drinfeld module.
OUTPUT: a ring morphism from the function ring to the Ore polynomial ring
EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.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 over its base twisted by Frob^2 Defn: T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: from sage.rings.morphism import RingHomomorphism sage: isinstance(phi.morphism(), RingHomomorphism) True
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> 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 over its base twisted by Frob^2 Defn: T |--> z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 >>> from sage.rings.morphism import RingHomomorphism >>> isinstance(phi.morphism(), RingHomomorphism) True
Actually, the
DrinfeldModule
method__call__()
simply class the__call__
method of this morphism:sage: phi.morphism()(T) == phi(T) True sage: a = A.random_element() sage: phi.morphism()(a) == phi(a) True
>>> from sage.all import * >>> phi.morphism()(T) == phi(T) True >>> a = A.random_element() >>> phi.morphism()(a) == phi(a) True
And many methods of the Drinfeld module have a counterpart in the morphism object:
sage: m = phi.morphism() sage: m.domain() is phi.function_ring() True sage: m.codomain() is phi.ore_polring() True sage: m.im_gens() [z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12] sage: phi(T) == m.im_gens()[0] True
>>> from sage.all import * >>> m = phi.morphism() >>> m.domain() is phi.function_ring() True >>> m.codomain() is phi.ore_polring() True >>> m.im_gens() [z12^5*t^2 + z12^3*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12] >>> phi(T) == m.im_gens()[Integer(0)] True
- rank()[source]#
Return the rank of the Drinfeld module.
In our case, the rank is the degree of the generator.
OUTPUT: an integer
EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: phi.rank() 2 sage: psi = DrinfeldModule(A, [p_root, 2]) sage: psi.rank() 1 sage: rho = DrinfeldModule(A, [p_root, 0, 0, 0, 1]) sage: rho.rank() 4
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> phi.rank() 2 >>> psi = DrinfeldModule(A, [p_root, Integer(2)]) >>> psi.rank() 1 >>> rho = DrinfeldModule(A, [p_root, Integer(0), Integer(0), Integer(0), Integer(1)]) >>> rho.rank() 4
- scalar_multiplication(x)[source]#
Return the endomorphism of this Drinfeld module, which is the multiplication by \(x\), i.e. the isogeny defined by the Ore polynomial \(\phi_x\).
INPUT:
x
– an element in the ring of functions
EXAMPLES:
sage: Fq = GF(5) sage: A.<T> = Fq[] sage: K.<z> = Fq.extension(3) sage: phi = DrinfeldModule(A, [z, 0, 1, z]) sage: phi Drinfeld module defined by T |--> z*t^3 + t^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
>>> from sage.all import * >>> Fq = GF(Integer(5)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(3), names=('z',)); (z,) = K._first_ngens(1) >>> phi = DrinfeldModule(A, [z, Integer(0), Integer(1), z]) >>> phi Drinfeld module defined by T |--> z*t^3 + t^2 + z >>> 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
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
>>> from sage.all import * >>> phi.hom(T**Integer(2) + Integer(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
- velu(isog)[source]#
Return a new Drinfeld module such that input is an isogeny to this module with domain
self
; if no such isogeny exists, raise an exception.INPUT:
isog
– the Ore polynomial that defines the isogeny
OUTPUT: a Drinfeld module
ALGORITHM:
The input defines an isogeny if only if:
1. The degree of the characteristic divides the height of the input. (The height of an Ore polynomial \(P(\tau)\) is the maximum \(n\) such that \(\tau^n\) right-divides \(P(\tau)\).)
2. The input right-divides the generator, which can be tested with Euclidean division.
We test if the input is an isogeny, and, if it is, we return the quotient of the Euclidean division.
Height and Euclidean division of Ore polynomials are implemented as methods of class
sage.rings.polynomial.ore_polynomial_element.OrePolynomial
.Another possible algorithm is to recursively solve a system, see arXiv 2203.06970, Eq. 1.1.
EXAMPLES:
sage: Fq = GF(25) sage: A.<T> = Fq[] sage: K.<z12> = Fq.extension(6) sage: p_root = 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: phi = DrinfeldModule(A, [p_root, z12^3, z12^5]) sage: t = phi.ore_polring().gen() sage: isog = t + 2*z12^11 + 4*z12^9 + 2*z12^8 + 2*z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + 4*z12^2 + 4*z12 + 4 sage: psi = phi.velu(isog) sage: psi Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 sage: isog in Hom(phi, psi) True
>>> from sage.all import * >>> Fq = GF(Integer(25)) >>> A = Fq['T']; (T,) = A._first_ngens(1) >>> K = Fq.extension(Integer(6), names=('z12',)); (z12,) = K._first_ngens(1) >>> p_root = Integer(2)*z12**Integer(11) + Integer(2)*z12**Integer(10) + z12**Integer(9) + Integer(3)*z12**Integer(8) + z12**Integer(7) + Integer(2)*z12**Integer(5) + Integer(2)*z12**Integer(4) + Integer(3)*z12**Integer(3) + z12**Integer(2) + Integer(2)*z12 >>> phi = DrinfeldModule(A, [p_root, z12**Integer(3), z12**Integer(5)]) >>> t = phi.ore_polring().gen() >>> isog = t + Integer(2)*z12**Integer(11) + Integer(4)*z12**Integer(9) + Integer(2)*z12**Integer(8) + Integer(2)*z12**Integer(6) + Integer(3)*z12**Integer(5) + z12**Integer(4) + Integer(2)*z12**Integer(3) + Integer(4)*z12**Integer(2) + Integer(4)*z12 + Integer(4) >>> psi = phi.velu(isog) >>> psi Drinfeld module defined by T |--> (z12^11 + 3*z12^10 + z12^9 + z12^7 + z12^5 + 4*z12^4 + 4*z12^3 + z12^2 + 1)*t^2 + (2*z12^11 + 4*z12^10 + 2*z12^8 + z12^6 + 3*z12^5 + z12^4 + 2*z12^3 + z12^2 + z12 + 4)*t + 2*z12^11 + 2*z12^10 + z12^9 + 3*z12^8 + z12^7 + 2*z12^5 + 2*z12^4 + 3*z12^3 + z12^2 + 2*z12 >>> isog in Hom(phi, psi) True
This method works for endomorphisms as well:
sage: phi.velu(phi(T)) is phi True sage: phi.velu(t^6) is phi True
>>> from sage.all import * >>> phi.velu(phi(T)) is phi True >>> phi.velu(t**Integer(6)) is phi True
The following inputs do not define isogenies, and the method returns
None
:sage: phi.velu(0) Traceback (most recent call last): ... ValueError: the input does not define an isogeny sage: phi.velu(t) Traceback (most recent call last): ... ValueError: the input does not define an isogeny sage: phi.velu(t^3 + t + 2) Traceback (most recent call last): ... ValueError: the input does not define an isogeny
>>> from sage.all import * >>> phi.velu(Integer(0)) Traceback (most recent call last): ... ValueError: the input does not define an isogeny >>> phi.velu(t) Traceback (most recent call last): ... ValueError: the input does not define an isogeny >>> phi.velu(t**Integer(3) + t + Integer(2)) Traceback (most recent call last): ... ValueError: the input does not define an isogeny