|
26 | 26 |
|
27 | 27 | from sage.categories.drinfeld_modules import DrinfeldModules
|
28 | 28 | from sage.categories.homset import Hom
|
| 29 | +from sage.misc.cachefunc import cached_method |
29 | 30 | from sage.misc.latex import latex
|
30 | 31 | from sage.misc.latex import latex_variable_name
|
| 32 | +from sage.misc.lazy_import import lazy_import |
31 | 33 | from sage.misc.lazy_string import _LazyString
|
32 | 34 | from sage.rings.integer import Integer
|
| 35 | +from sage.rings.integer_ring import ZZ |
33 | 36 | from sage.rings.polynomial.ore_polynomial_element import OrePolynomial
|
34 | 37 | from sage.rings.polynomial.polynomial_ring import PolynomialRing_general
|
35 | 38 | from sage.rings.ring_extension import RingExtension_generic
|
|
38 | 41 | from sage.structure.sequence import Sequence
|
39 | 42 | from sage.structure.unique_representation import UniqueRepresentation
|
40 | 43 |
|
| 44 | +lazy_import('sage.rings.lazy_series_ring', 'LazyPowerSeriesRing') |
| 45 | + |
41 | 46 |
|
42 | 47 | class DrinfeldModule(Parent, UniqueRepresentation):
|
43 | 48 | r"""
|
@@ -958,6 +963,134 @@ def coefficients(self, sparse=True):
|
958 | 963 | """
|
959 | 964 | return self._gen.coefficients(sparse=sparse)
|
960 | 965 |
|
| 966 | + @cached_method |
| 967 | + def _compute_coefficient_exp(self, k): |
| 968 | + r""" |
| 969 | + Return the `q^k`-th coefficient of the exponential of this Drinfeld module. |
| 970 | +
|
| 971 | + INPUT: |
| 972 | +
|
| 973 | + - ``k`` (integer) -- the index of the coefficient |
| 974 | +
|
| 975 | + TESTS:: |
| 976 | +
|
| 977 | + sage: A = GF(2)['T'] |
| 978 | + sage: K.<T> = Frac(A) |
| 979 | + sage: phi = DrinfeldModule(A, [T, 1]) |
| 980 | + sage: q = A.base_ring().cardinality() |
| 981 | + sage: phi._compute_coefficient_exp(0) |
| 982 | + 1 |
| 983 | + sage: phi._compute_coefficient_exp(1) |
| 984 | + 1/(T^2 + T) |
| 985 | + sage: phi._compute_coefficient_exp(2) |
| 986 | + 1/(T^8 + T^6 + T^5 + T^3) |
| 987 | + sage: phi._compute_coefficient_exp(3) |
| 988 | + 1/(T^24 + T^20 + T^18 + T^17 + T^14 + T^13 + T^11 + T^7) |
| 989 | + """ |
| 990 | + k = ZZ(k) |
| 991 | + if k.is_zero(): |
| 992 | + return self._base.one() |
| 993 | + q = self._Fq.cardinality() |
| 994 | + c = self._base.zero() |
| 995 | + for i in range(k): |
| 996 | + j = k - i |
| 997 | + c += self._compute_coefficient_exp(i)*self._compute_coefficient_log(j)**(q**i) |
| 998 | + return -c |
| 999 | + |
| 1000 | + def exponential(self, name='z'): |
| 1001 | + r""" |
| 1002 | + Return the exponential of this Drinfeld module. |
| 1003 | +
|
| 1004 | + Note that the exponential is only defined when the |
| 1005 | + `\mathbb{F}_q[T]`-characteristic is zero. |
| 1006 | +
|
| 1007 | + INPUT: |
| 1008 | +
|
| 1009 | + - ``name`` (string, default: ``'z'``) -- the name of the |
| 1010 | + generator of the lazy power series ring. |
| 1011 | +
|
| 1012 | + OUTPUT: |
| 1013 | +
|
| 1014 | + A lazy power series over the base field. |
| 1015 | +
|
| 1016 | + EXAMPLES:: |
| 1017 | +
|
| 1018 | + sage: A = GF(2)['T'] |
| 1019 | + sage: K.<T> = Frac(A) |
| 1020 | + sage: phi = DrinfeldModule(A, [T, 1]) |
| 1021 | + sage: q = A.base_ring().cardinality() |
| 1022 | + sage: exp = phi.exponential(); exp |
| 1023 | + z + ((1/(T^2+T))*z^2) + ((1/(T^8+T^6+T^5+T^3))*z^4) + O(z^8) |
| 1024 | +
|
| 1025 | + The exponential is returned as a lazy power series, meaning that |
| 1026 | + any of its coefficients can be computed on demands:: |
| 1027 | +
|
| 1028 | + sage: exp[2^4] |
| 1029 | + 1/(T^64 + T^56 + T^52 + ... + T^27 + T^23 + T^15) |
| 1030 | + sage: exp[2^5] |
| 1031 | + 1/(T^160 + T^144 + T^136 + ... + T^55 + T^47 + T^31) |
| 1032 | +
|
| 1033 | + Example in higher rank:: |
| 1034 | +
|
| 1035 | + sage: A = GF(5)['T'] |
| 1036 | + sage: K.<T> = Frac(A) |
| 1037 | + sage: phi = DrinfeldModule(A, [T, T^2, T + T^2 + T^4, 1]) |
| 1038 | + sage: exp = phi.exponential(); exp |
| 1039 | + z + ((T/(T^4+4))*z^5) + O(z^8) |
| 1040 | +
|
| 1041 | + The exponential is the compositional inverse of the logarithm |
| 1042 | + (see :meth:`logarithm`):: |
| 1043 | +
|
| 1044 | + sage: log = phi.logarithm(); log |
| 1045 | + z + ((4*T/(T^4+4))*z^5) + O(z^8) |
| 1046 | + sage: exp.compose(log) |
| 1047 | + z + O(z^8) |
| 1048 | + sage: log.compose(exp) |
| 1049 | + z + O(z^8) |
| 1050 | +
|
| 1051 | + :: |
| 1052 | +
|
| 1053 | + sage: Fq.<w> = GF(3) |
| 1054 | + sage: A = Fq['T'] |
| 1055 | + sage: phi = DrinfeldModule(A, [w, 1]) |
| 1056 | + sage: phi.exponential() |
| 1057 | + Traceback (most recent call last): |
| 1058 | + ... |
| 1059 | + ValueError: characteristic must be zero (=T + 2) |
| 1060 | +
|
| 1061 | + TESTS:: |
| 1062 | +
|
| 1063 | + sage: A = GF(2)['T'] |
| 1064 | + sage: K.<T> = Frac(A) |
| 1065 | + sage: phi = DrinfeldModule(A, [T, 1]) |
| 1066 | + sage: exp = phi.exponential() |
| 1067 | + sage: exp[2] == 1/(T**q - T) # expected value |
| 1068 | + True |
| 1069 | + sage: exp[2^2] == 1/((T**(q**2) - T)*(T**q - T)**q) # expected value |
| 1070 | + True |
| 1071 | + sage: exp[2^3] == 1/((T**(q**3) - T)*(T**(q**2) - T)**q*(T**q - T)**(q**2)) # expected value |
| 1072 | + True |
| 1073 | +
|
| 1074 | + REFERENCE: |
| 1075 | +
|
| 1076 | + See section 4.6 of [Gos1998]_ for the definition of the |
| 1077 | + exponential. |
| 1078 | + """ |
| 1079 | + if self.category()._characteristic: |
| 1080 | + raise ValueError(f"characteristic must be zero (={self.characteristic()})") |
| 1081 | + L = LazyPowerSeriesRing(self._base, name) |
| 1082 | + zero = self._base.zero() |
| 1083 | + q = self._Fq.cardinality() |
| 1084 | + |
| 1085 | + def coeff_exp(k): |
| 1086 | + # Return the k-th coefficient of the exponential. |
| 1087 | + k = ZZ(k) |
| 1088 | + if k.is_power_of(q): |
| 1089 | + return self._compute_coefficient_exp(k.log(q)) |
| 1090 | + else: |
| 1091 | + return zero |
| 1092 | + return L(coeff_exp, valuation=1) |
| 1093 | + |
961 | 1094 | def gen(self):
|
962 | 1095 | r"""
|
963 | 1096 | Return the generator of the Drinfeld module.
|
@@ -1103,6 +1236,120 @@ def j_invariant(self):
|
1103 | 1236 | q = self._Fq.order()
|
1104 | 1237 | return (g**(q+1)) / delta
|
1105 | 1238 |
|
| 1239 | + @cached_method |
| 1240 | + def _compute_coefficient_log(self, k): |
| 1241 | + r""" |
| 1242 | + Return the `q^k`-th coefficient of the logarithm of this Drinfeld module. |
| 1243 | +
|
| 1244 | + TESTS:: |
| 1245 | +
|
| 1246 | + sage: A = GF(2)['T'] |
| 1247 | + sage: K.<T> = Frac(A) |
| 1248 | + sage: phi = DrinfeldModule(A, [T, 1]) |
| 1249 | + sage: q = A.base_ring().cardinality() |
| 1250 | + sage: phi._compute_coefficient_log(0) |
| 1251 | + 1 |
| 1252 | + sage: phi._compute_coefficient_log(1) |
| 1253 | + 1/(T^2 + T) |
| 1254 | + sage: phi._compute_coefficient_log(2) |
| 1255 | + 1/(T^6 + T^5 + T^3 + T^2) |
| 1256 | + sage: phi._compute_coefficient_log(3) |
| 1257 | + 1/(T^14 + T^13 + T^11 + T^10 + T^7 + T^6 + T^4 + T^3) |
| 1258 | + """ |
| 1259 | + k = ZZ(k) |
| 1260 | + if k.is_zero(): |
| 1261 | + return self._base.one() |
| 1262 | + r = self._gen.degree() |
| 1263 | + T = self._gen[0] |
| 1264 | + q = self._Fq.cardinality() |
| 1265 | + c = self._base.zero() |
| 1266 | + for i in range(k): |
| 1267 | + j = k - i |
| 1268 | + if j < r + 1: |
| 1269 | + c += self._compute_coefficient_log(i)*self._gen[j]**(q**i) |
| 1270 | + return c/(T - T**(q**k)) |
| 1271 | + |
| 1272 | + def logarithm(self, name='z'): |
| 1273 | + r""" |
| 1274 | + Return the logarithm of the given Drinfeld module. |
| 1275 | +
|
| 1276 | + By definition, the logarithm is the compositional inverse of the |
| 1277 | + exponential (see :meth:`exponential`). Note that the logarithm |
| 1278 | + is only defined when the `\mathbb{F}_q[T]`-characteristic is |
| 1279 | + zero. |
| 1280 | +
|
| 1281 | + INPUT: |
| 1282 | +
|
| 1283 | + - ``name`` (string, default: ``'z'``) -- the name of the |
| 1284 | + generator of the lazy power series ring. |
| 1285 | +
|
| 1286 | + OUTPUT: |
| 1287 | +
|
| 1288 | + A lazy power series over the base field. |
| 1289 | +
|
| 1290 | + EXAMPLES:: |
| 1291 | +
|
| 1292 | + sage: A = GF(2)['T'] |
| 1293 | + sage: K.<T> = Frac(A) |
| 1294 | + sage: phi = DrinfeldModule(A, [T, 1]) |
| 1295 | + sage: log = phi.logarithm(); log |
| 1296 | + z + ((1/(T^2+T))*z^2) + ((1/(T^6+T^5+T^3+T^2))*z^4) + O(z^8) |
| 1297 | +
|
| 1298 | + The logarithm is returned as a lazy power series, meaning that |
| 1299 | + any of its coefficients can be computed on demands:: |
| 1300 | +
|
| 1301 | + sage: log[2^4] |
| 1302 | + 1/(T^30 + T^29 + T^27 + ... + T^7 + T^5 + T^4) |
| 1303 | + sage: log[2^5] |
| 1304 | + 1/(T^62 + T^61 + T^59 + ... + T^8 + T^6 + T^5) |
| 1305 | +
|
| 1306 | + Example in higher rank:: |
| 1307 | +
|
| 1308 | + sage: A = GF(5)['T'] |
| 1309 | + sage: K.<T> = Frac(A) |
| 1310 | + sage: phi = DrinfeldModule(A, [T, T^2, T + T^2 + T^4, 1]) |
| 1311 | + sage: phi.logarithm() |
| 1312 | + z + ((4*T/(T^4+4))*z^5) + O(z^8) |
| 1313 | +
|
| 1314 | + TESTS:: |
| 1315 | +
|
| 1316 | + sage: A = GF(2)['T'] |
| 1317 | + sage: K.<T> = Frac(A) |
| 1318 | + sage: phi = DrinfeldModule(A, [T, 1]) |
| 1319 | + sage: q = 2 |
| 1320 | + sage: log[2] == -1/((T**q - T)) # expected value |
| 1321 | + True |
| 1322 | + sage: log[2**2] == 1/((T**q - T)*(T**(q**2) - T)) # expected value |
| 1323 | + True |
| 1324 | + sage: log[2**3] == -1/((T**q - T)*(T**(q**2) - T)*(T**(q**3) - T)) # expected value |
| 1325 | + True |
| 1326 | +
|
| 1327 | + :: |
| 1328 | +
|
| 1329 | + sage: Fq.<w> = GF(3) |
| 1330 | + sage: A = Fq['T'] |
| 1331 | + sage: phi = DrinfeldModule(A, [w, 1]) |
| 1332 | + sage: phi.logarithm() |
| 1333 | + Traceback (most recent call last): |
| 1334 | + ... |
| 1335 | + ValueError: characteristic must be zero (=T + 2) |
| 1336 | + """ |
| 1337 | + if self.category()._characteristic: |
| 1338 | + raise ValueError(f"characteristic must be zero (={self.characteristic()})") |
| 1339 | + L = LazyPowerSeriesRing(self._base, name) |
| 1340 | + zero = self._base.zero() |
| 1341 | + q = self._Fq.cardinality() |
| 1342 | + |
| 1343 | + def coeff_log(k): |
| 1344 | + # Return the k-th coefficient of the logarithm |
| 1345 | + k = ZZ(k) |
| 1346 | + if k.is_power_of(q): |
| 1347 | + return self._compute_coefficient_log(k.log(q)) |
| 1348 | + else: |
| 1349 | + return self._base.zero() |
| 1350 | + return L(coeff_log, valuation=1) |
| 1351 | + |
| 1352 | + |
1106 | 1353 | def morphism(self):
|
1107 | 1354 | r"""
|
1108 | 1355 | Return the morphism object that defines the Drinfeld module.
|
|
0 commit comments