Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
78 changes: 65 additions & 13 deletions src/sage/categories/drinfeld_modules.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# sage_setup: distribution = sagemath-categories
# sage.doctest: needs sage.rings.finite_rings
r"""
Expand Down Expand Up @@ -123,7 +124,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 +136,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 +208,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 +217,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 +228,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 @@ -356,6 +357,30 @@ def Endsets(self):
"""
return Homsets().Endsets()

def A_field(self):
r"""
Return the underlying `A`-field of this category,
viewed as an algebra over the function ring `A`.

This is an instance of the class
:class:`sage.rings.ring_extension.RingExtension`.

NOTE::

This method has the same behavior as :meth:`base`.

EXAMPLES::

sage: Fq = GF(25)
sage: A.<T> = Fq[]
sage: K.<z> = Fq.extension(6)
sage: phi = DrinfeldModule(A, [z, z^3, z^5])
sage: C = phi.category()
sage: C.A_field()
Finite Field in z of size 5^12 over its base
"""
return self.base()

def base_morphism(self):
r"""
Return the base morphism of the category.
Expand Down Expand Up @@ -483,7 +508,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 @@ -510,7 +535,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 @@ -568,14 +593,41 @@ def super_categories(self):

class ParentMethods:

def A_field(self):
r"""
Return the underlying `A`-field of this Drinfeld module,
viewed as an algebra over the function ring `A`.

This is an instance of the class
:class:`sage.rings.ring_extension.RingExtension`.

NOTE::

This method has the same behavior as :meth:`base`.

EXAMPLES::

sage: Fq = GF(25)
sage: A.<T> = Fq[]
sage: K.<z> = Fq.extension(6)
sage: phi = DrinfeldModule(A, [z, z^3, z^5])
sage: phi.A_field()
Finite Field in z of size 5^12 over its base
"""
return self.category().A_field()

def base(self):
r"""
Return the base field of this Drinfeld module, viewed as
an algebra over the function ring.
Return the underlying `A`-field of this Drinfeld module,
viewed as an algebra over the function ring `A`.

This is an instance of the class
:class:`sage.rings.ring_extension.RingExtension`.

NOTE::

This method has the same behavior as :meth:`A_field`.

EXAMPLES::

sage: Fq = GF(25)
Expand Down Expand Up @@ -718,7 +770,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 @@ -744,7 +796,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 @@ -773,8 +825,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", "CarlitzExponential")
lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "CarlitzLogarithm")
7 changes: 4 additions & 3 deletions src/sage/rings/function_field/drinfeld_modules/action.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# -*- coding: utf-8 -*-
# sage.doctest: needs sage.rings.finite_rings
r"""
The module action induced by a Drinfeld module
Expand Down Expand Up @@ -60,7 +61,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 +155,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 +175,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
187 changes: 187 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,187 @@
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 or an element in a field
(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: K = Frac(R)
sage: CarlitzModule(A, K)
Drinfeld module defined by T |--> τ + z

Using a similar syntax, we can construct the reduction over 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
else:
raise ValueError("cannot construct a Carlitz module from the given data")
return DrinfeldModule(A, [z, 1])

def CarlitzExponential(A, prec=+Infinity, name='z'):

Check failure on line 102 in src/sage/rings/function_field/drinfeld_modules/carlitz_module.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (E302)

src/sage/rings/function_field/drinfeld_modules/carlitz_module.py:102:1: E302 Expected 2 blank lines, found 1
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 = CarlitzExponential(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: CarlitzExponential(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 = CarlitzLogarithm(A)
sage: exp(log)
z + O(z^8)
sage: log(exp)
z + O(z^8)
"""
C = CarlitzModule(A)
return C.exponential(prec, name)

def CarlitzLogarithm(A, prec=+Infinity, name='z'):

Check failure on line 150 in src/sage/rings/function_field/drinfeld_modules/carlitz_module.py

View workflow job for this annotation

GitHub Actions / Lint

Ruff (E302)

src/sage/rings/function_field/drinfeld_modules/carlitz_module.py:150:1: E302 Expected 2 blank lines, found 1
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 = CarlitzLogarithm(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: CarlitzLogarithm(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)
Loading
Loading