Skip to content

Commit e55feb2

Browse files
author
Release Manager
committed
sagemathgh-40433: Constructor for the Carlitz module We add a constructor for instantiating easily the Carlitz module. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [x] I have updated the documentation and checked the documentation preview. ### ⌛ Dependencies sagemath#40430, sagemath#40436 URL: sagemath#40433 Reported by: Xavier Caruso Reviewer(s): Antoine Leudière, Xavier Caruso
2 parents 99ae29d + b201d77 commit e55feb2

File tree

3 files changed

+203
-2
lines changed

3 files changed

+203
-2
lines changed

src/sage/rings/function_field/all.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@
33
from sage.misc.lazy_import import lazy_import
44

55
lazy_import("sage.rings.function_field.drinfeld_modules.drinfeld_module", "DrinfeldModule")
6+
lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "CarlitzModule")
7+
lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "carlitz_exponential")
8+
lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "carlitz_logarithm")
Lines changed: 197 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,197 @@
1+
r"""
2+
Carlitz module
3+
4+
AUTHORS:
5+
6+
- Xavier Caruso (2025-07): initial version
7+
"""
8+
9+
# *****************************************************************************
10+
# Copyright (C) 2025 Xavier Caruso <[email protected]>
11+
#
12+
# This program is free software: you can redistribute it and/or modify
13+
# it under the terms of the GNU General Public License as published by
14+
# the Free Software Foundation, either version 2 of the License, or
15+
# (at your option) any later version.
16+
# http://www.gnu.org/licenses/
17+
# *****************************************************************************
18+
19+
from sage.structure.parent import Parent
20+
from sage.structure.element import Element
21+
from sage.categories.finite_fields import FiniteFields
22+
23+
from sage.rings.infinity import Infinity
24+
25+
from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic
26+
from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule
27+
28+
29+
def CarlitzModule(A, base=None):
30+
r"""
31+
Return the Carlitz module over `A`.
32+
33+
INPUT:
34+
35+
- ``A`` -- a polynomial ring over a finite field
36+
37+
- ``base`` -- a field, an element in a field or a
38+
string (default: the fraction field of ``A``)
39+
40+
EXAMPLES::
41+
42+
sage: Fq = GF(7)
43+
sage: A.<T> = Fq[]
44+
sage: CarlitzModule(A)
45+
Drinfeld module defined by T |--> τ + T
46+
47+
We can specify a different base.
48+
This is interesting for instance for having two different variable
49+
names::
50+
51+
sage: R.<z> = Fq[]
52+
sage: CarlitzModule(A, R)
53+
Drinfeld module defined by T |--> τ + z
54+
55+
One can even use the following shortcut, which avoids the
56+
construction of `R`::
57+
58+
sage: CarlitzModule(A, 'z')
59+
Drinfeld module defined by T |--> τ + z
60+
61+
Using a similar syntax, we can construct the reduction of the
62+
Carlitz module modulo primes::
63+
64+
sage: F.<a> = Fq.extension(z^2 + 1)
65+
sage: CarlitzModule(A, F)
66+
Drinfeld module defined by T |--> τ + a
67+
68+
It is also possible to pass in any element in the base field
69+
(in this case, the result might not be strictly speaking the
70+
Carlitz module, but it is always a Drinfeld module of rank 1)::
71+
72+
sage: CarlitzModule(A, z^2)
73+
Drinfeld module defined by T |--> τ + z^2
74+
75+
TESTS::
76+
77+
sage: CarlitzModule(Fq)
78+
Traceback (most recent call last):
79+
...
80+
TypeError: the function ring must be defined over a finite field
81+
82+
::
83+
84+
sage: S.<x,y> = QQ[]
85+
sage: CarlitzModule(A, S)
86+
Traceback (most recent call last):
87+
...
88+
ValueError: function ring base must coerce into base field
89+
"""
90+
if (not isinstance(A, PolynomialRing_generic)
91+
or A.base_ring() not in FiniteFields()):
92+
raise TypeError('the function ring must be defined over a finite field')
93+
if base is None:
94+
K = A.fraction_field()
95+
z = K.gen()
96+
elif isinstance(base, Parent):
97+
if base.has_coerce_map_from(A):
98+
z = base(A.gen())
99+
else:
100+
z = base.gen()
101+
elif isinstance(base, Element):
102+
z = base
103+
elif isinstance(base, str):
104+
K = A.base_ring()[base]
105+
z = K.gen()
106+
else:
107+
raise ValueError("cannot construct a Carlitz module from the given data")
108+
return DrinfeldModule(A, [z, 1])
109+
110+
111+
def carlitz_exponential(A, prec=+Infinity, name='z'):
112+
r"""
113+
Return the Carlitz exponential attached the ring `A`.
114+
115+
INPUT:
116+
117+
- ``prec`` -- an integer or ``Infinity`` (default: ``Infinity``);
118+
the precision at which the series is returned; if ``Infinity``,
119+
a lazy power series in returned, else, a classical power series
120+
is returned.
121+
122+
- ``name`` -- string (default: ``'z'``); the name of the
123+
generator of the lazy power series ring
124+
125+
EXAMPLES::
126+
127+
sage: A.<T> = GF(2)[]
128+
129+
When ``prec`` is ``Infinity`` (which is the default),
130+
the exponential is returned as a lazy power series, meaning
131+
that any of its coefficients can be computed on demands::
132+
133+
sage: exp = carlitz_exponential(A)
134+
sage: exp
135+
z + ((1/(T^2+T))*z^2) + ((1/(T^8+T^6+T^5+T^3))*z^4) + O(z^8)
136+
sage: exp[2^4]
137+
1/(T^64 + T^56 + T^52 + ... + T^27 + T^23 + T^15)
138+
sage: exp[2^5]
139+
1/(T^160 + T^144 + T^136 + ... + T^55 + T^47 + T^31)
140+
141+
On the contrary, when ``prec`` is a finite number, all the
142+
required coefficients are computed at once::
143+
144+
sage: carlitz_exponential(A, prec=10)
145+
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)
146+
147+
We check that the Carlitz exponential is the compositional inverse
148+
of the Carlitz logarithm::
149+
150+
sage: log = carlitz_logarithm(A)
151+
sage: exp(log)
152+
z + O(z^8)
153+
sage: log(exp)
154+
z + O(z^8)
155+
"""
156+
C = CarlitzModule(A)
157+
return C.exponential(prec, name)
158+
159+
160+
def carlitz_logarithm(A, prec=+Infinity, name='z'):
161+
r"""
162+
Return the Carlitz exponential attached the ring `A`.
163+
164+
INPUT:
165+
166+
- ``prec`` -- an integer or ``Infinity`` (default: ``Infinity``);
167+
the precision at which the series is returned; if ``Infinity``,
168+
a lazy power series in returned, else, a classical power series
169+
is returned.
170+
171+
- ``name`` -- string (default: ``'z'``); the name of the
172+
generator of the lazy power series ring
173+
174+
EXAMPLES::
175+
176+
sage: A.<T> = GF(2)[]
177+
178+
When ``prec`` is ``Infinity`` (which is the default),
179+
the exponential is returned as a lazy power series, meaning
180+
that any of its coefficients can be computed on demands::
181+
182+
sage: log = carlitz_logarithm(A)
183+
sage: log
184+
z + ((1/(T^2+T))*z^2) + ((1/(T^6+T^5+T^3+T^2))*z^4) + O(z^8)
185+
sage: log[2^4]
186+
1/(T^30 + T^29 + T^27 + ... + T^7 + T^5 + T^4)
187+
sage: log[2^5]
188+
1/(T^62 + T^61 + T^59 + ... + T^8 + T^6 + T^5)
189+
190+
On the contrary, when ``prec`` is a finite number, all the
191+
required coefficients are computed at once::
192+
193+
sage: carlitz_logarithm(A, prec=10)
194+
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)
195+
"""
196+
C = CarlitzModule(A)
197+
return C.logarithm(prec, name)

src/sage/rings/function_field/drinfeld_modules/finite_drinfeld_module.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
from sage.matrix.constructor import Matrix
2929
from sage.matrix.matrix_space import MatrixSpace
3030
from sage.matrix.special import companion_matrix
31+
from sage.misc.functional import log
3132
from sage.misc.misc_c import prod
3233
from sage.modules.free_module_element import vector
3334
from sage.rings.function_field.drinfeld_modules.drinfeld_module import DrinfeldModule
@@ -157,7 +158,7 @@ def __init__(self, gen, category):
157158
# added one to ensure that DrinfeldModule_finite would always
158159
# have _frobenius_norm and _frobenius_trace attributes.
159160
super().__init__(gen, category)
160-
self._base_degree_over_constants = self.base_over_constants_field().degree(self._Fq)
161+
self._base_degree_over_constants = log(self._base.cardinality(), self._Fq.cardinality())
161162
self._frobenius_norm = None
162163
self._frobenius_trace = None
163164
self._frobenius_charpoly = None
@@ -253,7 +254,7 @@ def frobenius_endomorphism(self):
253254
True
254255
"""
255256
t = self.ore_polring().gen()
256-
deg = self.base_over_constants_field().degree_over()
257+
deg = self._base_degree_over_constants
257258
return self._Hom_(self, category=self.category())(t**deg)
258259

259260
def frobenius_charpoly(self, var='X', algorithm=None):

0 commit comments

Comments
 (0)