# Drinfeld module morphisms#

This module provides the class sage.rings.function_fields.drinfeld_module.morphism.DrinfeldModuleMorphism.

AUTHORS: - Antoine Leudière (2022-04)

class sage.rings.function_field.drinfeld_modules.morphism.DrinfeldModuleMorphism(parent, ore_pol)#

This class represents Drinfeld $$\mathbb{F}_q[T]$$-module morphisms.

Let $$\phi$$ and $$\psi$$ be two Drinfeld $$\mathbb{F}_q[T]$$-modules over a field $$K$$. A morphism of Drinfeld modules $$\phi \to \psi$$ is an Ore polynomial $$f \in K\{\tau\}$$ such that $$f \phi_a = \psi_a f$$ for every $$a \in \mathbb{F}_q[T]$$. In our case, this is equivalent to $$f \phi_T = \psi_T f$$. An isogeny is a nonzero morphism.

To create a morphism object, the user should never explicitly instantiate DrinfeldModuleMorphism, but rather call the parent homset with the defining Ore polynomial:

sage: Fq = GF(4)
sage: A.<T> = Fq[]
sage: K.<z> = 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: 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


The given Ore polynomial must indeed define a morphism:

sage: morphism = Hom(phi, psi)(1)
Traceback (most recent call last):
...
ValueError: Ore polynomial does not define a morphism


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
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
sage: morphism.codomain() is psi
True

sage: morphism.ore_polynomial()
t + z^5 + z^3 + z + 1
sage: morphism.ore_polynomial() is ore_pol
True


One can check various properties:

sage: morphism.is_zero()
False
sage: morphism.is_isogeny()
True
sage: morphism.is_endomorphism()
False
sage: morphism.is_isomorphism()
False

characteristic_polynomial(var='X')#

Return the characteristic polynomial of this endomorphism.

INPUT:

• var – a string (default: X), the name of the variable of the characteristic polynomial

EXAMPLES:

sage: Fq = GF(5)
sage: A.<T> = Fq[]
sage: K.<z> = Fq.extension(3)
sage: phi = DrinfeldModule(A, [z, 0, 1, z])

sage: f = phi.frobenius_endomorphism()
sage: f.characteristic_polynomial()
X^3 + (T + 1)*X^2 + (2*T + 3)*X + 2*T^3 + T + 1


We verify, on an example, that the caracteristic polynomial of a morphism corresponding to $$\phi_a$$ is $$(X-a)^r$$ where $$r$$ is the rank:

sage: g = phi.hom(T^2 + 1)
sage: chi = g.characteristic_polynomial()
sage: chi.factor()
(X + 4*T^2 + 4)^3


An example with another variable name:

sage: f.characteristic_polynomial(var='Y')
Y^3 + (T + 1)*Y^2 + (2*T + 3)*Y + 2*T^3 + T + 1

charpoly(var='X')#

Return the characteristic polynomial of this endomorphism.

INPUT:

• var – a string (default: X), the name of the variable of the characteristic polynomial

EXAMPLES:

sage: Fq = GF(5)
sage: A.<T> = Fq[]
sage: K.<z> = Fq.extension(3)
sage: phi = DrinfeldModule(A, [z, 0, 1, z])

sage: f = phi.frobenius_endomorphism()
sage: chi = f.charpoly()
sage: chi
X^3 + (T + 1)*X^2 + (2*T + 3)*X + 2*T^3 + T + 1


We check that the characteristic polynomial annihilates the morphism (Cayley-Hamilton’s theorem):

sage: chi(f)
Endomorphism of Drinfeld module defined by T |--> z*t^3 + t^2 + z
Defn: 0


We verify, on an example, that the caracteristic polynomial of the morphism corresponding to $$\phi_a$$ is $$(X-a)^r$$ where $$r$$ is the rank:

sage: g = phi.hom(T^2 + 1)
sage: g.charpoly().factor()
(X + 4*T^2 + 4)^3


An example with another variable name:

sage: f.charpoly(var='Y')
Y^3 + (T + 1)*Y^2 + (2*T + 3)*Y + 2*T^3 + T + 1

dual_isogeny()#

Return a dual isogeny to this morphism.

By definition, a dual isogeny of $$f : \phi \to \psi$$ is an isogeny $$g : \psi \to \phi$$ such that the composite $$g \circ f$$ is the multiplication by a generator of the norm of $$f$$.

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()
sage: f = phi.hom(t + 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
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


We check that $$f \circ g$$ (resp. $$g \circ f$$) is the multiplication by the norm of $$f$$:

sage: a = f.norm().gen(); a
T + 4
sage: g * f == phi.hom(a)
True

sage: psi = f.codomain()
sage: f * g == psi.hom(a)
True

inverse()#

Return the inverse of this morphism.

Only morphisms defined by constant nonzero Ore polynomials are invertible.

EXAMPLES:

sage: Fq = GF(5)
sage: A.<T> = Fq[]
sage: K.<z> = 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
Defn: 2
sage: f.inverse()
Endomorphism of Drinfeld module defined by T |--> z^2*t^3 + z*t^2 + t + z
Defn: 3


Inversion of general isomorphisms between different Drinfeld modules also works:

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
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
Defn: 3*z^2 + 4


When the morphism is not invertible, an error is raised:

sage: F = phi.frobenius_endomorphism()
sage: F.inverse()
Traceback (most recent call last):
...
ZeroDivisionError: this morphism is not invertible

is_identity()#

Return True whether the morphism is the identity morphism.

EXAMPLES:

sage: Fq = GF(2)
sage: A.<T> = Fq[]
sage: K.<z6> = Fq.extension(6)
sage: phi = DrinfeldModule(A, [z6, 1, 1])
sage: morphism = End(phi)(1)
sage: morphism.is_identity()
True

sage: psi = DrinfeldModule(A, [z6, z6^4 + z6^2 + 1, 1])
sage: t = phi.ore_polring().gen()
sage: morphism = Hom(phi, psi)(t + z6^5 + z6^2 + 1)
sage: morphism.is_identity()
False

is_isogeny()#

Return True whether the morphism is an isogeny.

EXAMPLES:

sage: Fq = GF(2)
sage: A.<T> = Fq[]
sage: K.<z6> = 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: morphism.is_isogeny()
True

sage: zero_morphism = End(phi)(0)
sage: zero_morphism.is_isogeny()
False

sage: identity_morphism = End(phi)(1)
sage: identity_morphism.is_isogeny()
True

sage: frobenius_endomorphism = phi.frobenius_endomorphism()
sage: frobenius_endomorphism.is_isogeny()
True

is_isomorphism()#

Return True whether the morphism is an isomorphism.

EXAMPLES:

sage: Fq = GF(2)
sage: A.<T> = Fq[]
sage: K.<z6> = 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: morphism.is_isomorphism()
False

sage: zero_morphism = End(phi)(0)
sage: zero_morphism.is_isomorphism()
False

sage: identity_morphism = End(phi)(1)
sage: identity_morphism.is_isomorphism()
True

sage: frobenius_endomorphism = phi.frobenius_endomorphism()
sage: frobenius_endomorphism.is_isomorphism()
False

is_zero()#

Return True whether the morphism is the zero morphism.

EXAMPLES:

sage: Fq = GF(2)
sage: A.<T> = Fq[]
sage: K.<z6> = 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: morphism.is_zero()
False

sage: zero_morphism = End(phi)(0)
sage: zero_morphism.is_zero()
True

norm(ideal=True)#

Return the norm of this isogeny.

INPUT:

• ideal – a boolean (default: True); if True, return the norm as an ideal in the function ring of the Drinfeld modules; if False, return the norm as an element in this function ring (only relevant for endomorphisms)

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()
sage: f = phi.hom(t + 1)
sage: f.norm()
Principal ideal (T + 4) of Univariate Polynomial Ring in T over Finite Field of size 5


The norm of the Frobenius endomorphism is equal to the characteristic:

sage: F = phi.frobenius_endomorphism()
sage: F.norm()
Principal ideal (T^3 + 3*T + 3) of Univariate Polynomial Ring in T over Finite Field of size 5
sage: phi.characteristic()
T^3 + 3*T + 3


For $$a$$ in the underlying function ring, the norm of the endomorphism given by $$\phi_a$$ is $$a^r$$ where $$r$$ is the rank:

sage: g = phi.hom(T)
sage: g.norm()
Principal ideal (T^3) of Univariate Polynomial Ring in T over Finite Field of size 5

sage: h = phi.hom(T+1)
sage: h.norm()
Principal ideal (T^3 + 3*T^2 + 3*T + 1) of Univariate Polynomial Ring in T over Finite Field of size 5


For endomorphisms, the norm is not an ideal of $$A$$ but it makes sense as an actual element of $$A$$. We can get this element by passing in the argument ideal=False:

sage: phi.hom(2*T).norm(ideal=False)
3*T^3

sage: f.norm(ideal=False)
Traceback (most recent call last):
...
ValueError: norm is defined as an actual element only for endomorphisms

ore_polynomial()#

Return the Ore polynomial that defines the morphism.

EXAMPLES:

sage: Fq = GF(2)
sage: A.<T> = Fq[]
sage: K.<z6> = 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: ore_pol = morphism.ore_polynomial()
sage: ore_pol
t + z6^5 + z6^2 + 1

sage: ore_pol * phi(T) == psi(T) * ore_pol
True