Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions src/sage/categories/drinfeld_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -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``.

Expand All @@ -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::
Expand All @@ -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.<t> = 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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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::
Expand Down Expand Up @@ -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()
3 changes: 3 additions & 0 deletions src/sage/rings/function_field/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
from sage.misc.lazy_import import lazy_import

lazy_import("sage.rings.function_field.drinfeld_modules.drinfeld_module", "DrinfeldModule")
lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "CarlitzModule")
lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "carlitz_exponential")
lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "carlitz_logarithm")
6 changes: 3 additions & 3 deletions src/sage/rings/function_field/drinfeld_modules/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -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::

Expand Down Expand Up @@ -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{{{{ }}' \
Expand All @@ -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}'
Expand Down
197 changes: 197 additions & 0 deletions src/sage/rings/function_field/drinfeld_modules/carlitz_module.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
r"""
Carlitz module

AUTHORS:

- Xavier Caruso (2025-07): initial version
"""

# *****************************************************************************
# Copyright (C) 2025 Xavier Caruso <[email protected]>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# http://www.gnu.org/licenses/
# *****************************************************************************

from sage.structure.parent import Parent
from sage.structure.element import Element
from sage.categories.finite_fields import FiniteFields

from sage.rings.infinity import Infinity

from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic
from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule


def CarlitzModule(A, base=None):
r"""
Return the Carlitz module over `A`.

INPUT:

- ``A`` -- a polynomial ring over a finite field

- ``base`` -- a field, an element in a field or a
string (default: the fraction field of ``A``)

EXAMPLES::

sage: Fq = GF(7)
sage: A.<T> = Fq[]
sage: CarlitzModule(A)
Drinfeld module defined by T |--> τ + T

We can specify a different base.
This is interesting for instance for having two different variable
names::

sage: R.<z> = Fq[]
sage: CarlitzModule(A, R)
Drinfeld module defined by T |--> τ + z

One can even use the following shortcut, which avoids the
construction of `R`::

sage: CarlitzModule(A, 'z')
Drinfeld module defined by T |--> τ + z

Using a similar syntax, we can construct the reduction of the
Carlitz module modulo primes::

sage: F.<a> = Fq.extension(z^2 + 1)
sage: CarlitzModule(A, F)
Drinfeld module defined by T |--> τ + a

It is also possible to pass in any element in the base field
(in this case, the result might not be strictly speaking the
Carlitz module, but it is always a Drinfeld module of rank 1)::

sage: CarlitzModule(A, z^2)
Drinfeld module defined by T |--> τ + z^2

TESTS::

sage: CarlitzModule(Fq)
Traceback (most recent call last):
...
TypeError: the function ring must be defined over a finite field

::

sage: S.<x,y> = QQ[]
sage: CarlitzModule(A, S)
Traceback (most recent call last):
...
ValueError: function ring base must coerce into base field
"""
if (not isinstance(A, PolynomialRing_generic)
or A.base_ring() not in FiniteFields()):
raise TypeError('the function ring must be defined over a finite field')
if base is None:
K = A.fraction_field()
z = K.gen()
elif isinstance(base, Parent):
if base.has_coerce_map_from(A):
z = base(A.gen())
else:
z = base.gen()
elif isinstance(base, Element):
z = base
elif isinstance(base, str):
K = A.base_ring()[base]
z = K.gen()
else:
raise ValueError("cannot construct a Carlitz module from the given data")
return DrinfeldModule(A, [z, 1])


def carlitz_exponential(A, prec=+Infinity, name='z'):
r"""
Return the Carlitz exponential attached the ring `A`.

INPUT:

- ``prec`` -- an integer or ``Infinity`` (default: ``Infinity``);
the precision at which the series is returned; if ``Infinity``,
a lazy power series in returned, else, a classical power series
is returned.

- ``name`` -- string (default: ``'z'``); the name of the
generator of the lazy power series ring

EXAMPLES::

sage: A.<T> = GF(2)[]

When ``prec`` is ``Infinity`` (which is the default),
the exponential is returned as a lazy power series, meaning
that any of its coefficients can be computed on demands::

sage: exp = carlitz_exponential(A)
sage: exp
z + ((1/(T^2+T))*z^2) + ((1/(T^8+T^6+T^5+T^3))*z^4) + O(z^8)
sage: exp[2^4]
1/(T^64 + T^56 + T^52 + ... + T^27 + T^23 + T^15)
sage: exp[2^5]
1/(T^160 + T^144 + T^136 + ... + T^55 + T^47 + T^31)

On the contrary, when ``prec`` is a finite number, all the
required coefficients are computed at once::

sage: carlitz_exponential(A, prec=10)
z + (1/(T^2 + T))*z^2 + (1/(T^8 + T^6 + T^5 + T^3))*z^4 + (1/(T^24 + T^20 + T^18 + T^17 + T^14 + T^13 + T^11 + T^7))*z^8 + O(z^10)

We check that the Carlitz exponential is the compositional inverse
of the Carlitz logarithm::

sage: log = carlitz_logarithm(A)
sage: exp(log)
z + O(z^8)
sage: log(exp)
z + O(z^8)
"""
C = CarlitzModule(A)
return C.exponential(prec, name)


def carlitz_logarithm(A, prec=+Infinity, name='z'):
r"""
Return the Carlitz exponential attached the ring `A`.

INPUT:

- ``prec`` -- an integer or ``Infinity`` (default: ``Infinity``);
the precision at which the series is returned; if ``Infinity``,
a lazy power series in returned, else, a classical power series
is returned.

- ``name`` -- string (default: ``'z'``); the name of the
generator of the lazy power series ring

EXAMPLES::

sage: A.<T> = GF(2)[]

When ``prec`` is ``Infinity`` (which is the default),
the exponential is returned as a lazy power series, meaning
that any of its coefficients can be computed on demands::

sage: log = carlitz_logarithm(A)
sage: log
z + ((1/(T^2+T))*z^2) + ((1/(T^6+T^5+T^3+T^2))*z^4) + O(z^8)
sage: log[2^4]
1/(T^30 + T^29 + T^27 + ... + T^7 + T^5 + T^4)
sage: log[2^5]
1/(T^62 + T^61 + T^59 + ... + T^8 + T^6 + T^5)

On the contrary, when ``prec`` is a finite number, all the
required coefficients are computed at once::

sage: carlitz_logarithm(A, prec=10)
z + (1/(T^2 + T))*z^2 + (1/(T^6 + T^5 + T^3 + T^2))*z^4 + (1/(T^14 + T^13 + T^11 + T^10 + T^7 + T^6 + T^4 + T^3))*z^8 + O(z^10)
"""
C = CarlitzModule(A)
return C.logarithm(prec, name)
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ class DrinfeldModule_charzero(DrinfeldModule):
sage: A.<T> = GF(3)[]
sage: phi = DrinfeldModule(A, [T, 1])
sage: phi
Drinfeld module defined by T |--> t + T
Drinfeld module defined by T |--> τ + T

::

Expand Down Expand Up @@ -107,7 +107,7 @@ class DrinfeldModule_charzero(DrinfeldModule):
sage: L.<s> = 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::
Expand All @@ -116,7 +116,7 @@ class DrinfeldModule_charzero(DrinfeldModule):
sage: K.<z> = 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):
Expand Down Expand Up @@ -448,7 +448,7 @@ class DrinfeldModule_rational(DrinfeldModule_charzero):
sage: Fq = GF(q)
sage: A.<T> = Fq[]
sage: C = DrinfeldModule(A, [T, 1]); C
Drinfeld module defined by T |--> t + T
Drinfeld module defined by T |--> τ + T
sage: type(C)
<class 'sage.rings.function_field.drinfeld_modules.charzero_drinfeld_module.DrinfeldModule_rational_with_category'>
"""
Expand Down Expand Up @@ -556,7 +556,7 @@ def class_polynomial(self):
sage: Fq = GF(q)
sage: A.<T> = 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

Expand Down
Loading
Loading