From c4920401391d3881030410f80fad369a4e01039b Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 13 Dec 2025 10:15:58 +0100 Subject: [PATCH 1/2] distinguish between base and A_field --- src/sage/categories/drinfeld_modules.py | 35 ++++++------------- .../function_field/drinfeld_modules/action.py | 4 +-- .../drinfeld_modules/drinfeld_module.py | 19 +++------- src/sage/rings/ring_extension_element.pyx | 5 ++- 4 files changed, 22 insertions(+), 41 deletions(-) diff --git a/src/sage/categories/drinfeld_modules.py b/src/sage/categories/drinfeld_modules.py index 4f54127ba26..62c4b8cbecc 100644 --- a/src/sage/categories/drinfeld_modules.py +++ b/src/sage/categories/drinfeld_modules.py @@ -79,7 +79,7 @@ class DrinfeldModules(Category_over_base_ring): The base field is retrieved using the method :meth:`base`:: sage: C.base() - Finite Field in z of size 11^4 over its base + Finite Field in z of size 11^4 Equivalently, one can use :meth:`base_morphism` to retrieve the base morphism:: @@ -170,13 +170,6 @@ class DrinfeldModules(Category_over_base_ring): ... TypeError: base field must be a ring extension - Note that `C.base_morphism()` has codomain `K` while - the defining morphism of `C.base()` has codomain `K` viewed - as an `A`-field. Thus, they differ:: - - sage: C.base().defining_morphism() == C.base_morphism() - False - :: sage: base = Hom(A, A)(1) @@ -276,7 +269,7 @@ def __init__(self, base_morphism, name='τ'): i = A.coerce_map_from(Fq) Fq_to_K = self._base_morphism * i self._base_over_constants_field = base_field.over(Fq_to_K) - super().__init__(base=base_field.over(base_morphism)) + super().__init__(base=base_field) def _latex_(self): r""" @@ -377,7 +370,7 @@ def A_field(self): sage: C.A_field() Finite Field in z of size 5^12 over its base """ - return self.base() + return self.base().over(self._base_morphism) def base_morphism(self): r""" @@ -461,7 +454,7 @@ def constant_coefficient(self): sage: C = phi.category() sage: C.constant_coefficient() z^3 + 7*z^2 + 6*z + 10 - sage: C.constant_coefficient() == C.base()(T) + sage: C.constant_coefficient() == C.A_field()(T) True """ return self._constant_coefficient @@ -616,15 +609,8 @@ def A_field(self): def base(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:`A_field`. + Return the field over which this Drinfeld module + is defined. EXAMPLES:: @@ -634,14 +620,15 @@ def base(self): 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.base() - Finite Field in z12 of size 5^12 over its base + Finite Field in z12 of size 5^12 The base can be infinite:: sage: sigma = DrinfeldModule(A, [T, 1]) sage: sigma.base() - Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 over its base + Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 5^2 """ + # should we add a deprecation? return self.category().base() def base_morphism(self): @@ -758,8 +745,8 @@ def constant_coefficient(self): `\gamma(T)`:: sage: C = phi.category() - sage: base = C.base() - sage: base(T) == phi.constant_coefficient() + sage: F = C.A_field() + sage: F(T) == phi.constant_coefficient() True Naturally, two Drinfeld modules in the same category have the diff --git a/src/sage/rings/function_field/drinfeld_modules/action.py b/src/sage/rings/function_field/drinfeld_modules/action.py index 9bb50a00a18..953d8bc9d1c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/action.py +++ b/src/sage/rings/function_field/drinfeld_modules/action.py @@ -59,7 +59,7 @@ class DrinfeldModuleAction(Action): 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 + Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by T |--> τ^3 + z The action on elements is computed as follows:: @@ -174,7 +174,7 @@ def _repr_(self) -> str: 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 |--> τ^3 + z + Action on Finite Field in z of size 11^2 induced by Drinfeld module defined by T |--> τ^3 + z """ return f'Action on {self._base} induced by ' \ f'{self._drinfeld_module}' diff --git a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py index efa6b58eab5..51df74c0afc 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -101,7 +101,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: psi Drinfeld module defined by T |--> (T + 1)*τ + T sage: psi.base() - Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 7^2 over its base + Fraction Field of Univariate Polynomial Ring in T over Finite Field in z2 of size 7^2 .. NOTE:: @@ -213,7 +213,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): :meth:`base`:: sage: phi.base() - Finite Field in z of size 3^12 over its base + Finite Field in z of size 3^12 The base morphism is retrieved using :meth:`base_morphism`:: @@ -223,14 +223,6 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 Defn: T |--> z - Note that the base field is *not* the field `K`. Rather, it is a - ring extension - (see :class:`sage.rings.ring_extension.RingExtension`) whose - underlying ring is `K` and whose base is the base morphism:: - - sage: phi.base() is K - False - .. RUBRIC:: Getters One can retrieve basic properties:: @@ -403,7 +395,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: action = phi.action() sage: action - Action on Finite Field in z of size 3^12 over its base + Action on Finite Field in z of size 3^12 induced by Drinfeld module defined by T |--> τ^2 + τ + z The action on elements is computed by calling the action object:: @@ -711,7 +703,7 @@ def __call__(self, a): :: sage: a = A.random_element(5) - sage: phi(a)[0] == phi.category().base()(a) + sage: phi(a)[0] == phi.A_field()(a) True """ return self._morphism(a) @@ -859,7 +851,7 @@ def action(self): 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 + Action on Finite Field in z12 of size 5^12 induced by Drinfeld module defined by T |--> z12^5*τ^2 + z12^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 @@ -1446,7 +1438,6 @@ def is_isomorphic(self, other, absolutely=False) -> bool: if absolutely: return True else: - ue = ue.backend(force=True) try: _ = ue.nth_root(e) except ValueError: diff --git a/src/sage/rings/ring_extension_element.pyx b/src/sage/rings/ring_extension_element.pyx index c96172db27c..e49aee4fdb1 100644 --- a/src/sage/rings/ring_extension_element.pyx +++ b/src/sage/rings/ring_extension_element.pyx @@ -167,7 +167,10 @@ cdef class RingExtensionElement(CommutativeAlgebraElement): """ if (self._parent)._import_methods: output = self._backend(*to_backend(args), **to_backend(kwargs)) - return from_backend(output, self._parent) + if args: + return from_backend(output, args[0].parent()) + else: + return from_backend(output, self._parent) return TypeError("this element is not callable") def __dir__(self): From 9c8175e155f3196f7d8eb10eb01fb1375c3e54be Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 13 Dec 2025 22:16:57 +0100 Subject: [PATCH 2/2] Carlitz factorial and Bernoulli-Carlitz numbers --- src/sage/rings/function_field/all.py | 2 + .../drinfeld_modules/carlitz_module.py | 105 ++++++++++++++++++ 2 files changed, 107 insertions(+) diff --git a/src/sage/rings/function_field/all.py b/src/sage/rings/function_field/all.py index 459ad867b0d..4f2e8edde11 100644 --- a/src/sage/rings/function_field/all.py +++ b/src/sage/rings/function_field/all.py @@ -6,3 +6,5 @@ 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") +lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "carlitz_factorial") +lazy_import("sage.rings.function_field.drinfeld_modules.carlitz_module", "carlitz_bernoulli") diff --git a/src/sage/rings/function_field/drinfeld_modules/carlitz_module.py b/src/sage/rings/function_field/drinfeld_modules/carlitz_module.py index efac57955f2..868cc2ceeed 100644 --- a/src/sage/rings/function_field/drinfeld_modules/carlitz_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/carlitz_module.py @@ -16,6 +16,8 @@ # http://www.gnu.org/licenses/ # ***************************************************************************** +from sage.misc.cachefunc import cached_function + from sage.structure.parent import Parent from sage.structure.element import Element from sage.categories.finite_fields import FiniteFields @@ -114,6 +116,8 @@ def carlitz_exponential(A, prec=+Infinity, name='z'): INPUT: + - ``A`` -- a polynomial ring over a finite field + - ``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 @@ -163,6 +167,8 @@ def carlitz_logarithm(A, prec=+Infinity, name='z'): INPUT: + - ``A`` -- a polynomial ring over a finite field + - ``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 @@ -195,3 +201,102 @@ def carlitz_logarithm(A, prec=+Infinity, name='z'): """ C = CarlitzModule(A) return C.logarithm(prec, name) + + +def carlitz_factorial(A, n): + r""" + Return the Carlitz factorial attached the ring `A`, + evaluated at `n`. + + INPUT: + + - ``A`` -- a polynomial ring over a finite field + + - ``n`` -- an integer + + EXAMPLES:: + + sage: A. = GF(3)[] + sage: carlitz_factorial(A, 0) + 1 + sage: carlitz_factorial(A, 1) + T^3 + 2*T + sage: carlitz_factorial(A, 2) + T^6 + T^4 + T^2 + sage: carlitz_factorial(A, 3) + T^18 + 2*T^12 + 2*T^10 + T^4 + + TESTS:: + + sage: carlitz_factorial(ZZ, 10) + Traceback (most recent call last): + ... + TypeError: the function ring must be defined over a finite 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') + T = A.gen() + q = A.base_ring().cardinality() + ans = A.one() + D = A.one() + j = 1 + while n > 0: + n, c = n.quo_rem(q) + D = D**q * (T**(q**j) - T) + ans *= D**c + j += 1 + return ans + + +carlitz_series = {} + +def carlitz_bernoulli(A, n): + r""" + Return the `n`-th Bernoulli-Carlitz number attached + to the ring `A`. + + - ``A`` -- a polynomial ring over a finite field + + - ``n`` -- an integer + + EXAMPLES:: + + sage: A. = GF(3)[] + sage: carlitz_bernoulli(A, 2) + 2*T^3 + T + sage: carlitz_bernoulli(A, 4) + T^15 + T^13 + T^11 + 2*T^7 + 2*T^5 + 2*T^3 + + The Bernoulli-Carlitz numbers vanish when `n` is not a + multiple of `q-1` where `q` is the cardinality of the + base field:: + + sage: carlitz_bernoulli(A, 1) + 0 + sage: carlitz_bernoulli(A, 3) + 0 + + TESTS:: + + sage: carlitz_bernoulli(ZZ, 10) + Traceback (most recent call last): + ... + TypeError: the function ring must be defined over a finite field + + :: + + sage: B. = GF(3)[] + sage: carlitz_bernoulli(B, 2) + 2*X^3 + X + """ + 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') + q = A.base_ring().cardinality() + if q not in carlitz_series: + e = carlitz_exponential(A) + carlitz_series[q] = e.parent().gen() / e + coeff = carlitz_series[q][n] + coeff = A.fraction_field()(coeff) + return A(coeff * carlitz_factorial(A, n))