From c28a01e4e8f3346b44b40d7715b85ebd58cb3ff2 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 7 Feb 2023 21:39:30 +0100 Subject: [PATCH 01/62] implement basic J-invariants and is_isomorphic methods --- .../drinfeld_modules/drinfeld_module.py | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) 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 67e4ab963e8..8bfd5221ae5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -24,8 +24,10 @@ # http://www.gnu.org/licenses/ # ***************************************************************************** +from sage.arith.misc import gcd from sage.categories.drinfeld_modules import DrinfeldModules from sage.categories.homset import Hom +from sage.geometry.polyhedron.constructor import Polyhedron from sage.misc.latex import latex from sage.misc.lazy_string import _LazyString from sage.rings.integer import Integer @@ -1123,6 +1125,78 @@ def j_invariant(self): q = self._Fq.order() return (g**(q+1)) / delta + def basic_J_invariant(self, coeffs_exponents, check=True): + r""" + Return the basic J-invariant given by coeff if it exists. + """ + r = self._gen.degree() + q = self._Fq.order() + dr = list(coeffs_exponents).pop() + if check: + left = 0 + for k, d in enumerate(coeffs_exponents, start=1): + left += d*(q**k - 1) + if left != dr*(q**r - 1): + raise ValueError + num = self._base.one() + coeffs = self.coefficients()[1:] + gr = coeffs.pop() + for g, d in zip(coeffs, coeffs_exponents): + if g: + num *= g**d + return num/(gr**dr) + + def _compute_basic_J_invariants(self): + """ + Return a generator of basic J-invariants. + """ + # Create the equation and inequalities for the polyhedron: + r = self._gen.degree() + q = self._Fq.order() + equation = [0] + inequalities = [] + for i in range(1, r): + + # create the equation: + # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) + equation.append(q ** i - 1) + + # create inequalities of the form 0 <= delta_i + lower_bounds = [0] * (r + 1) + lower_bounds[i] = 1 + + # create inequalities of the form delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) + upper_bounds = [Integer((q ** r - 1)/(q ** (gcd(i, r)) - 1))] + [0] * r + upper_bounds[i] = -1 + + inequalities.extend((lower_bounds, upper_bounds)) + + equation.append(1 - q ** r) + + # Create the polyhedron defined by the equation and the inequalities. + polyhedron = Polyhedron(ieqs=inequalities, eqns=[equation]) + + # Compute its integral points + integral_points = polyhedron.integral_points() + + return [p for p in integral_points if gcd(p) == 1] + + def is_isomorphic(self, psi): + r""" + Return whether ``self`` is isomorphic to the Drinfeld module `\psi`. + """ + if self.rank() != psi.rank(): + return False + if self.rank() == 1: + return True + basic_J_inv = self._compute_basic_J_invariants() + isom = True + for J in basic_J_inv: + if self.basic_J_invariant(J, check=False) != psi.basic_J_invariant(J, check=False): + isom = False + break + return isom + def morphism(self): r""" Return the morphism object that defines the Drinfeld module. From 4305c8559ab1e61593197b08835dd339eafaf9a6 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 8 Feb 2023 00:26:14 +0100 Subject: [PATCH 02/62] add doc and refactor basic j invariant --- .../drinfeld_modules/drinfeld_module.py | 79 ++++++++++++++++--- 1 file changed, 66 insertions(+), 13 deletions(-) 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 8bfd5221ae5..0be6aa4e715 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1125,30 +1125,70 @@ def j_invariant(self): q = self._Fq.order() return (g**(q+1)) / delta - def basic_J_invariant(self, coeffs_exponents, check=True): + def basic_j_invariant(self, parameters, check=True): r""" - Return the basic J-invariant given by coeff if it exists. + Return the basic J-invariant corresponding to the given parameters. + + EXAMPLES:: + + sage: K. = Frac(GF(5)['T']) + sage: c = T^4 + 3*T^2 + T + sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) + sage: phi.basic_j_invariant([20, 7, 2]) + (T^7 + 2*T^6 + T^5 + T^2 + 2*T + 1)/(T^4 + 2*T^2 + 1) + sage: phi.basic_j_invariant([1, 1, 1]) + Traceback (most recent call last): + ... + ValueError: the given parameters does not defines a basic j-invariant """ r = self._gen.degree() q = self._Fq.order() - dr = list(coeffs_exponents).pop() + parameters = list(parameters) + dr = parameters.pop() if check: + right = dr*(q**r - 1) left = 0 - for k, d in enumerate(coeffs_exponents, start=1): - left += d*(q**k - 1) - if left != dr*(q**r - 1): - raise ValueError + for k, d in enumerate(parameters): + left += d*(q**(k+1) - 1) + if left != right: + raise ValueError("the given parameters does not defines a basic j-invariant") num = self._base.one() coeffs = self.coefficients()[1:] gr = coeffs.pop() - for g, d in zip(coeffs, coeffs_exponents): + for g, d in zip(coeffs, parameters): if g: num *= g**d return num/(gr**dr) - def _compute_basic_J_invariants(self): + def basic_j_invariants_parameters(self): """ - Return a generator of basic J-invariants. + Return the list of basic j-invariants. + + EXAMPLES:: + + sage: K. = Frac(GF(5)['T']) + sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) + sage: phi.basic_j_invariants_parameters() + [(1, 5, 1), + (7, 4, 1), + (13, 3, 1), + (19, 2, 1), + (25, 1, 1), + (31, 0, 1), + (8, 9, 2), + (20, 7, 2), + (9, 14, 3), + (15, 13, 3), + (27, 11, 3), + (10, 19, 4), + (22, 17, 4), + (11, 24, 5), + (17, 23, 5), + (23, 22, 5), + (29, 21, 5), + (0, 31, 6), + (12, 29, 6), + (31, 31, 7)] """ # Create the equation and inequalities for the polyhedron: r = self._gen.degree() @@ -1183,16 +1223,29 @@ def _compute_basic_J_invariants(self): def is_isomorphic(self, psi): r""" - Return whether ``self`` is isomorphic to the Drinfeld module `\psi`. + Return ``True`` whether ``self`` is isomorphic to the Drinfeld module + `\psi`. + + EXAMPLES:: + + sage: K. = Frac(GF(5)['T']) + sage: c = T^4 + 3*T^2 + T + sage: phi = DrinfeldModule(A, [T, T^3, T^9, T]) + sage: psi = DrinfeldModule(A, [T, c^4*T^3, c^(24)*T^9, c^(124)*T]) + sage: phi.is_isomorphic(psi) + True + sage: phi0 = DrinfeldModule(A, [T, 1, 1, 1]) + sage: phi.is_isomorphic(phi0) + False """ if self.rank() != psi.rank(): return False if self.rank() == 1: return True - basic_J_inv = self._compute_basic_J_invariants() + basic_J_inv = self.basic_j_invariants_parameters() isom = True for J in basic_J_inv: - if self.basic_J_invariant(J, check=False) != psi.basic_J_invariant(J, check=False): + if self.basic_j_invariant(J, check=False) != psi.basic_j_invariant(J, check=False): isom = False break return isom From ba08866b0c9a95323d3508b4e85fc98a922ebd4a Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 14 Feb 2023 12:15:55 +0100 Subject: [PATCH 03/62] fix some doctests --- .../drinfeld_modules/drinfeld_module.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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 2afd28f4da5..1aae745d4cc 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -591,7 +591,7 @@ def __classcall_private__(cls, function_ring, gen, name='t'): base_morphism = Hom(function_ring, base_field_noext)(gen[0]) base_field = base_field_noext.over(base_morphism) - # This test is also done in the category. We put it here also + # This test is also done in the category. We put it here also # to have a friendlier error message if not base_field.is_field(): raise ValueError('generator coefficients must live in a field') @@ -1110,7 +1110,8 @@ def basic_j_invariant(self, parameters, check=True): EXAMPLES:: - sage: K. = Frac(GF(5)['T']) + sage: A = GF(5)['T'] + sage: K. = Frac(A) sage: c = T^4 + 3*T^2 + T sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) sage: phi.basic_j_invariant([20, 7, 2]) @@ -1145,7 +1146,8 @@ def basic_j_invariants_parameters(self): EXAMPLES:: - sage: K. = Frac(GF(5)['T']) + sage: A = GF(5)['T'] + sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) sage: phi.basic_j_invariants_parameters() [(1, 5, 1), @@ -1207,7 +1209,8 @@ def is_isomorphic(self, psi): EXAMPLES:: - sage: K. = Frac(GF(5)['T']) + sage: A = GF(5)['T'] + sage: K. = Frac(A) sage: c = T^4 + 3*T^2 + T sage: phi = DrinfeldModule(A, [T, T^3, T^9, T]) sage: psi = DrinfeldModule(A, [T, c^4*T^3, c^(24)*T^9, c^(124)*T]) From 87ce47a96fd7c4306dfdaef9ce378fb7d5d9f7ae Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 14 Feb 2023 12:46:03 +0100 Subject: [PATCH 04/62] drinfeld_module.py: make basic_j_invariants_parameters return a list of lists --- .../drinfeld_modules/drinfeld_module.py | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) 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 1aae745d4cc..45be4db6082 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1123,26 +1123,25 @@ def basic_j_invariant(self, parameters, check=True): """ r = self._gen.degree() q = self._Fq.order() - parameters = list(parameters) - dr = parameters.pop() + dr = parameters[-1] if check: right = dr*(q**r - 1) left = 0 - for k, d in enumerate(parameters): + for k, d in enumerate(parameters[:-1]): left += d*(q**(k+1) - 1) if left != right: raise ValueError("the given parameters does not defines a basic j-invariant") num = self._base.one() coeffs = self.coefficients()[1:] gr = coeffs.pop() - for g, d in zip(coeffs, parameters): + for g, d in zip(coeffs, parameters[:-1]): if g: num *= g**d return num/(gr**dr) def basic_j_invariants_parameters(self): """ - Return the list of basic j-invariants. + Return the list of basic j-invariants parameters. EXAMPLES:: @@ -1150,26 +1149,26 @@ def basic_j_invariants_parameters(self): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) sage: phi.basic_j_invariants_parameters() - [(1, 5, 1), - (7, 4, 1), - (13, 3, 1), - (19, 2, 1), - (25, 1, 1), - (31, 0, 1), - (8, 9, 2), - (20, 7, 2), - (9, 14, 3), - (15, 13, 3), - (27, 11, 3), - (10, 19, 4), - (22, 17, 4), - (11, 24, 5), - (17, 23, 5), - (23, 22, 5), - (29, 21, 5), - (0, 31, 6), - (12, 29, 6), - (31, 31, 7)] + [[1, 5, 1], + [7, 4, 1], + [13, 3, 1], + [19, 2, 1], + [25, 1, 1], + [31, 0, 1], + [8, 9, 2], + [20, 7, 2], + [9, 14, 3], + [15, 13, 3], + [27, 11, 3], + [10, 19, 4], + [22, 17, 4], + [11, 24, 5], + [17, 23, 5], + [23, 22, 5], + [29, 21, 5], + [0, 31, 6], + [12, 29, 6], + [31, 31, 7]] """ # Create the equation and inequalities for the polyhedron: r = self._gen.degree() @@ -1200,7 +1199,7 @@ def basic_j_invariants_parameters(self): # Compute its integral points integral_points = polyhedron.integral_points() - return [p for p in integral_points if gcd(p) == 1] + return [list(p) for p in integral_points if gcd(p) == 1] def is_isomorphic(self, psi): r""" From 03259fcb8cb4a126c8656938223948b4f47c2ef3 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 14 Feb 2023 12:49:19 +0100 Subject: [PATCH 05/62] drinfeld_module.py: minor change in variables names --- .../function_field/drinfeld_modules/drinfeld_module.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 45be4db6082..89d90d856ff 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1223,10 +1223,10 @@ def is_isomorphic(self, psi): return False if self.rank() == 1: return True - basic_J_inv = self.basic_j_invariants_parameters() + j_inv_params = self.basic_j_invariants_parameters() isom = True - for J in basic_J_inv: - if self.basic_j_invariant(J, check=False) != psi.basic_j_invariant(J, check=False): + for p in j_inv_params: + if self.basic_j_invariant(p, check=False) != psi.basic_j_invariant(p, check=False): isom = False break return isom From 460753c705240274bffd9d8ebfd6b746c0c62584 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 14 Feb 2023 13:09:43 +0100 Subject: [PATCH 06/62] drinfeld_module.py: remove some copies of lists --- .../function_field/drinfeld_modules/drinfeld_module.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) 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 89d90d856ff..4b2b34555d6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1132,9 +1132,8 @@ def basic_j_invariant(self, parameters, check=True): if left != right: raise ValueError("the given parameters does not defines a basic j-invariant") num = self._base.one() - coeffs = self.coefficients()[1:] - gr = coeffs.pop() - for g, d in zip(coeffs, parameters[:-1]): + gr = self.coefficients()[-1] + for g, d in zip(self.coefficients()[1:], parameters[:-1]): if g: num *= g**d return num/(gr**dr) @@ -1176,7 +1175,6 @@ def basic_j_invariants_parameters(self): equation = [0] inequalities = [] for i in range(1, r): - # create the equation: # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) equation.append(q ** i - 1) @@ -1223,9 +1221,8 @@ def is_isomorphic(self, psi): return False if self.rank() == 1: return True - j_inv_params = self.basic_j_invariants_parameters() isom = True - for p in j_inv_params: + for p in self.basic_j_invariants_parameters(): if self.basic_j_invariant(p, check=False) != psi.basic_j_invariant(p, check=False): isom = False break From 906e774ac75273ee2846373d3803997feccc56b0 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 14 Feb 2023 13:54:26 +0100 Subject: [PATCH 07/62] drinfeld_module.py: make the j_invariant method work for higher ranks --- .../drinfeld_modules/drinfeld_module.py | 43 +++++++++++-------- 1 file changed, 24 insertions(+), 19 deletions(-) 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 4b2b34555d6..cfe7d40ebc2 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1062,15 +1062,24 @@ def is_finite(self): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return isinstance(self, FiniteDrinfeldModule) - def j_invariant(self): + def j_invariant(self, k=1): r""" - Return the j-invariant of the Drinfeld module if the rank is - two; raise a NotImplementedError otherwise. + Return the `k`-th `j`-invariant of the Drinfeld + `\mathbb{F}_q[T]`-module. - Assume the rank is two. Write the generator `\phi_T = \omega + - g\tau + \Delta\tau^2`. The j-invariant is defined by - `\frac{g^{q+1}}{\Delta}`, `q` being the order of the base field - of the function ring. In our case, this field is always finite. + Recall that the `k`-th `j`-invariant of a Drinfeld module `\phi` of any + rank is defined by + + .. MATH:: + + j_k(\phi) := g_k(\phi)^{(q^r - 1)/(q^\mathrm{gcd}(k, r) - 1)}}{g_r(\phi)^{(q^k - 1)/(q^{\mathrm{gcd}(k, r)} - 1)} + + where `g_i(\phi)` is the `i`-th coefficient of the generator `\phi_T`. + + INPUT: + + - `k` (default: 1) - an integer greater of equal to one and less than + the rank. OUTPUT: an element in the base codomain @@ -1089,20 +1098,16 @@ def j_invariant(self): sage: rho = DrinfeldModule(A, [p_root, 0, 1]) sage: rho.j_invariant() 0 - - The rank must be two:: - - sage: sigma = DrinfeldModule(A, [p_root, 1, 0]) - sage: sigma.j_invariant() - Traceback (most recent call last): - ... - NotImplementedError: rank must be 2 """ - self._check_rank_two() - g = self.coefficient(1) - delta = self.coefficient(2) + if not isinstance(k, (int, Integer)): + raise TypeError(f"k must be an integer") + r = self.rank() + if k <= 0 or k >= r: + raise ValueError(f"k (={k}) must be greater or equal to one and less than the rank (={r})") q = self._Fq.order() - return (g**(q+1)) / delta + gk = self.coefficient(k) + gr = self.coefficient(r) + return gk**(Integer((q**r - 1)/(q**gcd(k, r) - 1))) / gr**(Integer((q**k - 1)/(q**gcd(k, r) - 1))) def basic_j_invariant(self, parameters, check=True): r""" From 07c97b52894d6ee7b8be388568e3b9745e7a974a Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 14 Feb 2023 14:04:04 +0100 Subject: [PATCH 08/62] drinfeld_module.py: add some parameters check in basic_j_invariant --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 5 +++++ 1 file changed, 5 insertions(+) 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 cfe7d40ebc2..7ba34cec8a8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1126,8 +1126,13 @@ def basic_j_invariant(self, parameters, check=True): ... ValueError: the given parameters does not defines a basic j-invariant """ + # TODO: add doctests, fix pep8, define the parameters. r = self._gen.degree() q = self._Fq.order() + if not isinstance(parameters, list): + raise TypeError("parameters must be a list") + if not len(parameters) == r: + raise ValueError(f"the length of the parameters list must be {r}") dr = parameters[-1] if check: right = dr*(q**r - 1) From fc4943d684d3d5f10108eeee0095ccc0b53f2458 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 19 Feb 2023 09:54:26 +0100 Subject: [PATCH 09/62] drinfeld_module.py: fix basic j-invariant bug --- .../drinfeld_modules/drinfeld_module.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) 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 3151798fef7..2cc57ee6598 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -31,6 +31,7 @@ from sage.misc.latex import latex from sage.misc.latex import latex_variable_name from sage.misc.lazy_string import _LazyString +from sage.misc.misc_c import prod from sage.rings.integer import Integer from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.polynomial_ring import PolynomialRing_general @@ -1135,17 +1136,16 @@ def basic_j_invariant(self, parameters, check=True): raise ValueError(f"the length of the parameters list must be {r}") dr = parameters[-1] if check: + # check that the following equation is satisfied: + # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) right = dr*(q**r - 1) - left = 0 - for k, d in enumerate(parameters[:-1]): - left += d*(q**(k+1) - 1) + left = sum(d*(q**(k+1) - 1) for k, d in enumerate(parameters[:-1])) if left != right: - raise ValueError("the given parameters does not defines a basic j-invariant") - num = self._base.one() + raise ValueError("the given parameters does not defines a basic\ + j-invariant") gr = self.coefficients()[-1] - for g, d in zip(self.coefficients()[1:], parameters[:-1]): - if g: - num *= g**d + num = prod(g**d for g, d in zip(self.coefficients(sparse=False)[1:],\ + parameters[:-1])) return num/(gr**dr) def basic_j_invariants_parameters(self): From 196ef5ae05fafa9ecd7894bfd1b07edb633272d7 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 19 Feb 2023 10:30:16 +0100 Subject: [PATCH 10/62] drinfeld_module.py: enhance j_invariant method --- .../drinfeld_modules/drinfeld_module.py | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) 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 2cc57ee6598..79e0d232188 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1099,16 +1099,35 @@ def j_invariant(self, k=1): sage: rho = DrinfeldModule(A, [p_root, 0, 1]) sage: rho.j_invariant() 0 + + :: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T^2, 1, T + 1, T^3]) + sage: phi.j_invariant() + T^309 + sage: phi.j_invariant(2) + 1/T^3 + sage: phi.j_invariant(3) + (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93 + sage: sage: phi.j_invariant([0, 7, 54, 11]) + (T^54 + 4*T^53 + T^52 + 4*T^51 + T^50 + 2*T^29 + 3*T^28 + 2*T^27 + 3*T^26 + 2*T^25 + T^4 + 4*T^3 + T^2 + 4*T + 1)/T^33 """ - if not isinstance(k, (int, Integer)): - raise TypeError(f"k must be an integer") - r = self.rank() - if k <= 0 or k >= r: - raise ValueError(f"k (={k}) must be greater or equal to one and less than the rank (={r})") - q = self._Fq.order() - gk = self.coefficient(k) - gr = self.coefficient(r) - return gk**(Integer((q**r - 1)/(q**gcd(k, r) - 1))) / gr**(Integer((q**k - 1)/(q**gcd(k, r) - 1))) + # TODO: add documentation for this method + if isinstance(k, (int, Integer)): + r = self.rank() + if k <= 0 or k >= r: + raise ValueError(f"k (={k}) must be greater or equal to one and less than the rank (={r})") + q = self._Fq.order() + gk = self.coefficient(k) + gr = self.coefficient(r) + j_inv = gk**(Integer((q**r - 1)/(q**gcd(k, r) - 1))) / gr**(Integer((q**k - 1)/(q**gcd(k, r) - 1))) + elif isinstance(k, list): + j_inv = self.basic_j_invariant(k) + else: + raise TypeError("k must be an integer or a list of integer") + return j_inv def basic_j_invariant(self, parameters, check=True): r""" @@ -1127,7 +1146,7 @@ def basic_j_invariant(self, parameters, check=True): ... ValueError: the given parameters does not defines a basic j-invariant """ - # TODO: add doctests, fix pep8, define the parameters. + # TODO: add doctests, fix pep8, define the parameters, implement j_invariantS. r = self._gen.degree() q = self._Fq.order() if not isinstance(parameters, list): From 7221b1f1d1f7357ed4db1864ea08ce9afbb35cd6 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 23 Feb 2023 09:00:37 -0500 Subject: [PATCH 11/62] drinfeld_module.py: fix doctest and typo --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 79e0d232188..f85cd5a9698 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1111,7 +1111,7 @@ def j_invariant(self, k=1): 1/T^3 sage: phi.j_invariant(3) (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93 - sage: sage: phi.j_invariant([0, 7, 54, 11]) + sage: phi.j_invariant([0, 7, 54, 11]) (T^54 + 4*T^53 + T^52 + 4*T^51 + T^50 + 2*T^29 + 3*T^28 + 2*T^27 + 3*T^26 + 2*T^25 + T^4 + 4*T^3 + T^2 + 4*T + 1)/T^33 """ # TODO: add documentation for this method @@ -1126,7 +1126,7 @@ def j_invariant(self, k=1): elif isinstance(k, list): j_inv = self.basic_j_invariant(k) else: - raise TypeError("k must be an integer or a list of integer") + raise TypeError("k must be an integer or a list of integers") return j_inv def basic_j_invariant(self, parameters, check=True): From 26fa7e9bfedf09d3943339d576818d842ab43381 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sat, 25 Feb 2023 14:29:47 -0500 Subject: [PATCH 12/62] drinfeld_module.py: add a parameter option to the method basic_j_invariant_parameters The method will now compute only the parameters of the nonzero basic j-invariants. --- .../drinfeld_modules/drinfeld_module.py | 38 ++++++++++++++----- 1 file changed, 28 insertions(+), 10 deletions(-) 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 f85cd5a9698..2a4218b275e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1167,7 +1167,7 @@ def basic_j_invariant(self, parameters, check=True): parameters[:-1])) return num/(gr**dr) - def basic_j_invariants_parameters(self): + def basic_j_invariants_parameters(self, param=None): """ Return the list of basic j-invariants parameters. @@ -1198,27 +1198,43 @@ def basic_j_invariants_parameters(self): [12, 29, 6], [31, 31, 7]] """ - # Create the equation and inequalities for the polyhedron: r = self._gen.degree() + if param is None: + param = [idx for idx, c in enumerate( + self.coefficients(sparse=False)[1:-1], start=1) if c] + elif isinstance(param, list): # check if param is valid + if not all(isinstance(k, (int, Integer)) for k in param): + raise TypeError("the elements of the list param must be integers") + if max(param) >= r or min(param) <= 0: + raise ValueError(f"the maximum or the minimum of the list" \ + "param must be > 0 and < {r} respectively") + if not all(param[i] < param[i+1] for i in range(len(param) - 1)): + raise ValueError(f"the elements of param should be distinct" \ + "and sorted") + elif param == "all": + param = list(range(1, r)) + else: + raise TypeError("input param is invalid") + # Create the equation and inequalities for the polyhedron: q = self._Fq.order() equation = [0] inequalities = [] - for i in range(1, r): + for idx, i in enumerate(param): # create the equation: # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) - equation.append(q ** i - 1) + equation.append(q**i - 1) # create inequalities of the form 0 <= delta_i - lower_bounds = [0] * (r + 1) - lower_bounds[i] = 1 + lower_bounds = [0]*(len(param) + 2) + lower_bounds[idx + 1] = 1 # create inequalities of the form delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) - upper_bounds = [Integer((q ** r - 1)/(q ** (gcd(i, r)) - 1))] + [0] * r - upper_bounds[i] = -1 + upper_bounds = [Integer((q**r - 1)/(q**(gcd(i, r)) - 1))] + [0]*(len(param) + 1) + upper_bounds[idx + 1] = -1 inequalities.extend((lower_bounds, upper_bounds)) - equation.append(1 - q ** r) + equation.append(1 - q**r) # Create the polyhedron defined by the equation and the inequalities. polyhedron = Polyhedron(ieqs=inequalities, eqns=[equation]) @@ -1226,7 +1242,9 @@ def basic_j_invariants_parameters(self): # Compute its integral points integral_points = polyhedron.integral_points() - return [list(p) for p in integral_points if gcd(p) == 1] + param.append(r) + points = [list(p) for p in integral_points if gcd(p) == 1] + return [param, points] def is_isomorphic(self, psi): r""" From 41731fa9edd7ae3986ba6ecf7ba883ec8c02d94d Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sat, 25 Feb 2023 14:55:59 -0500 Subject: [PATCH 13/62] drinfeld_module.py: fix doctest in basic_j_invariant_paramters method --- .../drinfeld_modules/drinfeld_module.py | 47 ++++++++++--------- 1 file changed, 25 insertions(+), 22 deletions(-) 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 2a4218b275e..1058a2f7874 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1177,26 +1177,30 @@ def basic_j_invariants_parameters(self, param=None): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) sage: phi.basic_j_invariants_parameters() - [[1, 5, 1], - [7, 4, 1], - [13, 3, 1], - [19, 2, 1], - [25, 1, 1], - [31, 0, 1], - [8, 9, 2], - [20, 7, 2], - [9, 14, 3], - [15, 13, 3], - [27, 11, 3], - [10, 19, 4], - [22, 17, 4], - [11, 24, 5], - [17, 23, 5], - [23, 22, 5], - [29, 21, 5], - [0, 31, 6], - [12, 29, 6], - [31, 31, 7]] + [[[1, 2, 3], [1, 5, 1]], + [[1, 2, 3], [7, 4, 1]], + [[1, 2, 3], [13, 3, 1]], + [[1, 2, 3], [19, 2, 1]], + [[1, 2, 3], [25, 1, 1]], + [[1, 2, 3], [31, 0, 1]], + [[1, 2, 3], [8, 9, 2]], + [[1, 2, 3], [20, 7, 2]], + [[1, 2, 3], [9, 14, 3]], + [[1, 2, 3], [15, 13, 3]], + [[1, 2, 3], [27, 11, 3]], + [[1, 2, 3], [10, 19, 4]], + [[1, 2, 3], [22, 17, 4]], + [[1, 2, 3], [11, 24, 5]], + [[1, 2, 3], [17, 23, 5]], + [[1, 2, 3], [23, 22, 5]], + [[1, 2, 3], [29, 21, 5]], + [[1, 2, 3], [0, 31, 6]], + [[1, 2, 3], [12, 29, 6]], + [[1, 2, 3], [31, 31, 7]]] + sage: phi.basic_j_invariants_parameters([1]) + [[[1, 3], [31, 1]]] + sage: phi.basic_j_invariants_parameters([2]) + [[[2, 3], [31, 6]]] """ r = self._gen.degree() if param is None: @@ -1243,8 +1247,7 @@ def basic_j_invariants_parameters(self, param=None): integral_points = polyhedron.integral_points() param.append(r) - points = [list(p) for p in integral_points if gcd(p) == 1] - return [param, points] + return [[param, list(p)] for p in integral_points if gcd(p) == 1] def is_isomorphic(self, psi): r""" From 2b2ddd9d8d6f0cecbc70096e39e95c39353a2657 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sat, 25 Feb 2023 15:38:33 -0500 Subject: [PATCH 14/62] drinfeld_module.py: fix basic_j_invariant method --- .../drinfeld_modules/drinfeld_module.py | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) 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 1058a2f7874..756e1827df9 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1111,7 +1111,7 @@ def j_invariant(self, k=1): 1/T^3 sage: phi.j_invariant(3) (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93 - sage: phi.j_invariant([0, 7, 54, 11]) + sage: phi.j_invariant([[1, 2, 3, 4], [0, 7, 54, 11]]) (T^54 + 4*T^53 + T^52 + 4*T^51 + T^50 + 2*T^29 + 3*T^28 + 2*T^27 + 3*T^26 + 2*T^25 + T^4 + 4*T^3 + T^2 + 4*T + 1)/T^33 """ # TODO: add documentation for this method @@ -1139,32 +1139,30 @@ def basic_j_invariant(self, parameters, check=True): sage: K. = Frac(A) sage: c = T^4 + 3*T^2 + T sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) - sage: phi.basic_j_invariant([20, 7, 2]) + sage: phi.basic_j_invariant([[1, 2, 3], [20, 7, 2]]) (T^7 + 2*T^6 + T^5 + T^2 + 2*T + 1)/(T^4 + 2*T^2 + 1) - sage: phi.basic_j_invariant([1, 1, 1]) + sage: phi.basic_j_invariant([[1, 2, 3], [1, 1, 1]]) Traceback (most recent call last): ... ValueError: the given parameters does not defines a basic j-invariant """ # TODO: add doctests, fix pep8, define the parameters, implement j_invariantS. + # TODO: delete this method? r = self._gen.degree() q = self._Fq.order() if not isinstance(parameters, list): raise TypeError("parameters must be a list") - if not len(parameters) == r: - raise ValueError(f"the length of the parameters list must be {r}") - dr = parameters[-1] + dr = parameters[1][-1] if check: # check that the following equation is satisfied: # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) right = dr*(q**r - 1) - left = sum(d*(q**(k+1) - 1) for k, d in enumerate(parameters[:-1])) + left = sum(parameters[1][i]*(q**(parameters[0][i]) - 1) for i in range(len(parameters[0]) - 1)) if left != right: raise ValueError("the given parameters does not defines a basic\ j-invariant") gr = self.coefficients()[-1] - num = prod(g**d for g, d in zip(self.coefficients(sparse=False)[1:],\ - parameters[:-1])) + num = prod(self._gen[k]**d for k, d in zip(parameters[0][:-1], parameters[1][:-1])) return num/(gr**dr) def basic_j_invariants_parameters(self, param=None): From 9b51066b08d882f220a74b116001cdd3447b3374 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 26 Feb 2023 18:34:31 -0500 Subject: [PATCH 15/62] fix the method is_isomorphic --- .../drinfeld_modules/drinfeld_module.py | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) 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 756e1827df9..c53fa4c3c82 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1264,11 +1264,28 @@ def is_isomorphic(self, psi): sage: phi0 = DrinfeldModule(A, [T, 1, 1, 1]) sage: phi.is_isomorphic(phi0) False + + :: + + sage: phi = DrinfeldModule(A, [T, 1, 0, 1, 0, 0, 0, T^2, 1, 1]) + sage: phi.is_isomorphic(phi) + True + sage: psi = DrinfeldModule(A, [T, 1, 0, 1, 0, T, 0, T^2, 1, 1]) + sage: phi.is_isomorphic(psi) + False """ - if self.rank() != psi.rank(): + # trivial checks: + if self._gen == psi._gen: + return True + if self._gen.degree() != psi._gen.degree(): return False - if self.rank() == 1: + if self._gen.degree() == 1: return True + # check if self and psi are patterned alike: + if not all(bool(g_self) == bool(g_psi) for g_self, g_psi in\ + zip(self._gen.coefficients(sparse=False), psi._gen.coefficients(sparse=False))): + return False + # check that all the nonzero basic j-invariants agree isom = True for p in self.basic_j_invariants_parameters(): if self.basic_j_invariant(p, check=False) != psi.basic_j_invariant(p, check=False): From 7f02bf115549debb43654ba48290136ace585516 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 1 Mar 2023 09:07:26 -0500 Subject: [PATCH 16/62] make j_invariant method returns the list of all jk_invariants --- .../drinfeld_modules/drinfeld_module.py | 55 +++++++------------ 1 file changed, 19 insertions(+), 36 deletions(-) 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 c53fa4c3c82..be9ac4f1c34 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1063,26 +1063,22 @@ def is_finite(self): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return isinstance(self, FiniteDrinfeldModule) - def j_invariant(self, k=1): + def j_invariant(self): r""" - Return the `k`-th `j`-invariant of the Drinfeld - `\mathbb{F}_q[T]`-module. + Return the `j`-invariant of the Drinfeld `\mathbb{F}_q[T]`-module, that + is the list of all its `j_k`-invariants. - Recall that the `k`-th `j`-invariant of a Drinfeld module `\phi` of any - rank is defined by + Recall that the `j_k`-invariant of a Drinfeld module `\phi` of any rank + is defined by: .. MATH:: j_k(\phi) := g_k(\phi)^{(q^r - 1)/(q^\mathrm{gcd}(k, r) - 1)}}{g_r(\phi)^{(q^k - 1)/(q^{\mathrm{gcd}(k, r)} - 1)} - where `g_i(\phi)` is the `i`-th coefficient of the generator `\phi_T`. + where `1\leq k \leq r - 1` and `g_i(\phi)` is the `i`-th coefficient of + the generator. - INPUT: - - - `k` (default: 1) - an integer greater of equal to one and less than - the rank. - - OUTPUT: an element in the base codomain + OUTPUT: the list of all the `j_k`-invariants of ``self``. EXAMPLES:: @@ -1092,13 +1088,13 @@ def j_invariant(self, k=1): 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.j_invariant() - z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2 + [z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2] sage: psi = DrinfeldModule(A, [p_root, 1, 1]) sage: psi.j_invariant() - 1 + [1] sage: rho = DrinfeldModule(A, [p_root, 0, 1]) sage: rho.j_invariant() - 0 + [0] :: @@ -1106,28 +1102,15 @@ def j_invariant(self, k=1): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, T^2, 1, T + 1, T^3]) sage: phi.j_invariant() - T^309 - sage: phi.j_invariant(2) - 1/T^3 - sage: phi.j_invariant(3) - (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93 - sage: phi.j_invariant([[1, 2, 3, 4], [0, 7, 54, 11]]) - (T^54 + 4*T^53 + T^52 + 4*T^51 + T^50 + 2*T^29 + 3*T^28 + 2*T^27 + 3*T^26 + 2*T^25 + T^4 + 4*T^3 + T^2 + 4*T + 1)/T^33 + [T^309, + 1/T^3, + (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93] """ - # TODO: add documentation for this method - if isinstance(k, (int, Integer)): - r = self.rank() - if k <= 0 or k >= r: - raise ValueError(f"k (={k}) must be greater or equal to one and less than the rank (={r})") - q = self._Fq.order() - gk = self.coefficient(k) - gr = self.coefficient(r) - j_inv = gk**(Integer((q**r - 1)/(q**gcd(k, r) - 1))) / gr**(Integer((q**k - 1)/(q**gcd(k, r) - 1))) - elif isinstance(k, list): - j_inv = self.basic_j_invariant(k) - else: - raise TypeError("k must be an integer or a list of integers") - return j_inv + r = self._gen.degree() + q = self._Fq.order() + gr = self._gen[-1] + return [self._gen[k]**(Integer((q**r - 1)/(q**gcd(k, r) - 1))) /\ + gr**(Integer((q**k - 1)/(q**gcd(k, r) - 1))) for k in range(1, r)] def basic_j_invariant(self, parameters, check=True): r""" From ad36ae5a299c220cb3740d9b7182875bb99d0895 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 1 Mar 2023 09:43:23 -0500 Subject: [PATCH 17/62] drinfeld_module.py: fix failing doctest --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ae6fe364c70..7ded99cede4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -268,7 +268,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): As well as the j-invariant if the rank is two:: sage: phi.j_invariant() # j-invariant - 1 + [1] A Drinfeld `\mathbb{F}_q[T]`-module can be seen as an Ore polynomial with positive degree and constant coefficient `\gamma(T)`, where From 7354e38b4e26ddd8a5dbd351f6de69f31a9df4e3 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 10:10:44 -0500 Subject: [PATCH 18/62] drinfeld_module: generalize j_invariant to higher rank and enhance documentation --- .../drinfeld_modules/drinfeld_module.py | 113 ++++++++++++++---- 1 file changed, 93 insertions(+), 20 deletions(-) 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 7ded99cede4..842bea9b6c5 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -268,7 +268,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): As well as the j-invariant if the rank is two:: sage: phi.j_invariant() # j-invariant - [1] + 1 A Drinfeld `\mathbb{F}_q[T]`-module can be seen as an Ore polynomial with positive degree and constant coefficient `\gamma(T)`, where @@ -1064,22 +1064,53 @@ def is_finite(self): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return isinstance(self, FiniteDrinfeldModule) - def j_invariant(self): + def j_invariant(self, parameter=None, check=True): r""" - Return the `j`-invariant of the Drinfeld `\mathbb{F}_q[T]`-module, that - is the list of all its `j_k`-invariants. + Return the `j`-invariant of the Drinfeld `\mathbb{F}_q[T]`-module for + the given parameter. + + Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r`. Then then + `[[k_1, \ldots, k_n], [d_1, \ldots, d_n, d_r]]`-`j`-invariant of `\phi` + is defined by + + .. MATH:: - Recall that the `j_k`-invariant of a Drinfeld module `\phi` of any rank - is defined by: + j_k(\phi) := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} + + where `1\leq k_i \leq r - 1` and the integers `d_i` satisfies the + *weight-0 condition*: .. MATH:: - j_k(\phi) := g_k(\phi)^{(q^r - 1)/(q^\mathrm{gcd}(k, r) - 1)}}{g_r(\phi)^{(q^k - 1)/(q^{\mathrm{gcd}(k, r)} - 1)} + d_1 (q^{k_1} - 1) + d_2 (q^{k_2} - 1) + ... + d_{n} (q^{k_n} - 1) = d_r (q^r - 1) + + Furthermore, if `\gcd(d_1,\ldots, d_n, d_r) = 1` and + + .. MATH:: - where `1\leq k \leq r - 1` and `g_i(\phi)` is the `i`-th coefficient of - the generator. + 0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), \quad (1 \leq i \leq n) - OUTPUT: the list of all the `j_k`-invariants of ``self``. + then the `j`-invariant is called *basic*. See the method + meth:`basic_j_invariants_parameters` for computing the list of all basic + `j`-invariant parameters. + + INPUT: + + - ``parameter`` (integer or list, default: None) -- this parameter is + either a list of two lists or an integer between 1 and `r-1` (`r` is + the rank). If ``parameter`` is a list of lists, then it must be of the + form ``[[k1, k2, ..., kn], [d1, d2, ..., dn, dr]]`` where the ``ki`` + and ``di`` are integers satisfying the weight-0 condition described + above. If ``parameter`` is an integer ``k`` then the method returns + the ``j``-invariant associated to the parameter ``[[k], [dk, dr]]``. + If the rank of the Drinfeld module is 2, then the method returns by + default the usual `j`-invariant. + + - ``check`` (bool, default: ``True``) -- if this flag is set to + ``False`` then the code will not check if the given parameter satisfy + the weight-0 condition. + + OUTPUT: the `j`-invariant of ``self`` for the given parameter. EXAMPLES:: @@ -1089,29 +1120,71 @@ def j_invariant(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.j_invariant() - [z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2] + z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2 sage: psi = DrinfeldModule(A, [p_root, 1, 1]) sage: psi.j_invariant() - [1] + 1 sage: rho = DrinfeldModule(A, [p_root, 0, 1]) sage: rho.j_invariant() - [0] + 0 :: sage: A = GF(5)['T'] sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, T^2, 1, T + 1, T^3]) - sage: phi.j_invariant() - [T^309, - 1/T^3, - (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93] + sage: phi.j_invariant(1) + T^309 + sage: phi.j_invariant(2) + 1/T^3 + sage: phi.j_invariant(3) + (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93 + + :: + + sage: Fq. = GF(7) + sage: A. = Fq[] + sage: phi = DrinfeldModule(A, [a, a^2 + a, 0, 3*a, a^2+1]) + sage: J = phi.j_invariant([[1, 3], [267, 269, 39]]); J + 5 + sage: J == (phi.coefficient(1)**267)*(phi.coefficient(3)**269)/(phi.coefficient(4)**39) + True + sage: phi.j_invariant([[3], [400, 57]]) + 4 + sage: phi.j_invariant([[3], [400, 57]]) == phi.j_invariant(3) + True """ r = self._gen.degree() q = self._Fq.order() - gr = self._gen[-1] - return [self._gen[k]**(Integer((q**r - 1)/(q**gcd(k, r) - 1))) /\ - gr**(Integer((q**k - 1)/(q**gcd(k, r) - 1))) for k in range(1, r)] + if parameter is None: + if r != 2: + raise ValueError("input 'parameter' must be different from " + "None if the rank is greater than 2") + return self._gen[1]**(q+1)/self._gen[2] + if isinstance(parameter, (int, Integer)): + if parameter <= 0 or parameter >= r: + raise ValueError(f"input 'parameter' must be greater or " + "equal to 1 or less than the rank (={r})") + dk = Integer((q**r - 1)/(q**gcd(parameter, r) - 1)) + dr = Integer((q**parameter - 1)/(q**gcd(parameter, r) - 1)) + return self._gen[parameter]**dk / self._gen[-1]**dr + elif isinstance(parameter, list): + if len(parameter) != 2: + raise ValueError("input 'parameter' must be of length 2") + else: + raise TypeError("input 'parameter' must be a list or an integer") + dr = parameter[1][-1] + if check: + # check that the following equation is satisfied: + # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) + right = dr*(q**r - 1) + left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in + range(len(parameter[0]))) + if left != right: + raise ValueError("input 'parameter' does not defines a basic " + "j-invariant") + num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) + return num/(self._gen[-1]**dr) def basic_j_invariant(self, parameters, check=True): r""" From 181d56e00c0dbb55ab45989be0cb4b529fee3014 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 10:26:42 -0500 Subject: [PATCH 19/62] drinfeld_module.py: delete basic_j_invariant method and change an error message --- .../drinfeld_modules/drinfeld_module.py | 44 ++----------------- 1 file changed, 4 insertions(+), 40 deletions(-) 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 842bea9b6c5..df7ed76fb9c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1175,53 +1175,17 @@ def j_invariant(self, parameter=None, check=True): raise TypeError("input 'parameter' must be a list or an integer") dr = parameter[1][-1] if check: - # check that the following equation is satisfied: + # check that the weight-0 condition is statisfied: # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) right = dr*(q**r - 1) left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in range(len(parameter[0]))) if left != right: - raise ValueError("input 'parameter' does not defines a basic " - "j-invariant") + raise ValueError("input 'parameter' does not satisfy the " + "weight-0 condition") num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) return num/(self._gen[-1]**dr) - def basic_j_invariant(self, parameters, check=True): - r""" - Return the basic J-invariant corresponding to the given parameters. - - EXAMPLES:: - - sage: A = GF(5)['T'] - sage: K. = Frac(A) - sage: c = T^4 + 3*T^2 + T - sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) - sage: phi.basic_j_invariant([[1, 2, 3], [20, 7, 2]]) - (T^7 + 2*T^6 + T^5 + T^2 + 2*T + 1)/(T^4 + 2*T^2 + 1) - sage: phi.basic_j_invariant([[1, 2, 3], [1, 1, 1]]) - Traceback (most recent call last): - ... - ValueError: the given parameters does not defines a basic j-invariant - """ - # TODO: add doctests, fix pep8, define the parameters, implement j_invariantS. - # TODO: delete this method? - r = self._gen.degree() - q = self._Fq.order() - if not isinstance(parameters, list): - raise TypeError("parameters must be a list") - dr = parameters[1][-1] - if check: - # check that the following equation is satisfied: - # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) - right = dr*(q**r - 1) - left = sum(parameters[1][i]*(q**(parameters[0][i]) - 1) for i in range(len(parameters[0]) - 1)) - if left != right: - raise ValueError("the given parameters does not defines a basic\ - j-invariant") - gr = self.coefficients()[-1] - num = prod(self._gen[k]**d for k, d in zip(parameters[0][:-1], parameters[1][:-1])) - return num/(gr**dr) - def basic_j_invariants_parameters(self, param=None): """ Return the list of basic j-invariants parameters. @@ -1345,7 +1309,7 @@ def is_isomorphic(self, psi): # check that all the nonzero basic j-invariants agree isom = True for p in self.basic_j_invariants_parameters(): - if self.basic_j_invariant(p, check=False) != psi.basic_j_invariant(p, check=False): + if self.j_invariant(p, check=False) != psi.j_invariant(p, check=False): isom = False break return isom From 656a3195f43e8a6769d586a2fef9c19bf5531866 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 11:28:46 -0500 Subject: [PATCH 20/62] drinfeld_module.py: fix basic_j_invariant_parameters and add documentation --- .../drinfeld_modules/drinfeld_module.py | 114 ++++++++++-------- 1 file changed, 66 insertions(+), 48 deletions(-) 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 df7ed76fb9c..aafd8ab6562 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1091,7 +1091,7 @@ def j_invariant(self, parameter=None, check=True): 0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), \quad (1 \leq i \leq n) then the `j`-invariant is called *basic*. See the method - meth:`basic_j_invariants_parameters` for computing the list of all basic + :meth:`basic_j_invariant_parameters` for computing the list of all basic `j`-invariant parameters. INPUT: @@ -1186,73 +1186,92 @@ def j_invariant(self, parameter=None, check=True): num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) return num/(self._gen[-1]**dr) - def basic_j_invariants_parameters(self, param=None): + def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): """ - Return the list of basic j-invariants parameters. + Return the list of basic `j`-invariant parameters. + + See the method :meth:`j_invariant` for the definition of the basic + `j`-invariant parameters. + + INPUT: + + - ``coeff_indices`` (list, default: ``None``) -- a list of indices for + the coefficients of the Drinfeld module. If specific indices are + chosen, then the method will return only the basic `j`-invariant + parameters that involves the given indices. + + - ``nonzero`` (bool, Default: ``False``) -- setting this to ``True`` + will return only the parameters for which the basic `j`-invariant is + nonzero EXAMPLES:: sage: A = GF(5)['T'] sage: K. = Frac(A) - sage: phi = DrinfeldModule(A, [T, 1, T+1, T^2 + 1]) - sage: phi.basic_j_invariants_parameters() - [[[1, 2, 3], [1, 5, 1]], - [[1, 2, 3], [7, 4, 1]], - [[1, 2, 3], [13, 3, 1]], - [[1, 2, 3], [19, 2, 1]], - [[1, 2, 3], [25, 1, 1]], - [[1, 2, 3], [31, 0, 1]], - [[1, 2, 3], [8, 9, 2]], - [[1, 2, 3], [20, 7, 2]], - [[1, 2, 3], [9, 14, 3]], - [[1, 2, 3], [15, 13, 3]], - [[1, 2, 3], [27, 11, 3]], - [[1, 2, 3], [10, 19, 4]], - [[1, 2, 3], [22, 17, 4]], - [[1, 2, 3], [11, 24, 5]], - [[1, 2, 3], [17, 23, 5]], - [[1, 2, 3], [23, 22, 5]], - [[1, 2, 3], [29, 21, 5]], - [[1, 2, 3], [0, 31, 6]], - [[1, 2, 3], [12, 29, 6]], - [[1, 2, 3], [31, 31, 7]]] - sage: phi.basic_j_invariants_parameters([1]) - [[[1, 3], [31, 1]]] - sage: phi.basic_j_invariants_parameters([2]) - [[[2, 3], [31, 6]]] + sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) + sage: phi.basic_j_invariant_parameters() + [[[1, 2], [1, 5, 1]], + [[1, 2], [7, 4, 1]], + [[1, 2], [13, 3, 1]], + [[1, 2], [19, 2, 1]], + [[1, 2], [25, 1, 1]], + [[1, 2], [31, 0, 1]], + [[1, 2], [8, 9, 2]], + [[1, 2], [20, 7, 2]], + [[1, 2], [9, 14, 3]], + [[1, 2], [15, 13, 3]], + [[1, 2], [27, 11, 3]], + [[1, 2], [10, 19, 4]], + [[1, 2], [22, 17, 4]], + [[1, 2], [11, 24, 5]], + [[1, 2], [17, 23, 5]], + [[1, 2], [23, 22, 5]], + [[1, 2], [29, 21, 5]], + [[1, 2], [0, 31, 6]], + [[1, 2], [12, 29, 6]], + [[1, 2], [31, 31, 7]]] + sage: phi.basic_j_invariant_parameters([1]) + [[[1], [31, 1]]] + sage: phi.basic_j_invariant_parameters([2]) + [[[2], [31, 6]]] + sage: phi.basic_j_invariant_parameters(nonzero=True) + [[[2], [31, 6]]] """ r = self._gen.degree() - if param is None: - param = [idx for idx, c in enumerate( - self.coefficients(sparse=False)[1:-1], start=1) if c] - elif isinstance(param, list): # check if param is valid - if not all(isinstance(k, (int, Integer)) for k in param): - raise TypeError("the elements of the list param must be integers") - if max(param) >= r or min(param) <= 0: + if coeff_indices is None: + if nonzero: + coeff_indices = [k for k, g in enumerate( + self.coefficients(sparse=False)[1:-1], start=1) if g] + else: + coeff_indices = list(range(1, r)) + elif isinstance(coeff_indices, list): # check if coeff_indices is valid + if not all(isinstance(k, (int, Integer)) for k in coeff_indices): + raise TypeError("the elements of the list coeff_indices must be integers") + if max(coeff_indices) >= r or min(coeff_indices) <= 0: raise ValueError(f"the maximum or the minimum of the list" \ - "param must be > 0 and < {r} respectively") - if not all(param[i] < param[i+1] for i in range(len(param) - 1)): - raise ValueError(f"the elements of param should be distinct" \ + "coeff_indices must be > 0 and < {r} respectively") + if not all(coeff_indices[i] < coeff_indices[i+1] for i in range(len(coeff_indices) - 1)): + raise ValueError(f"the elements of coeff_indices should be distinct" \ "and sorted") - elif param == "all": - param = list(range(1, r)) + if nonzero: + coeff_indices = [k for k in coeff_indices if self._gen[k]] else: - raise TypeError("input param is invalid") + raise TypeError("input coeff_indices is invalid") # Create the equation and inequalities for the polyhedron: q = self._Fq.order() equation = [0] inequalities = [] - for idx, i in enumerate(param): + for idx, i in enumerate(coeff_indices): # create the equation: # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) equation.append(q**i - 1) # create inequalities of the form 0 <= delta_i - lower_bounds = [0]*(len(param) + 2) + lower_bounds = [0]*(len(coeff_indices) + 2) lower_bounds[idx + 1] = 1 # create inequalities of the form delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) - upper_bounds = [Integer((q**r - 1)/(q**(gcd(i, r)) - 1))] + [0]*(len(param) + 1) + upper_bounds = [Integer((q**r - 1)/(q**(gcd(i, r)) - 1))] + [0]*(len(coeff_indices) + 1) upper_bounds[idx + 1] = -1 inequalities.extend((lower_bounds, upper_bounds)) @@ -1265,8 +1284,7 @@ def basic_j_invariants_parameters(self, param=None): # Compute its integral points integral_points = polyhedron.integral_points() - param.append(r) - return [[param, list(p)] for p in integral_points if gcd(p) == 1] + return [[coeff_indices, list(p)] for p in integral_points if gcd(p) == 1] def is_isomorphic(self, psi): r""" @@ -1308,7 +1326,7 @@ def is_isomorphic(self, psi): return False # check that all the nonzero basic j-invariants agree isom = True - for p in self.basic_j_invariants_parameters(): + for p in self.basic_j_invariant_parameters(nonzero=True): if self.j_invariant(p, check=False) != psi.j_invariant(p, check=False): isom = False break From cc3dd7f26e938f3242b33482da8bed2b122b32a0 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 11:36:32 -0500 Subject: [PATCH 21/62] drinfeld_module.py: put the new methods in alphabetical order --- .../drinfeld_modules/drinfeld_module.py | 292 +++++++++--------- 1 file changed, 146 insertions(+), 146 deletions(-) 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 aafd8ab6562..d49d2b3af3d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -886,6 +886,106 @@ def action(self): from sage.rings.function_field.drinfeld_modules.action import DrinfeldModuleAction return DrinfeldModuleAction(self) + def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): + """ + Return the list of basic `j`-invariant parameters. + + See the method :meth:`j_invariant` for the definition of the basic + `j`-invariant parameters. + + INPUT: + + - ``coeff_indices`` (list, default: ``None``) -- a list of indices for + the coefficients of the Drinfeld module. If specific indices are + chosen, then the method will return only the basic `j`-invariant + parameters that involves the given indices. + + - ``nonzero`` (bool, Default: ``False``) -- setting this to ``True`` + will return only the parameters for which the basic `j`-invariant is + nonzero + + EXAMPLES:: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) + sage: phi.basic_j_invariant_parameters() + [[[1, 2], [1, 5, 1]], + [[1, 2], [7, 4, 1]], + [[1, 2], [13, 3, 1]], + [[1, 2], [19, 2, 1]], + [[1, 2], [25, 1, 1]], + [[1, 2], [31, 0, 1]], + [[1, 2], [8, 9, 2]], + [[1, 2], [20, 7, 2]], + [[1, 2], [9, 14, 3]], + [[1, 2], [15, 13, 3]], + [[1, 2], [27, 11, 3]], + [[1, 2], [10, 19, 4]], + [[1, 2], [22, 17, 4]], + [[1, 2], [11, 24, 5]], + [[1, 2], [17, 23, 5]], + [[1, 2], [23, 22, 5]], + [[1, 2], [29, 21, 5]], + [[1, 2], [0, 31, 6]], + [[1, 2], [12, 29, 6]], + [[1, 2], [31, 31, 7]]] + sage: phi.basic_j_invariant_parameters([1]) + [[[1], [31, 1]]] + sage: phi.basic_j_invariant_parameters([2]) + [[[2], [31, 6]]] + sage: phi.basic_j_invariant_parameters(nonzero=True) + [[[2], [31, 6]]] + """ + r = self._gen.degree() + if coeff_indices is None: + if nonzero: + coeff_indices = [k for k, g in enumerate( + self.coefficients(sparse=False)[1:-1], start=1) if g] + else: + coeff_indices = list(range(1, r)) + elif isinstance(coeff_indices, list): # check if coeff_indices is valid + if not all(isinstance(k, (int, Integer)) for k in coeff_indices): + raise TypeError("the elements of the list coeff_indices must be integers") + if max(coeff_indices) >= r or min(coeff_indices) <= 0: + raise ValueError(f"the maximum or the minimum of the list" \ + "coeff_indices must be > 0 and < {r} respectively") + if not all(coeff_indices[i] < coeff_indices[i+1] for i in range(len(coeff_indices) - 1)): + raise ValueError(f"the elements of coeff_indices should be distinct" \ + "and sorted") + if nonzero: + coeff_indices = [k for k in coeff_indices if self._gen[k]] + else: + raise TypeError("input coeff_indices is invalid") + # Create the equation and inequalities for the polyhedron: + q = self._Fq.order() + equation = [0] + inequalities = [] + for idx, i in enumerate(coeff_indices): + # create the equation: + # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) + equation.append(q**i - 1) + + # create inequalities of the form 0 <= delta_i + lower_bounds = [0]*(len(coeff_indices) + 2) + lower_bounds[idx + 1] = 1 + + # create inequalities of the form delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) + upper_bounds = [Integer((q**r - 1)/(q**(gcd(i, r)) - 1))] + [0]*(len(coeff_indices) + 1) + upper_bounds[idx + 1] = -1 + + inequalities.extend((lower_bounds, upper_bounds)) + + equation.append(1 - q**r) + + # Create the polyhedron defined by the equation and the inequalities. + polyhedron = Polyhedron(ieqs=inequalities, eqns=[equation]) + + # Compute its integral points + integral_points = polyhedron.integral_points() + + return [[coeff_indices, list(p)] for p in integral_points if gcd(p) == 1] + def coefficient(self, n): r""" Return the `n`-th coefficient of the generator. @@ -1064,6 +1164,52 @@ def is_finite(self): from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return isinstance(self, FiniteDrinfeldModule) + def is_isomorphic(self, psi): + r""" + Return ``True`` whether ``self`` is isomorphic to the Drinfeld module + `\psi`. + + EXAMPLES:: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: c = T^4 + 3*T^2 + T + sage: phi = DrinfeldModule(A, [T, T^3, T^9, T]) + sage: psi = DrinfeldModule(A, [T, c^4*T^3, c^(24)*T^9, c^(124)*T]) + sage: phi.is_isomorphic(psi) + True + sage: phi0 = DrinfeldModule(A, [T, 1, 1, 1]) + sage: phi.is_isomorphic(phi0) + False + + :: + + sage: phi = DrinfeldModule(A, [T, 1, 0, 1, 0, 0, 0, T^2, 1, 1]) + sage: phi.is_isomorphic(phi) + True + sage: psi = DrinfeldModule(A, [T, 1, 0, 1, 0, T, 0, T^2, 1, 1]) + sage: phi.is_isomorphic(psi) + False + """ + # trivial checks: + if self._gen == psi._gen: + return True + if self._gen.degree() != psi._gen.degree(): + return False + if self._gen.degree() == 1: + return True + # check if self and psi are patterned alike: + if not all(bool(g_self) == bool(g_psi) for g_self, g_psi in\ + zip(self._gen.coefficients(sparse=False), psi._gen.coefficients(sparse=False))): + return False + # check that all the nonzero basic j-invariants agree + isom = True + for p in self.basic_j_invariant_parameters(nonzero=True): + if self.j_invariant(p, check=False) != psi.j_invariant(p, check=False): + isom = False + break + return isom + def j_invariant(self, parameter=None, check=True): r""" Return the `j`-invariant of the Drinfeld `\mathbb{F}_q[T]`-module for @@ -1186,152 +1332,6 @@ def j_invariant(self, parameter=None, check=True): num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) return num/(self._gen[-1]**dr) - def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): - """ - Return the list of basic `j`-invariant parameters. - - See the method :meth:`j_invariant` for the definition of the basic - `j`-invariant parameters. - - INPUT: - - - ``coeff_indices`` (list, default: ``None``) -- a list of indices for - the coefficients of the Drinfeld module. If specific indices are - chosen, then the method will return only the basic `j`-invariant - parameters that involves the given indices. - - - ``nonzero`` (bool, Default: ``False``) -- setting this to ``True`` - will return only the parameters for which the basic `j`-invariant is - nonzero - - EXAMPLES:: - - sage: A = GF(5)['T'] - sage: K. = Frac(A) - sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) - sage: phi.basic_j_invariant_parameters() - [[[1, 2], [1, 5, 1]], - [[1, 2], [7, 4, 1]], - [[1, 2], [13, 3, 1]], - [[1, 2], [19, 2, 1]], - [[1, 2], [25, 1, 1]], - [[1, 2], [31, 0, 1]], - [[1, 2], [8, 9, 2]], - [[1, 2], [20, 7, 2]], - [[1, 2], [9, 14, 3]], - [[1, 2], [15, 13, 3]], - [[1, 2], [27, 11, 3]], - [[1, 2], [10, 19, 4]], - [[1, 2], [22, 17, 4]], - [[1, 2], [11, 24, 5]], - [[1, 2], [17, 23, 5]], - [[1, 2], [23, 22, 5]], - [[1, 2], [29, 21, 5]], - [[1, 2], [0, 31, 6]], - [[1, 2], [12, 29, 6]], - [[1, 2], [31, 31, 7]]] - sage: phi.basic_j_invariant_parameters([1]) - [[[1], [31, 1]]] - sage: phi.basic_j_invariant_parameters([2]) - [[[2], [31, 6]]] - sage: phi.basic_j_invariant_parameters(nonzero=True) - [[[2], [31, 6]]] - """ - r = self._gen.degree() - if coeff_indices is None: - if nonzero: - coeff_indices = [k for k, g in enumerate( - self.coefficients(sparse=False)[1:-1], start=1) if g] - else: - coeff_indices = list(range(1, r)) - elif isinstance(coeff_indices, list): # check if coeff_indices is valid - if not all(isinstance(k, (int, Integer)) for k in coeff_indices): - raise TypeError("the elements of the list coeff_indices must be integers") - if max(coeff_indices) >= r or min(coeff_indices) <= 0: - raise ValueError(f"the maximum or the minimum of the list" \ - "coeff_indices must be > 0 and < {r} respectively") - if not all(coeff_indices[i] < coeff_indices[i+1] for i in range(len(coeff_indices) - 1)): - raise ValueError(f"the elements of coeff_indices should be distinct" \ - "and sorted") - if nonzero: - coeff_indices = [k for k in coeff_indices if self._gen[k]] - else: - raise TypeError("input coeff_indices is invalid") - # Create the equation and inequalities for the polyhedron: - q = self._Fq.order() - equation = [0] - inequalities = [] - for idx, i in enumerate(coeff_indices): - # create the equation: - # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) - equation.append(q**i - 1) - - # create inequalities of the form 0 <= delta_i - lower_bounds = [0]*(len(coeff_indices) + 2) - lower_bounds[idx + 1] = 1 - - # create inequalities of the form delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) - upper_bounds = [Integer((q**r - 1)/(q**(gcd(i, r)) - 1))] + [0]*(len(coeff_indices) + 1) - upper_bounds[idx + 1] = -1 - - inequalities.extend((lower_bounds, upper_bounds)) - - equation.append(1 - q**r) - - # Create the polyhedron defined by the equation and the inequalities. - polyhedron = Polyhedron(ieqs=inequalities, eqns=[equation]) - - # Compute its integral points - integral_points = polyhedron.integral_points() - - return [[coeff_indices, list(p)] for p in integral_points if gcd(p) == 1] - - def is_isomorphic(self, psi): - r""" - Return ``True`` whether ``self`` is isomorphic to the Drinfeld module - `\psi`. - - EXAMPLES:: - - sage: A = GF(5)['T'] - sage: K. = Frac(A) - sage: c = T^4 + 3*T^2 + T - sage: phi = DrinfeldModule(A, [T, T^3, T^9, T]) - sage: psi = DrinfeldModule(A, [T, c^4*T^3, c^(24)*T^9, c^(124)*T]) - sage: phi.is_isomorphic(psi) - True - sage: phi0 = DrinfeldModule(A, [T, 1, 1, 1]) - sage: phi.is_isomorphic(phi0) - False - - :: - - sage: phi = DrinfeldModule(A, [T, 1, 0, 1, 0, 0, 0, T^2, 1, 1]) - sage: phi.is_isomorphic(phi) - True - sage: psi = DrinfeldModule(A, [T, 1, 0, 1, 0, T, 0, T^2, 1, 1]) - sage: phi.is_isomorphic(psi) - False - """ - # trivial checks: - if self._gen == psi._gen: - return True - if self._gen.degree() != psi._gen.degree(): - return False - if self._gen.degree() == 1: - return True - # check if self and psi are patterned alike: - if not all(bool(g_self) == bool(g_psi) for g_self, g_psi in\ - zip(self._gen.coefficients(sparse=False), psi._gen.coefficients(sparse=False))): - return False - # check that all the nonzero basic j-invariants agree - isom = True - for p in self.basic_j_invariant_parameters(nonzero=True): - if self.j_invariant(p, check=False) != psi.j_invariant(p, check=False): - isom = False - break - return isom - def morphism(self): r""" Return the morphism object that defines the Drinfeld module. From ab2f865fe6bde79de66b4f5ae84a586f82cd8200 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 13:55:38 -0500 Subject: [PATCH 22/62] Add Potemine reference and some documentation tweaks --- src/doc/en/reference/references/index.rst | 15 ++-- .../drinfeld_modules/drinfeld_module.py | 73 +++++++++++++++---- 2 files changed, 69 insertions(+), 19 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 206ce1e3359..a89a98ce1b8 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1869,9 +1869,9 @@ REFERENCES: .. [CW2005] \J. E. Cremona and M. Watkins. Computing isogenies of elliptic curves. preprint, 2005. -.. [CHW2015] Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation +.. [CHW2015] Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation with weakly integral anyons. Quantum Inf. Process. 14 (2015), - no. 8, 2687-2727. + no. 8, 2687-2727. .. [CW2015] Cui, S. X. and Wang, Z. (2015). Universal quantum computation with metaplectic anyons. Journal of Mathematical Physics, 56(3), 032202. @@ -2235,7 +2235,7 @@ REFERENCES: .. [Dy1993] \M. J. Dyer. *Hecke algebras and shellings of Bruhat intervals*. Compositio Mathematica, 1993, 89(1): 91-115. -.. [Dy1994] \M. J. Dyer. *Bruhat intervals, polyhedral cones and +.. [Dy1994] \M. J. Dyer. *Bruhat intervals, polyhedral cones and Kazhdan-Lusztig-Stanley polynomials*. Math.Z., 215(2):223-236, 1994. .. _ref-E: @@ -3461,7 +3461,7 @@ REFERENCES: :doi:`10.1016/j.bbr.2011.03.031`, :arxiv:`0909.2442`. .. [JS2021] \D. Jahn, C. Stump. - *Bruhat intervals, subword complexes and brick polyhedra for + *Bruhat intervals, subword complexes and brick polyhedra for finite Coxeter groups*, 2021, :arxiv:`2103.03715`. .. [JV2000] \J. Justin, L. Vuillon, *Return words in Sturmian and @@ -4980,6 +4980,11 @@ REFERENCES: .. [Pos2005] \A. Postnikov, Affine approach to quantum Schubert calculus, Duke Math. J. 128 (2005) 473-509 +.. [Pot1998] Potemine, I.Y., + Minimal Terminal Q-Factorial Models of Drinfeld Coarse Moduli Schemes. + Mathematical Physics, Analysis and Geometry 1, 171-191 (1998). + :doi:`10.1023/A:1009724323513` + .. [PPW2013] \D. Perkinson, J. Perlman, and J. Wilmes. *Primer for the algebraic geometry of sandpiles*. Tropical and Non-Archimedean @@ -6377,7 +6382,7 @@ REFERENCES: Combinatorics, 19(1), P53, 2012. :doi:`10.37236/2087` .. [Zie1959] \N. Zierler. *Linear Recurring Sequences*. - Journal of the Society for Industrial and Applied Mathematics 7(1) + Journal of the Society for Industrial and Applied Mathematics 7(1) (1959): 31-48. :doi:`10.1137/0107003` .. [Zie1998] \G. M. Ziegler. *Shelling polyhedral 3-balls and 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 d49d2b3af3d..3b12c4fc7b8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -265,7 +265,7 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: phi.height() 1 - As well as the j-invariant if the rank is two:: + As well as the j-invariant:: sage: phi.j_invariant() # j-invariant 1 @@ -887,7 +887,7 @@ def action(self): return DrinfeldModuleAction(self) def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): - """ + r""" Return the list of basic `j`-invariant parameters. See the method :meth:`j_invariant` for the definition of the basic @@ -897,13 +897,21 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): - ``coeff_indices`` (list, default: ``None``) -- a list of indices for the coefficients of the Drinfeld module. If specific indices are - chosen, then the method will return only the basic `j`-invariant - parameters that involves the given indices. + given, then the method will return only the basic `j`-invariant + parameters that involves these indices. - ``nonzero`` (bool, Default: ``False``) -- setting this to ``True`` will return only the parameters for which the basic `j`-invariant is nonzero + .. WARNING:: + + The usage of this method can be very computationally expensive + depending if the given Drinfeld module is of rank strictly greater + 4 and for large value of `q`. Setting the ``nonzero`` flag to + ``True`` can speed up the computation considerably if the Drinfeld + module possesses multiple zero coefficients. + EXAMPLES:: sage: A = GF(5)['T'] @@ -934,8 +942,21 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): [[[1], [31, 1]]] sage: phi.basic_j_invariant_parameters([2]) [[[2], [31, 6]]] + + Use ``nonzero=True`` to speed up the computations for Drinfeld modules + for which some coefficients are zero:: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, 0, T+1, 0, 1, 0, T]) sage: phi.basic_j_invariant_parameters(nonzero=True) - [[[2], [31, 6]]] + [[[2, 4], [260, 641, 26]], + [[2, 4], [157, 19, 1]], + [[2, 4], [27, 24, 1]], + [[2, 4], [188, 143, 6]], + [[2, 4], [401, 260, 11]], + ... + [[2, 4], [288, 39, 2]]] """ r = self._gen.degree() if coeff_indices is None: @@ -1228,13 +1249,13 @@ def j_invariant(self, parameter=None, check=True): .. MATH:: - d_1 (q^{k_1} - 1) + d_2 (q^{k_2} - 1) + ... + d_{n} (q^{k_n} - 1) = d_r (q^r - 1) + d_1 (q^{k_1} - 1) + d_2 (q^{k_2} - 1) + ... + d_{n} (q^{k_n} - 1) = d_r (q^r - 1). Furthermore, if `\gcd(d_1,\ldots, d_n, d_r) = 1` and .. MATH:: - 0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), \quad (1 \leq i \leq n) + 0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), \quad 1 \leq i \leq n, then the `j`-invariant is called *basic*. See the method :meth:`basic_j_invariant_parameters` for computing the list of all basic @@ -1244,13 +1265,20 @@ def j_invariant(self, parameter=None, check=True): - ``parameter`` (integer or list, default: None) -- this parameter is either a list of two lists or an integer between 1 and `r-1` (`r` is - the rank). If ``parameter`` is a list of lists, then it must be of the - form ``[[k1, k2, ..., kn], [d1, d2, ..., dn, dr]]`` where the ``ki`` - and ``di`` are integers satisfying the weight-0 condition described - above. If ``parameter`` is an integer ``k`` then the method returns - the ``j``-invariant associated to the parameter ``[[k], [dk, dr]]``. - If the rank of the Drinfeld module is 2, then the method returns by - default the usual `j`-invariant. + the rank). + + - If ``parameter`` is a list of two lists, then it must be of the + form `[[k_1, k_2, \ldots, k_n], [d_1, d_2, \ldots, d_n, d_r]]` where + the `k_i` and `d_i` are integers satisfying the weight-0 condition + described above. In this case the method returns the associated + `j`-invariant; + + - If ``parameter`` is an integer `k` then the method returns + the ``j``-invariant associated to the parameter `[[k], [d_k, d_r]]`; + + - If ``parameter`` is ``None`` and the rank of the Drinfeld module is + 2, then the method returns its usual `j`-invariant, that is the + `j`-invariant for the parameter `[[1], [q+1, 1]]`. - ``check`` (bool, default: ``True``) -- if this flag is set to ``False`` then the code will not check if the given parameter satisfy @@ -1258,6 +1286,11 @@ def j_invariant(self, parameter=None, check=True): OUTPUT: the `j`-invariant of ``self`` for the given parameter. + REFERENCE: + + The notion of basic `j`-invariant was introduced by Potemine in + _[Pot1998]. + EXAMPLES:: sage: Fq = GF(25) @@ -1299,6 +1332,18 @@ def j_invariant(self, parameter=None, check=True): 4 sage: phi.j_invariant([[3], [400, 57]]) == phi.j_invariant(3) True + + The list of all basic `j`-invariant parameters can be retrieved using + the method :meth:`basic_j_invariant_parameters`:: + + sage: A = GF(3)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T^2 + T + 1, 0, T^4 + 1, T - 1]) + sage: param = phi.basic_j_invariant_parameters(nonzero=True) + sage: phi.j_invariant(param[0]) + T^13 + 2*T^12 + T + 2 + sage: phi.j_invariant(param[1]) + T^35 + 2*T^31 + T^27 + 2*T^8 + T^4 + 2 """ r = self._gen.degree() q = self._Fq.order() From 3649d458e44cd1e39216ce2357146791678927cb Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 18:38:29 -0500 Subject: [PATCH 23/62] change parameters to tuples and add some parameter checks in j_invariant --- .../drinfeld_modules/drinfeld_module.py | 135 +++++++++++------- 1 file changed, 80 insertions(+), 55 deletions(-) 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 3b12c4fc7b8..42397a03adf 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -895,10 +895,11 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): INPUT: - - ``coeff_indices`` (list, default: ``None``) -- a list of indices for - the coefficients of the Drinfeld module. If specific indices are - given, then the method will return only the basic `j`-invariant - parameters that involves these indices. + - ``coeff_indices`` (list or tuple, default: ``None``) -- a list or a + tuple of indices for the coefficients of the Drinfeld module. If + this option is not ``None``, then the method will return only the basic + `j`-invariant parameters that involves the given indices. By default, + the method will use all the coefficients. - ``nonzero`` (bool, Default: ``False``) -- setting this to ``True`` will return only the parameters for which the basic `j`-invariant is @@ -918,30 +919,41 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) sage: phi.basic_j_invariant_parameters() - [[[1, 2], [1, 5, 1]], - [[1, 2], [7, 4, 1]], - [[1, 2], [13, 3, 1]], - [[1, 2], [19, 2, 1]], - [[1, 2], [25, 1, 1]], - [[1, 2], [31, 0, 1]], - [[1, 2], [8, 9, 2]], - [[1, 2], [20, 7, 2]], - [[1, 2], [9, 14, 3]], - [[1, 2], [15, 13, 3]], - [[1, 2], [27, 11, 3]], - [[1, 2], [10, 19, 4]], - [[1, 2], [22, 17, 4]], - [[1, 2], [11, 24, 5]], - [[1, 2], [17, 23, 5]], - [[1, 2], [23, 22, 5]], - [[1, 2], [29, 21, 5]], - [[1, 2], [0, 31, 6]], - [[1, 2], [12, 29, 6]], - [[1, 2], [31, 31, 7]]] - sage: phi.basic_j_invariant_parameters([1]) - [[[1], [31, 1]]] - sage: phi.basic_j_invariant_parameters([2]) - [[[2], [31, 6]]] + [((1, 2), (1, 5, 1)), + ((1, 2), (7, 4, 1)), + ((1, 2), (13, 3, 1)), + ((1, 2), (19, 2, 1)), + ((1, 2), (25, 1, 1)), + ((1, 2), (31, 0, 1)), + ((1, 2), (8, 9, 2)), + ((1, 2), (20, 7, 2)), + ((1, 2), (9, 14, 3)), + ((1, 2), (15, 13, 3)), + ((1, 2), (27, 11, 3)), + ((1, 2), (10, 19, 4)), + ((1, 2), (22, 17, 4)), + ((1, 2), (11, 24, 5)), + ((1, 2), (17, 23, 5)), + ((1, 2), (23, 22, 5)), + ((1, 2), (29, 21, 5)), + ((1, 2), (0, 31, 6)), + ((1, 2), (12, 29, 6)), + ((1, 2), (31, 31, 7))] + + If a list of indices is given, then only the parameters involving those + coefficients will be computed:: + + sage: A = GF(3)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T, 2, T, 2*T, T^3, T^4 + T^2 + 1]) + sage: phi.basic_j_invariant_parameters([1, 5]) + [((1, 5), (273, 91, 31)), + ((1, 5), (297, 163, 55)), + ((1, 5), (295, 157, 53)), + ((1, 5), (265, 67, 23)), + ((1, 5), (357, 343, 115)), + ... + ((1, 5), (146, 74, 25))] Use ``nonzero=True`` to speed up the computations for Drinfeld modules for which some coefficients are zero:: @@ -950,13 +962,13 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 0, T+1, 0, 1, 0, T]) sage: phi.basic_j_invariant_parameters(nonzero=True) - [[[2, 4], [260, 641, 26]], - [[2, 4], [157, 19, 1]], - [[2, 4], [27, 24, 1]], - [[2, 4], [188, 143, 6]], - [[2, 4], [401, 260, 11]], + [((2, 4), (260, 641, 26)), + ((2, 4), (157, 19, 1)), + ((2, 4), (27, 24, 1)), + ((2, 4), (188, 143, 6)), + ((2, 4), (401, 260, 11)), ... - [[2, 4], [288, 39, 2]]] + ((2, 4), (288, 39, 2))] """ r = self._gen.degree() if coeff_indices is None: @@ -965,7 +977,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): self.coefficients(sparse=False)[1:-1], start=1) if g] else: coeff_indices = list(range(1, r)) - elif isinstance(coeff_indices, list): # check if coeff_indices is valid + elif isinstance(coeff_indices, (tuple, list)): # check if coeff_indices is valid + coeff_indices = list(coeff_indices) if not all(isinstance(k, (int, Integer)) for k in coeff_indices): raise TypeError("the elements of the list coeff_indices must be integers") if max(coeff_indices) >= r or min(coeff_indices) <= 0: @@ -977,7 +990,7 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): if nonzero: coeff_indices = [k for k in coeff_indices if self._gen[k]] else: - raise TypeError("input coeff_indices is invalid") + raise TypeError("input coeff_indices must be None, a tuple or a list") # Create the equation and inequalities for the polyhedron: q = self._Fq.order() equation = [0] @@ -1005,7 +1018,7 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): # Compute its integral points integral_points = polyhedron.integral_points() - return [[coeff_indices, list(p)] for p in integral_points if gcd(p) == 1] + return [(tuple(coeff_indices), tuple(p)) for p in integral_points if gcd(p) == 1] def coefficient(self, n): r""" @@ -1281,8 +1294,8 @@ def j_invariant(self, parameter=None, check=True): `j`-invariant for the parameter `[[1], [q+1, 1]]`. - ``check`` (bool, default: ``True``) -- if this flag is set to - ``False`` then the code will not check if the given parameter satisfy - the weight-0 condition. + ``False`` then the code will not check if the given parameter is valid + and satisfy the weight-0 condition. OUTPUT: the `j`-invariant of ``self`` for the given parameter. @@ -1324,13 +1337,13 @@ def j_invariant(self, parameter=None, check=True): sage: Fq. = GF(7) sage: A. = Fq[] sage: phi = DrinfeldModule(A, [a, a^2 + a, 0, 3*a, a^2+1]) - sage: J = phi.j_invariant([[1, 3], [267, 269, 39]]); J + sage: J = phi.j_invariant(((1, 3), (267, 269, 39))); J 5 sage: J == (phi.coefficient(1)**267)*(phi.coefficient(3)**269)/(phi.coefficient(4)**39) True - sage: phi.j_invariant([[3], [400, 57]]) + sage: phi.j_invariant(((3,), (400, 57))) 4 - sage: phi.j_invariant([[3], [400, 57]]) == phi.j_invariant(3) + sage: phi.j_invariant(((3,), (400, 57))) == phi.j_invariant(3) True The list of all basic `j`-invariant parameters can be retrieved using @@ -1359,23 +1372,35 @@ def j_invariant(self, parameter=None, check=True): dk = Integer((q**r - 1)/(q**gcd(parameter, r) - 1)) dr = Integer((q**parameter - 1)/(q**gcd(parameter, r) - 1)) return self._gen[parameter]**dk / self._gen[-1]**dr - elif isinstance(parameter, list): + elif isinstance(parameter, tuple): if len(parameter) != 2: raise ValueError("input 'parameter' must be of length 2") + if check: + if not isinstance(parameter[0], tuple): + raise TypeError("first entry of input 'parameter' must be " + "a tuple") + if not isinstance(parameter[1], tuple): + raise TypeError("second entry of input 'parameter' must be " + "a tuple") + if not all(isinstance(p, (int, Integer)) for p in parameter[0]): + raise TypeError("first tuple of input 'parameter' must " + "contain only integers") + if not all(isinstance(p, (int, Integer)) for p in parameter[1]): + raise TypeError("second tuple of input 'parameter' must " + "contain only integers") + # check that the weight-0 condition is statisfied: + # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) + right = parameter[1][-1]*(q**r - 1) + left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in + range(len(parameter[0]))) + if left != right: + raise ValueError("input 'parameter' does not satisfy the " + "weight-0 condition") else: - raise TypeError("input 'parameter' must be a list or an integer") - dr = parameter[1][-1] - if check: - # check that the weight-0 condition is statisfied: - # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) - right = dr*(q**r - 1) - left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in - range(len(parameter[0]))) - if left != right: - raise ValueError("input 'parameter' does not satisfy the " - "weight-0 condition") + raise TypeError("input 'parameter' must be a tuple of length 2 or " + "an integer") num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) - return num/(self._gen[-1]**dr) + return num/(self._gen[-1]**parameter[1][-1]) def morphism(self): r""" From a0b71195662327a7363f66d8a58d03ee4180ea9d Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 19:08:56 -0500 Subject: [PATCH 24/62] implement basic_j_invariants --- .../drinfeld_modules/drinfeld_module.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) 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 42397a03adf..02b1dc7e158 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1020,6 +1020,43 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): return [(tuple(coeff_indices), tuple(p)) for p in integral_points if gcd(p) == 1] + def basic_j_invariants(self): + r""" + Return a dictionnary whose keys are the basic `j`-invariants parameters + and values are the associated `j`-invariant. + + See the method :meth:`j_invariant` for the definition of the basic + `j`-invariants. + + EXAMPLES:: + + sage: Fq = GF(25) + sage: A. = Fq[] + sage: K. = Fq.extension(6) + 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.basic_j_invariants() + {((1,), (26, 1)): z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2} + + :: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T + 2, T+1, 1]) + sage: J_phi = phi.basic_j_invariants(); J_phi + {((1, 2), (0, 31, 6)): T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1, + ((1, 2), (1, 5, 1)): T^6 + 2*T^5 + T + 2, + ((1, 2), (7, 4, 1)): T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3, + ((1, 2), (8, 9, 2)): T^17 + 2*T^15 + T^14 + 4*T^13 + 4*T^11 + 4*T^10 + 3*T^9 + 2*T^8 + 3*T^7 + 2*T^6 + 3*T^5 + 2*T^4 + 3*T^3 + 4*T^2 + 3*T + 1, + ((1, 2), (9, 14, 3)): T^23 + 2*T^22 + 2*T^21 + T^19 + 4*T^18 + T^17 + 4*T^16 + T^15 + 4*T^14 + 2*T^12 + 4*T^11 + 4*T^10 + 2*T^8 + 4*T^7 + 4*T^6 + 2*T^4 + T^2 + 2*T + 2, + ((1, 2), (10, 19, 4)): T^29 + 4*T^28 + T^27 + 4*T^26 + T^25 + 2*T^24 + 3*T^23 + 2*T^22 + 3*T^21 + 2*T^20 + 4*T^19 + T^18 + 4*T^17 + T^16 + 4*T^15 + T^9 + 4*T^8 + T^7 + 4*T^6 + T^5 + 4*T^4 + T^3 + 4*T^2 + T + 4, + ... + ((1, 2), (31, 31, 7)): T^62 + 3*T^61 + 2*T^60 + 3*T^57 + 4*T^56 + T^55 + 2*T^52 + T^51 + 4*T^50 + 3*T^37 + 4*T^36 + T^35 + 4*T^32 + 2*T^31 + 3*T^30 + T^27 + 3*T^26 + 2*T^25 + 2*T^12 + T^11 + 4*T^10 + T^7 + 3*T^6 + 2*T^5 + 4*T^2 + 2*T + 3} + sage: J_phi[((1, 2), (7, 4, 1))] + T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3 + """ + return {p: self.j_invariant(p, check=False) for p in self.basic_j_invariant_parameters()} + def coefficient(self, n): r""" Return the `n`-th coefficient of the generator. From ae3fb3ee80fcdded9b25cf19c10c19b3571ec143 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 21:45:31 -0500 Subject: [PATCH 25/62] add some doctests and error messages --- .../drinfeld_modules/drinfeld_module.py | 80 +++++++++++++++---- 1 file changed, 65 insertions(+), 15 deletions(-) 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 02b1dc7e158..379bcb0465e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1394,6 +1394,54 @@ def j_invariant(self, parameter=None, check=True): T^13 + 2*T^12 + T + 2 sage: phi.j_invariant(param[1]) T^35 + 2*T^31 + T^27 + 2*T^8 + T^4 + 2 + + TESTS:: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, T^2, 1, T + 1, T^3]) + sage: phi.j_invariant() + Traceback (most recent call last): + ... + ValueError: input 'parameter' must be different from None if the rank is greater than 2 + sage: phi.j_invariant(-1) + Traceback (most recent call last): + ... + ValueError: input 'parameter' must be >= 1 or < the rank (=4) + sage: phi.j_invariant('x') + Traceback (most recent call last): + ... + TypeError: input 'parameter' must be a tuple of length 2 or an integer + sage: phi.j_invariant((1, 2, 3)) + Traceback (most recent call last): + ... + ValueError: input 'parameter' must be of length 2 + sage: phi.j_invariant(('x', (1, 2, 3))) + Traceback (most recent call last): + ... + TypeError: first entry of input 'parameter' must be a tuple + sage: phi.j_invariant(((1, 2), 'x')) + Traceback (most recent call last): + ... + TypeError: second entry of input 'parameter' must be a tuple + sage: phi.j_invariant(((1, 2, 3, 4, 5), (2, 1))) + Traceback (most recent call last): + ... + ValueError: tuples of input 'parameters' are of incorrect length + sage: phi.j_invariant(((1, 'x'), (2, 3, 8))) + Traceback (most recent call last): + ... + TypeError: first tuple of input 'parameter' must contain only integers + sage: phi.j_invariant(((1, 2), (2, 3, 'x'))) + Traceback (most recent call last): + ... + TypeError: second tuple of input 'parameter' must contain only integers + sage: phi.j_invariant(((1, 2), (4, 3, 7))) + Traceback (most recent call last): + ... + ValueError: input 'parameter' does not satisfy the weight-0 condition + sage: phi.j_invariant(((1, 2), (4, 3, 7)), check=False) + 1/T^13 """ r = self._gen.degree() q = self._Fq.order() @@ -1404,27 +1452,29 @@ def j_invariant(self, parameter=None, check=True): return self._gen[1]**(q+1)/self._gen[2] if isinstance(parameter, (int, Integer)): if parameter <= 0 or parameter >= r: - raise ValueError(f"input 'parameter' must be greater or " - "equal to 1 or less than the rank (={r})") + raise ValueError("input 'parameter' must be >= 1 or < the " + f"rank (={r})") dk = Integer((q**r - 1)/(q**gcd(parameter, r) - 1)) dr = Integer((q**parameter - 1)/(q**gcd(parameter, r) - 1)) return self._gen[parameter]**dk / self._gen[-1]**dr elif isinstance(parameter, tuple): if len(parameter) != 2: raise ValueError("input 'parameter' must be of length 2") - if check: - if not isinstance(parameter[0], tuple): - raise TypeError("first entry of input 'parameter' must be " - "a tuple") - if not isinstance(parameter[1], tuple): - raise TypeError("second entry of input 'parameter' must be " - "a tuple") - if not all(isinstance(p, (int, Integer)) for p in parameter[0]): - raise TypeError("first tuple of input 'parameter' must " - "contain only integers") - if not all(isinstance(p, (int, Integer)) for p in parameter[1]): - raise TypeError("second tuple of input 'parameter' must " - "contain only integers") + if not isinstance(parameter[0], tuple): + raise TypeError("first entry of input 'parameter' must be " + "a tuple") + if not isinstance(parameter[1], tuple): + raise TypeError("second entry of input 'parameter' must be " + "a tuple") + if not len(parameter[0]) < r or not len(parameter[1]) == len(parameter[0]) + 1: + raise ValueError("tuples of input 'parameters' are of incorrect length") + if not all(isinstance(p, (int, Integer)) for p in parameter[0]): + raise TypeError("first tuple of input 'parameter' must " + "contain only integers") + if not all(isinstance(p, (int, Integer)) for p in parameter[1]): + raise TypeError("second tuple of input 'parameter' must " + "contain only integers") + if not check: # check that the weight-0 condition is statisfied: # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) right = parameter[1][-1]*(q**r - 1) From fa14b43dec5ad217cef9962cf51165dbf4748f82 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 22:00:55 -0500 Subject: [PATCH 26/62] add doctests to basic_j_invariants_parameters and fix some error messages --- .../drinfeld_modules/drinfeld_module.py | 51 +++++++++++++++---- 1 file changed, 40 insertions(+), 11 deletions(-) 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 379bcb0465e..2cd41123d20 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -969,6 +969,32 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): ((2, 4), (401, 260, 11)), ... ((2, 4), (288, 39, 2))] + + TESTS:: + + sage: A = GF(5)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) + sage: phi.basic_j_invariant_parameters([1, 'x']) + Traceback (most recent call last): + ... + TypeError: the elements of the list 'coeff_indices' must be integers + sage: phi.basic_j_invariant_parameters([1, 10]) + Traceback (most recent call last): + ... + ValueError: the maximum or the minimum of the list coeff_indices must be > 0 and < 3 respectively + sage: phi.basic_j_invariant_parameters([1, 1]) + Traceback (most recent call last): + ... + ValueError: the elements of coeff_indices should be distinct and sorted + sage: phi.basic_j_invariant_parameters([2, 1]) + Traceback (most recent call last): + ... + ValueError: the elements of coeff_indices should be distinct and sorted + sage: phi.basic_j_invariant_parameters('x') + Traceback (most recent call last): + ... + TypeError: input 'coeff_indices' must be NoneType, a tuple or a list """ r = self._gen.degree() if coeff_indices is None: @@ -980,17 +1006,20 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): elif isinstance(coeff_indices, (tuple, list)): # check if coeff_indices is valid coeff_indices = list(coeff_indices) if not all(isinstance(k, (int, Integer)) for k in coeff_indices): - raise TypeError("the elements of the list coeff_indices must be integers") + raise TypeError("the elements of the list 'coeff_indices' " + "must be integers") if max(coeff_indices) >= r or min(coeff_indices) <= 0: - raise ValueError(f"the maximum or the minimum of the list" \ - "coeff_indices must be > 0 and < {r} respectively") - if not all(coeff_indices[i] < coeff_indices[i+1] for i in range(len(coeff_indices) - 1)): - raise ValueError(f"the elements of coeff_indices should be distinct" \ - "and sorted") + raise ValueError("the maximum or the minimum of the list " + f"coeff_indices must be > 0 and < {r} respectively") + if not all(coeff_indices[i] < coeff_indices[i+1] for i in + range(len(coeff_indices) - 1)): + raise ValueError("the elements of coeff_indices should be " + "distinct and sorted") if nonzero: coeff_indices = [k for k in coeff_indices if self._gen[k]] else: - raise TypeError("input coeff_indices must be None, a tuple or a list") + raise TypeError("input 'coeff_indices' must be NoneType, a tuple " + "or a list") # Create the equation and inequalities for the polyhedron: q = self._Fq.order() equation = [0] @@ -1403,7 +1432,7 @@ def j_invariant(self, parameter=None, check=True): sage: phi.j_invariant() Traceback (most recent call last): ... - ValueError: input 'parameter' must be different from None if the rank is greater than 2 + TypeError: input 'parameter' must be different from NoneType if the rank is greater than 2 sage: phi.j_invariant(-1) Traceback (most recent call last): ... @@ -1447,8 +1476,8 @@ def j_invariant(self, parameter=None, check=True): q = self._Fq.order() if parameter is None: if r != 2: - raise ValueError("input 'parameter' must be different from " - "None if the rank is greater than 2") + raise TypeError("input 'parameter' must be different from " + "NoneType if the rank is greater than 2") return self._gen[1]**(q+1)/self._gen[2] if isinstance(parameter, (int, Integer)): if parameter <= 0 or parameter >= r: @@ -1474,7 +1503,7 @@ def j_invariant(self, parameter=None, check=True): if not all(isinstance(p, (int, Integer)) for p in parameter[1]): raise TypeError("second tuple of input 'parameter' must " "contain only integers") - if not check: + if check: # check that the weight-0 condition is statisfied: # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) right = parameter[1][-1]*(q**r - 1) From b4b7b983c190872160f01f4a4fe909323714f9fb Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Mon, 6 Mar 2023 22:37:46 -0500 Subject: [PATCH 27/62] add authors name --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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 2cd41123d20..f501742b100 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -10,8 +10,9 @@ AUTHORS: -- Antoine Leudière (2022-04) -- Xavier Caruso (2022-06) +- Antoine Leudière (2022-04): initial version +- Xavier Caruso (2022-06): initial version +- David Ayotte (2023-03): added basic `j`-invariants """ # ***************************************************************************** From 7a9ff2ee7d6be68d2e736f534fc07d2c2ee718a7 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 7 Mar 2023 00:56:05 -0500 Subject: [PATCH 28/62] fix some documentation mistakes for j_invariant method --- .../drinfeld_modules/drinfeld_module.py | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) 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 f501742b100..cd439086304 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1316,16 +1316,16 @@ def j_invariant(self, parameter=None, check=True): Return the `j`-invariant of the Drinfeld `\mathbb{F}_q[T]`-module for the given parameter. - Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r`. Then then - `[[k_1, \ldots, k_n], [d_1, \ldots, d_n, d_r]]`-`j`-invariant of `\phi` + Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r`. Then the + *`((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r)`-`j`-invariant* of `\phi` is defined by .. MATH:: - j_k(\phi) := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} + j_{k_1, \ldots, k_n}^{}(\phi)^{d_1, \ldots, d_n, d_r} := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} - where `1\leq k_i \leq r - 1` and the integers `d_i` satisfies the - *weight-0 condition*: + where `1\leq k_1 < k_2 < \ldots < k_n \leq r - 1` and the integers `d_i` + satisfies the *weight-0 condition*: .. MATH:: @@ -1343,22 +1343,22 @@ def j_invariant(self, parameter=None, check=True): INPUT: - - ``parameter`` (integer or list, default: None) -- this parameter is - either a list of two lists or an integer between 1 and `r-1` (`r` is + - ``parameter`` (integer or tuple, default: None) -- this parameter is + either a tuple of two tuples or an integer between 1 and `r-1` (`r` is the rank). - - If ``parameter`` is a list of two lists, then it must be of the - form `[[k_1, k_2, \ldots, k_n], [d_1, d_2, \ldots, d_n, d_r]]` where + - If ``parameter`` is a tuple of two tuples, then it must be of the + form `((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))` where the `k_i` and `d_i` are integers satisfying the weight-0 condition described above. In this case the method returns the associated `j`-invariant; - If ``parameter`` is an integer `k` then the method returns - the ``j``-invariant associated to the parameter `[[k], [d_k, d_r]]`; + the ``j``-invariant associated to the parameter `((k,), (d_k, d_r))`; - If ``parameter`` is ``None`` and the rank of the Drinfeld module is 2, then the method returns its usual `j`-invariant, that is the - `j`-invariant for the parameter `[[1], [q+1, 1]]`. + `j`-invariant for the parameter `((1,), (q+1, 1))`. - ``check`` (bool, default: ``True``) -- if this flag is set to ``False`` then the code will not check if the given parameter is valid @@ -1369,7 +1369,7 @@ def j_invariant(self, parameter=None, check=True): REFERENCE: The notion of basic `j`-invariant was introduced by Potemine in - _[Pot1998]. + [Pot1998]_. EXAMPLES:: From c031d3e614a7b482db209ae3ba945da8a3981ab4 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 7 Mar 2023 06:28:52 -0500 Subject: [PATCH 29/62] fix the documentation of j_invariant (again) --- .../function_field/drinfeld_modules/drinfeld_module.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 cd439086304..d403367b057 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1316,13 +1316,14 @@ def j_invariant(self, parameter=None, check=True): Return the `j`-invariant of the Drinfeld `\mathbb{F}_q[T]`-module for the given parameter. - Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r`. Then the - *`((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r)`-`j`-invariant* of `\phi` + Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r` with + `g_r \neq 0`. Then the + `((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r)`-`j`*-invariant* of `\phi` is defined by .. MATH:: - j_{k_1, \ldots, k_n}^{}(\phi)^{d_1, \ldots, d_n, d_r} := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} + j_{k_1, \ldots, k_n}^{d_1, \ldots, d_n, d_r}(\phi) := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} where `1\leq k_1 < k_2 < \ldots < k_n \leq r - 1` and the integers `d_i` satisfies the *weight-0 condition*: From 5544a6d4d8ab3cd7a381fcc3bccd740b97534be7 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 7 Mar 2023 14:08:49 -0500 Subject: [PATCH 30/62] allow lists to reprensent the parameters --- .../drinfeld_modules/drinfeld_module.py | 65 ++++++++++--------- 1 file changed, 33 insertions(+), 32 deletions(-) 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 d403367b057..c4419fcebd6 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -34,6 +34,7 @@ from sage.misc.lazy_string import _LazyString from sage.misc.misc_c import prod from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ from sage.rings.polynomial.ore_polynomial_element import OrePolynomial from sage.rings.polynomial.polynomial_ring import PolynomialRing_general from sage.rings.ring_extension import RingExtension_generic @@ -1318,7 +1319,7 @@ def j_invariant(self, parameter=None, check=True): Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r` with `g_r \neq 0`. Then the - `((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r)`-`j`*-invariant* of `\phi` + `((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r))`-`j`*-invariant* of `\phi` is defined by .. MATH:: @@ -1344,15 +1345,14 @@ def j_invariant(self, parameter=None, check=True): INPUT: - - ``parameter`` (integer or tuple, default: None) -- this parameter is - either a tuple of two tuples or an integer between 1 and `r-1` (`r` is - the rank). + - ``parameter`` (integer, tuple or list, default: None) -- this + parameter is either a list, a tuple or an integer between 1 and + `r-1` (`r` is the rank). - - If ``parameter`` is a tuple of two tuples, then it must be of the - form `((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))` where + - If ``parameter`` is a list or a tuple, then it must be of the form: + `((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))` where the `k_i` and `d_i` are integers satisfying the weight-0 condition - described above. In this case the method returns the associated - `j`-invariant; + described above. - If ``parameter`` is an integer `k` then the method returns the ``j``-invariant associated to the parameter `((k,), (d_k, d_r))`; @@ -1400,7 +1400,7 @@ def j_invariant(self, parameter=None, check=True): sage: phi.j_invariant(3) (T^156 + T^155 + T^151 + T^150 + T^131 + T^130 + T^126 + T^125 + T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1)/T^93 - :: + The parameter can either be a tuple or a list:: sage: Fq. = GF(7) sage: A. = Fq[] @@ -1409,9 +1409,9 @@ def j_invariant(self, parameter=None, check=True): 5 sage: J == (phi.coefficient(1)**267)*(phi.coefficient(3)**269)/(phi.coefficient(4)**39) True - sage: phi.j_invariant(((3,), (400, 57))) + sage: phi.j_invariant([[3], [400, 57]]) 4 - sage: phi.j_invariant(((3,), (400, 57))) == phi.j_invariant(3) + sage: phi.j_invariant([[3], [400, 57]]) == phi.j_invariant(3) True The list of all basic `j`-invariant parameters can be retrieved using @@ -1442,7 +1442,7 @@ def j_invariant(self, parameter=None, check=True): sage: phi.j_invariant('x') Traceback (most recent call last): ... - TypeError: input 'parameter' must be a tuple of length 2 or an integer + TypeError: input 'parameter' must be a tuple or a list of length 2 or an integer sage: phi.j_invariant((1, 2, 3)) Traceback (most recent call last): ... @@ -1450,23 +1450,23 @@ def j_invariant(self, parameter=None, check=True): sage: phi.j_invariant(('x', (1, 2, 3))) Traceback (most recent call last): ... - TypeError: first entry of input 'parameter' must be a tuple + TypeError: first component of input 'parameter' must be a tuple or a list sage: phi.j_invariant(((1, 2), 'x')) Traceback (most recent call last): ... - TypeError: second entry of input 'parameter' must be a tuple + TypeError: second component of input 'parameter' must be a tuple or a list sage: phi.j_invariant(((1, 2, 3, 4, 5), (2, 1))) Traceback (most recent call last): ... - ValueError: tuples of input 'parameters' are of incorrect length + ValueError: components of input 'parameters' are of incorrect length sage: phi.j_invariant(((1, 'x'), (2, 3, 8))) Traceback (most recent call last): ... - TypeError: first tuple of input 'parameter' must contain only integers + TypeError: first component of input 'parameter' must contain only integers sage: phi.j_invariant(((1, 2), (2, 3, 'x'))) Traceback (most recent call last): ... - TypeError: second tuple of input 'parameter' must contain only integers + TypeError: second component of input 'parameter' must contain only integers sage: phi.j_invariant(((1, 2), (4, 3, 7))) Traceback (most recent call last): ... @@ -1481,29 +1481,30 @@ def j_invariant(self, parameter=None, check=True): raise TypeError("input 'parameter' must be different from " "NoneType if the rank is greater than 2") return self._gen[1]**(q+1)/self._gen[2] - if isinstance(parameter, (int, Integer)): + if parameter in ZZ: if parameter <= 0 or parameter >= r: raise ValueError("input 'parameter' must be >= 1 or < the " f"rank (={r})") dk = Integer((q**r - 1)/(q**gcd(parameter, r) - 1)) dr = Integer((q**parameter - 1)/(q**gcd(parameter, r) - 1)) return self._gen[parameter]**dk / self._gen[-1]**dr - elif isinstance(parameter, tuple): + elif isinstance(parameter, (tuple, list)): if len(parameter) != 2: raise ValueError("input 'parameter' must be of length 2") - if not isinstance(parameter[0], tuple): - raise TypeError("first entry of input 'parameter' must be " - "a tuple") - if not isinstance(parameter[1], tuple): - raise TypeError("second entry of input 'parameter' must be " - "a tuple") + if not isinstance(parameter[0], (tuple, list)): + raise TypeError("first component of input 'parameter' must be " + "a tuple or a list") + if not isinstance(parameter[1], (tuple, list)): + raise TypeError("second component of input 'parameter' must be " + "a tuple or a list") if not len(parameter[0]) < r or not len(parameter[1]) == len(parameter[0]) + 1: - raise ValueError("tuples of input 'parameters' are of incorrect length") - if not all(isinstance(p, (int, Integer)) for p in parameter[0]): - raise TypeError("first tuple of input 'parameter' must " + raise ValueError("components of input 'parameters' are of " + "incorrect length") + if not all(p in ZZ for p in parameter[0]): + raise TypeError("first component of input 'parameter' must " "contain only integers") - if not all(isinstance(p, (int, Integer)) for p in parameter[1]): - raise TypeError("second tuple of input 'parameter' must " + if not all(p in ZZ for p in parameter[1]): + raise TypeError("second component of input 'parameter' must " "contain only integers") if check: # check that the weight-0 condition is statisfied: @@ -1515,8 +1516,8 @@ def j_invariant(self, parameter=None, check=True): raise ValueError("input 'parameter' does not satisfy the " "weight-0 condition") else: - raise TypeError("input 'parameter' must be a tuple of length 2 or " - "an integer") + raise TypeError("input 'parameter' must be a tuple or a list of " + "length 2 or an integer") num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) return num/(self._gen[-1]**parameter[1][-1]) From 4c2d37e34d9bc5aae47d56e72e73a35444c1a24e Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 15 Mar 2023 05:29:18 -0400 Subject: [PATCH 31/62] fix some pep8 line lenghts --- .../drinfeld_modules/drinfeld_module.py | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) 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 c4419fcebd6..4d863e04f44 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -899,9 +899,9 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): - ``coeff_indices`` (list or tuple, default: ``None``) -- a list or a tuple of indices for the coefficients of the Drinfeld module. If - this option is not ``None``, then the method will return only the basic - `j`-invariant parameters that involves the given indices. By default, - the method will use all the coefficients. + this option is not ``None``, then the method will return only the + basic `j`-invariant parameters that involves the given indices. By + default, the method will use all the coefficients. - ``nonzero`` (bool, Default: ``False``) -- setting this to ``True`` will return only the parameters for which the basic `j`-invariant is @@ -1005,7 +1005,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): self.coefficients(sparse=False)[1:-1], start=1) if g] else: coeff_indices = list(range(1, r)) - elif isinstance(coeff_indices, (tuple, list)): # check if coeff_indices is valid + # check if coeff_indices is valid: + elif isinstance(coeff_indices, (tuple, list)): coeff_indices = list(coeff_indices) if not all(isinstance(k, (int, Integer)) for k in coeff_indices): raise TypeError("the elements of the list 'coeff_indices' " @@ -1026,17 +1027,20 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): q = self._Fq.order() equation = [0] inequalities = [] + # Create the equation: + # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) + # = d_r (q^r - 1) for idx, i in enumerate(coeff_indices): - # create the equation: - # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) equation.append(q**i - 1) - # create inequalities of the form 0 <= delta_i + # Create inequalities of the form 0 <= delta_i lower_bounds = [0]*(len(coeff_indices) + 2) lower_bounds[idx + 1] = 1 - # create inequalities of the form delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) - upper_bounds = [Integer((q**r - 1)/(q**(gcd(i, r)) - 1))] + [0]*(len(coeff_indices) + 1) + # Create inequalities of the form + # delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) + upper_bounds = [Integer((q**r - 1)/(q**(gcd(i, r)) - 1))]\ + + [0]*(len(coeff_indices) + 1) upper_bounds[idx + 1] = -1 inequalities.extend((lower_bounds, upper_bounds)) @@ -1049,7 +1053,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): # Compute its integral points integral_points = polyhedron.integral_points() - return [(tuple(coeff_indices), tuple(p)) for p in integral_points if gcd(p) == 1] + return [(tuple(coeff_indices), tuple(p)) for p in integral_points + if gcd(p) == 1] def basic_j_invariants(self): r""" @@ -1086,7 +1091,8 @@ def basic_j_invariants(self): sage: J_phi[((1, 2), (7, 4, 1))] T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3 """ - return {p: self.j_invariant(p, check=False) for p in self.basic_j_invariant_parameters()} + return {p: self.j_invariant(p, check=False) + for p in self.basic_j_invariant_parameters()} def coefficient(self, n): r""" @@ -1302,12 +1308,13 @@ def is_isomorphic(self, psi): return True # check if self and psi are patterned alike: if not all(bool(g_self) == bool(g_psi) for g_self, g_psi in\ - zip(self._gen.coefficients(sparse=False), psi._gen.coefficients(sparse=False))): + zip(self._gen.coefficients(sparse=False), + psi._gen.coefficients(sparse=False))): return False # check that all the nonzero basic j-invariants agree isom = True for p in self.basic_j_invariant_parameters(nonzero=True): - if self.j_invariant(p, check=False) != psi.j_invariant(p, check=False): + if self.j_invariant(p, check=False) != psi.j_invariant(p,check=False): isom = False break return isom @@ -1319,15 +1326,15 @@ def j_invariant(self, parameter=None, check=True): Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r` with `g_r \neq 0`. Then the - `((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r))`-`j`*-invariant* of `\phi` - is defined by + `((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r))`-`j`-*invariant* of + `\phi` is defined by .. MATH:: j_{k_1, \ldots, k_n}^{d_1, \ldots, d_n, d_r}(\phi) := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} - where `1\leq k_1 < k_2 < \ldots < k_n \leq r - 1` and the integers `d_i` - satisfies the *weight-0 condition*: + where `1\leqslant k_1 < k_2 < \ldots < k_n \leqslant r - 1` and the + integers `d_i` satisfy the *weight-0 condition*: .. MATH:: @@ -1340,30 +1347,30 @@ def j_invariant(self, parameter=None, check=True): 0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), \quad 1 \leq i \leq n, then the `j`-invariant is called *basic*. See the method - :meth:`basic_j_invariant_parameters` for computing the list of all basic - `j`-invariant parameters. + :meth:`basic_j_invariant_parameters` for computing the list of all + basic `j`-invariant parameters. INPUT: - - ``parameter`` (integer, tuple or list, default: None) -- this - parameter is either a list, a tuple or an integer between 1 and - `r-1` (`r` is the rank). + - ``parameter`` (tuple or list, integer or NoneType; default: + ``None``): - If ``parameter`` is a list or a tuple, then it must be of the form: - `((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))` where + `((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))`, where the `k_i` and `d_i` are integers satisfying the weight-0 condition described above. - If ``parameter`` is an integer `k` then the method returns - the ``j``-invariant associated to the parameter `((k,), (d_k, d_r))`; + the ``j``-invariant associated to the parameter + `((k,), (d_k, d_r))`; - If ``parameter`` is ``None`` and the rank of the Drinfeld module is 2, then the method returns its usual `j`-invariant, that is the `j`-invariant for the parameter `((1,), (q+1, 1))`. - ``check`` (bool, default: ``True``) -- if this flag is set to - ``False`` then the code will not check if the given parameter is valid - and satisfy the weight-0 condition. + ``False`` then the code will not check if the given parameter is + valid and satisfy the weight-0 condition. OUTPUT: the `j`-invariant of ``self`` for the given parameter. @@ -1495,9 +1502,10 @@ def j_invariant(self, parameter=None, check=True): raise TypeError("first component of input 'parameter' must be " "a tuple or a list") if not isinstance(parameter[1], (tuple, list)): - raise TypeError("second component of input 'parameter' must be " - "a tuple or a list") - if not len(parameter[0]) < r or not len(parameter[1]) == len(parameter[0]) + 1: + raise TypeError("second component of input 'parameter' must " + "be a tuple or a list") + if not len(parameter[0]) < r or\ + not len(parameter[1]) == len(parameter[0]) + 1: raise ValueError("components of input 'parameters' are of " "incorrect length") if not all(p in ZZ for p in parameter[0]): @@ -1506,9 +1514,9 @@ def j_invariant(self, parameter=None, check=True): if not all(p in ZZ for p in parameter[1]): raise TypeError("second component of input 'parameter' must " "contain only integers") + # check that the weight-0 condition is statisfied: + # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) if check: - # check that the weight-0 condition is statisfied: - # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) right = parameter[1][-1]*(q**r - 1) left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in range(len(parameter[0]))) @@ -1518,7 +1526,8 @@ def j_invariant(self, parameter=None, check=True): else: raise TypeError("input 'parameter' must be a tuple or a list of " "length 2 or an integer") - num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) + num = prod(self._gen[k]**d + for k, d in zip(parameter[0], parameter[1][:-1])) return num/(self._gen[-1]**parameter[1][-1]) def morphism(self): From a85b044a9bb5e0575a61a1416c573a2ac83e3751 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 15 Mar 2023 05:34:46 -0400 Subject: [PATCH 32/62] fix some comments lines --- .../function_field/drinfeld_modules/drinfeld_module.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 4d863e04f44..5121132834e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1005,7 +1005,7 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): self.coefficients(sparse=False)[1:-1], start=1) if g] else: coeff_indices = list(range(1, r)) - # check if coeff_indices is valid: + # Check if coeff_indices is valid: elif isinstance(coeff_indices, (tuple, list)): coeff_indices = list(coeff_indices) if not all(isinstance(k, (int, Integer)) for k in coeff_indices): @@ -1299,19 +1299,19 @@ def is_isomorphic(self, psi): sage: phi.is_isomorphic(psi) False """ - # trivial checks: + # Trivial checks: if self._gen == psi._gen: return True if self._gen.degree() != psi._gen.degree(): return False if self._gen.degree() == 1: return True - # check if self and psi are patterned alike: + # Check if self and psi are patterned alike: if not all(bool(g_self) == bool(g_psi) for g_self, g_psi in\ zip(self._gen.coefficients(sparse=False), psi._gen.coefficients(sparse=False))): return False - # check that all the nonzero basic j-invariants agree + # Check that all the nonzero basic j-invariants agree isom = True for p in self.basic_j_invariant_parameters(nonzero=True): if self.j_invariant(p, check=False) != psi.j_invariant(p,check=False): @@ -1514,7 +1514,7 @@ def j_invariant(self, parameter=None, check=True): if not all(p in ZZ for p in parameter[1]): raise TypeError("second component of input 'parameter' must " "contain only integers") - # check that the weight-0 condition is statisfied: + # Check that the weight-0 condition is satisfied: # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) if check: right = parameter[1][-1]*(q**r - 1) From b0465d4ac6474fcbae9682a00d073b170dfd06c0 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 15 Mar 2023 05:36:37 -0400 Subject: [PATCH 33/62] fix typos in basic_j_invariant doc --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 5121132834e..1f140bbd5bd 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1058,8 +1058,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): def basic_j_invariants(self): r""" - Return a dictionnary whose keys are the basic `j`-invariants parameters - and values are the associated `j`-invariant. + Return a dictionary whose keys are the basic `j`-invariants parameters + and values are the corresponding `j`-invariant. See the method :meth:`j_invariant` for the definition of the basic `j`-invariants. From d91fb60a3ea4545b4b645b07856d6c6c6c919323 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 15 Mar 2023 05:55:16 -0400 Subject: [PATCH 34/62] fix docstring line lengths --- .../drinfeld_modules/drinfeld_module.py | 111 ++++++++++-------- 1 file changed, 61 insertions(+), 50 deletions(-) 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 1f140bbd5bd..9730b964e0c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -171,7 +171,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): sage: rho(T) == rho_T True - Images under the Drinfeld module are computed by calling the object:: + Images under the Drinfeld module are computed by calling the + object:: sage: phi(T) # phi_T, the generator of the Drinfeld module t^2 + t + z @@ -200,7 +201,8 @@ class DrinfeldModule(Parent, UniqueRepresentation): .. RUBRIC:: The base field of a Drinfeld module - The base field of the Drinfeld module is retrieved using :meth:`base`:: + The base field of the Drinfeld module is retrieved using + :meth:`base`:: sage: phi.base() Finite Field in z of size 3^12 over its base @@ -213,8 +215,9 @@ class DrinfeldModule(Parent, UniqueRepresentation): To: Finite Field in z of size 3^12 over its base 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 + 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 @@ -892,28 +895,30 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): r""" Return the list of basic `j`-invariant parameters. - See the method :meth:`j_invariant` for the definition of the basic - `j`-invariant parameters. + See the method :meth:`j_invariant` for the definition of the + basic `j`-invariant parameters. INPUT: - - ``coeff_indices`` (list or tuple, default: ``None``) -- a list or a - tuple of indices for the coefficients of the Drinfeld module. If - this option is not ``None``, then the method will return only the - basic `j`-invariant parameters that involves the given indices. By - default, the method will use all the coefficients. + - ``coeff_indices`` (list or tuple, default: ``None``) -- a list + or a tuple of indices for the coefficients of the Drinfeld + module. If this option is not ``None``, then the method will + return only the basic `j`-invariant parameters that involves + the given indices. By default, the method will use all the + coefficients. - - ``nonzero`` (bool, Default: ``False``) -- setting this to ``True`` - will return only the parameters for which the basic `j`-invariant is - nonzero + - ``nonzero`` (bool, Default: ``False``) -- setting this to + ``True`` will return only the parameters for which the basic + `j`-invariant is nonzero. .. WARNING:: - The usage of this method can be very computationally expensive - depending if the given Drinfeld module is of rank strictly greater - 4 and for large value of `q`. Setting the ``nonzero`` flag to - ``True`` can speed up the computation considerably if the Drinfeld - module possesses multiple zero coefficients. + The usage of this method can be very computationally + expensive depending if the given Drinfeld module is of rank + strictly greater 4 and for large value of `q`. Setting the + ``nonzero`` flag to ``True`` can speed up the computation + considerably if the Drinfeld module possesses multiple zero + coefficients. EXAMPLES:: @@ -942,8 +947,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): ((1, 2), (12, 29, 6)), ((1, 2), (31, 31, 7))] - If a list of indices is given, then only the parameters involving those - coefficients will be computed:: + If a list of indices is given, then only the parameters + involving those coefficients will be computed:: sage: A = GF(3)['T'] sage: K. = Frac(A) @@ -957,8 +962,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): ... ((1, 5), (146, 74, 25))] - Use ``nonzero=True`` to speed up the computations for Drinfeld modules - for which some coefficients are zero:: + Use ``nonzero=True`` to speed up the computations for Drinfeld + modules for which some coefficients are zero:: sage: A = GF(5)['T'] sage: K. = Frac(A) @@ -1047,7 +1052,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): equation.append(1 - q**r) - # Create the polyhedron defined by the equation and the inequalities. + # Create the polyhedron defined by the equation and the + # inequalities. polyhedron = Polyhedron(ieqs=inequalities, eqns=[equation]) # Compute its integral points @@ -1058,11 +1064,11 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): def basic_j_invariants(self): r""" - Return a dictionary whose keys are the basic `j`-invariants parameters - and values are the corresponding `j`-invariant. + Return a dictionary whose keys are the basic `j`-invariants + parameters and values are the corresponding `j`-invariant. - See the method :meth:`j_invariant` for the definition of the basic - `j`-invariants. + See the method :meth:`j_invariant` for the definition of the + basic `j`-invariants. EXAMPLES:: @@ -1321,56 +1327,61 @@ def is_isomorphic(self, psi): def j_invariant(self, parameter=None, check=True): r""" - Return the `j`-invariant of the Drinfeld `\mathbb{F}_q[T]`-module for - the given parameter. + Return the `j`-invariant of the Drinfeld + `\mathbb{F}_q[T]`-module for the given parameter. Suppose that `\phi_T = g_0 + g_1\tau + \cdots + g_r \tau^r` with `g_r \neq 0`. Then the - `((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r))`-`j`-*invariant* of - `\phi` is defined by + `((k_1, \ldots, k_n), (d_1, \ldots, d_n, d_r))`-`j`-*invariant* + of `\phi` is defined by .. MATH:: - j_{k_1, \ldots, k_n}^{d_1, \ldots, d_n, d_r}(\phi) := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} + j_{k_1, \ldots, k_n}^{d_1, \ldots, d_n, d_r}(\phi) + := \frac{1}{g_r^{d_q}}\prod_{i = 1}^n g_{k_i}^{d_i} - where `1\leqslant k_1 < k_2 < \ldots < k_n \leqslant r - 1` and the - integers `d_i` satisfy the *weight-0 condition*: + where `1\leqslant k_1 < k_2 < \ldots < k_n \leqslant r - 1` and + the integers `d_i` satisfy the *weight-0 condition*: .. MATH:: - d_1 (q^{k_1} - 1) + d_2 (q^{k_2} - 1) + ... + d_{n} (q^{k_n} - 1) = d_r (q^r - 1). + d_1 (q^{k_1} - 1) + d_2 (q^{k_2} - 1) + + \cdots + d_{n} (q^{k_n} - 1) = d_r (q^r - 1). Furthermore, if `\gcd(d_1,\ldots, d_n, d_r) = 1` and .. MATH:: - 0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), \quad 1 \leq i \leq n, + 0 \leq d_i \leq (q^r - 1)/(q^{\gcd(i, r)} - 1), + \quad 1 \leq i \leq n, then the `j`-invariant is called *basic*. See the method - :meth:`basic_j_invariant_parameters` for computing the list of all - basic `j`-invariant parameters. + :meth:`basic_j_invariant_parameters` for computing the list of + all basic `j`-invariant parameters. INPUT: - ``parameter`` (tuple or list, integer or NoneType; default: ``None``): - - If ``parameter`` is a list or a tuple, then it must be of the form: - `((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))`, where - the `k_i` and `d_i` are integers satisfying the weight-0 condition - described above. + - If ``parameter`` is a list or a tuple, then it must be of + the form: + `((k_1, k_2, \ldots, k_n), (d_1, d_2, \ldots, d_n, d_r))`, + where the `k_i` and `d_i` are integers satisfying the + weight-0 condition described above. - If ``parameter`` is an integer `k` then the method returns the ``j``-invariant associated to the parameter `((k,), (d_k, d_r))`; - - If ``parameter`` is ``None`` and the rank of the Drinfeld module is - 2, then the method returns its usual `j`-invariant, that is the - `j`-invariant for the parameter `((1,), (q+1, 1))`. + - If ``parameter`` is ``None`` and the rank of the Drinfeld + module is 2, then the method returns its usual + `j`-invariant, that is the `j`-invariant for the parameter + `((1,), (q+1, 1))`. - ``check`` (bool, default: ``True``) -- if this flag is set to - ``False`` then the code will not check if the given parameter is - valid and satisfy the weight-0 condition. + ``False`` then the code will not check if the given parameter + is valid and satisfy the weight-0 condition. OUTPUT: the `j`-invariant of ``self`` for the given parameter. @@ -1421,8 +1432,8 @@ def j_invariant(self, parameter=None, check=True): sage: phi.j_invariant([[3], [400, 57]]) == phi.j_invariant(3) True - The list of all basic `j`-invariant parameters can be retrieved using - the method :meth:`basic_j_invariant_parameters`:: + The list of all basic `j`-invariant parameters can be retrieved + using the method :meth:`basic_j_invariant_parameters`:: sage: A = GF(3)['T'] sage: K. = Frac(A) From 1a7bef4ed239709f108fd8fc3870c3d00e4948db Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 15 Mar 2023 06:52:15 -0400 Subject: [PATCH 35/62] fix some details --- .../drinfeld_modules/drinfeld_module.py | 44 +++++++++---------- 1 file changed, 21 insertions(+), 23 deletions(-) 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 9730b964e0c..e40089af613 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -900,25 +900,23 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): INPUT: - - ``coeff_indices`` (list or tuple, default: ``None``) -- a list - or a tuple of indices for the coefficients of the Drinfeld - module. If this option is not ``None``, then the method will - return only the basic `j`-invariant parameters that involves - the given indices. By default, the method will use all the - coefficients. + - ``coeff_indices`` (list or tuple, or NoneType; default: + ``None``) -- indices of the Drinfeld module generator + coefficients to be considered in the computation. If the + parameter is ``None`` (default), all the coefficients are + involved. - - ``nonzero`` (bool, Default: ``False``) -- setting this to - ``True`` will return only the parameters for which the basic - `j`-invariant is nonzero. + - ``nonzero`` (boolean, default: ``False``) -- if this flag + is set to ``True``, then only the parameters for which the + corresponding basic `j`-invariant is nonzero are returned. .. WARNING:: The usage of this method can be very computationally - expensive depending if the given Drinfeld module is of rank - strictly greater 4 and for large value of `q`. Setting the - ``nonzero`` flag to ``True`` can speed up the computation - considerably if the Drinfeld module possesses multiple zero - coefficients. + expensive. This can happen if the rank is greater than four, + or if `q` is large. Setting the ``nonzero`` flag to ``True`` + can speed up the computation considerably if the Drinfeld + module generator possesses multiple zero coefficients. EXAMPLES:: @@ -947,8 +945,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): ((1, 2), (12, 29, 6)), ((1, 2), (31, 31, 7))] - If a list of indices is given, then only the parameters - involving those coefficients will be computed:: + Specify a list of indices to restrict which coefficients appear + in the parameters:: sage: A = GF(3)['T'] sage: K. = Frac(A) @@ -963,7 +961,7 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): ((1, 5), (146, 74, 25))] Use ``nonzero=True`` to speed up the computations for Drinfeld - modules for which some coefficients are zero:: + modules having multiple zero coefficients:: sage: A = GF(5)['T'] sage: K. = Frac(A) @@ -1039,13 +1037,13 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): equation.append(q**i - 1) # Create inequalities of the form 0 <= delta_i - lower_bounds = [0]*(len(coeff_indices) + 2) + lower_bounds = [0] * (len(coeff_indices) + 2) lower_bounds[idx + 1] = 1 # Create inequalities of the form # delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) - upper_bounds = [Integer((q**r - 1)/(q**(gcd(i, r)) - 1))]\ - + [0]*(len(coeff_indices) + 1) + upper_bounds = [Integer((q**r - 1) / (q**(gcd(i, r)) - 1))]\ + + [0]*(len(coeff_indices) + 1) upper_bounds[idx + 1] = -1 inequalities.extend((lower_bounds, upper_bounds)) @@ -1097,8 +1095,8 @@ def basic_j_invariants(self): sage: J_phi[((1, 2), (7, 4, 1))] T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3 """ - return {p: self.j_invariant(p, check=False) - for p in self.basic_j_invariant_parameters()} + return {parameter: self.j_invariant(p, check=False) + for parameter in self.basic_j_invariant_parameters()} def coefficient(self, n): r""" @@ -1530,7 +1528,7 @@ def j_invariant(self, parameter=None, check=True): if check: right = parameter[1][-1]*(q**r - 1) left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in - range(len(parameter[0]))) + range(len(parameter[0]))) if left != right: raise ValueError("input 'parameter' does not satisfy the " "weight-0 condition") From e3eb1a530e2c6aff136d2086087e0f248bedff12 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 15 Mar 2023 08:11:13 -0400 Subject: [PATCH 36/62] fix some error messages --- .../drinfeld_modules/drinfeld_module.py | 77 +++++++++---------- 1 file changed, 35 insertions(+), 42 deletions(-) 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 e40089af613..a12709ccfb0 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -983,23 +983,23 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): sage: phi.basic_j_invariant_parameters([1, 'x']) Traceback (most recent call last): ... - TypeError: the elements of the list 'coeff_indices' must be integers + TypeError: coefficients indices must be integers sage: phi.basic_j_invariant_parameters([1, 10]) Traceback (most recent call last): ... - ValueError: the maximum or the minimum of the list coeff_indices must be > 0 and < 3 respectively + ValueError: indices must be > 0 and < 3 sage: phi.basic_j_invariant_parameters([1, 1]) Traceback (most recent call last): ... - ValueError: the elements of coeff_indices should be distinct and sorted + ValueError: indices must be distinct and sorted sage: phi.basic_j_invariant_parameters([2, 1]) Traceback (most recent call last): ... - ValueError: the elements of coeff_indices should be distinct and sorted + ValueError: indices must be distinct and sorted sage: phi.basic_j_invariant_parameters('x') Traceback (most recent call last): ... - TypeError: input 'coeff_indices' must be NoneType, a tuple or a list + TypeError: indices must be None, a tuple or a list """ r = self._gen.degree() if coeff_indices is None: @@ -1012,20 +1012,16 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): elif isinstance(coeff_indices, (tuple, list)): coeff_indices = list(coeff_indices) if not all(isinstance(k, (int, Integer)) for k in coeff_indices): - raise TypeError("the elements of the list 'coeff_indices' " - "must be integers") + raise TypeError('coefficients indices must be integers') if max(coeff_indices) >= r or min(coeff_indices) <= 0: - raise ValueError("the maximum or the minimum of the list " - f"coeff_indices must be > 0 and < {r} respectively") + raise ValueError(f'indices must be > 0 and < {r}') if not all(coeff_indices[i] < coeff_indices[i+1] for i in range(len(coeff_indices) - 1)): - raise ValueError("the elements of coeff_indices should be " - "distinct and sorted") + raise ValueError('indices must be distinct and sorted') if nonzero: coeff_indices = [k for k in coeff_indices if self._gen[k]] else: - raise TypeError("input 'coeff_indices' must be NoneType, a tuple " - "or a list") + raise TypeError('indices must be None, a tuple or a list') # Create the equation and inequalities for the polyhedron: q = self._Fq.order() equation = [0] @@ -1095,7 +1091,7 @@ def basic_j_invariants(self): sage: J_phi[((1, 2), (7, 4, 1))] T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3 """ - return {parameter: self.j_invariant(p, check=False) + return {parameter: self.j_invariant(parameter, check=False) for parameter in self.basic_j_invariant_parameters()} def coefficient(self, n): @@ -1450,43 +1446,43 @@ def j_invariant(self, parameter=None, check=True): sage: phi.j_invariant() Traceback (most recent call last): ... - TypeError: input 'parameter' must be different from NoneType if the rank is greater than 2 + TypeError: parameter must be different from NoneType if the rank is greater than 2 sage: phi.j_invariant(-1) Traceback (most recent call last): ... - ValueError: input 'parameter' must be >= 1 or < the rank (=4) + ValueError: integer parameter must be >= 1 and < the rank (=4) sage: phi.j_invariant('x') Traceback (most recent call last): ... - TypeError: input 'parameter' must be a tuple or a list of length 2 or an integer + TypeError: parameter must be a tuple or a list of length 2 or an integer sage: phi.j_invariant((1, 2, 3)) Traceback (most recent call last): ... - ValueError: input 'parameter' must be of length 2 + ValueError: list or tuple parameter must be of length 2 sage: phi.j_invariant(('x', (1, 2, 3))) Traceback (most recent call last): ... - TypeError: first component of input 'parameter' must be a tuple or a list + TypeError: list or tuple parameter must contain tuples or lists sage: phi.j_invariant(((1, 2), 'x')) Traceback (most recent call last): ... - TypeError: second component of input 'parameter' must be a tuple or a list + TypeError: list or tuple parameter must contain tuples or lists sage: phi.j_invariant(((1, 2, 3, 4, 5), (2, 1))) Traceback (most recent call last): ... - ValueError: components of input 'parameters' are of incorrect length + ValueError: components of tuple or list parameter have incorrect length sage: phi.j_invariant(((1, 'x'), (2, 3, 8))) Traceback (most recent call last): ... - TypeError: first component of input 'parameter' must contain only integers + TypeError: components of tuple or list parameter must contain only integers sage: phi.j_invariant(((1, 2), (2, 3, 'x'))) Traceback (most recent call last): ... - TypeError: second component of input 'parameter' must contain only integers + TypeError: components of tuple or list parameter must contain only integers sage: phi.j_invariant(((1, 2), (4, 3, 7))) Traceback (most recent call last): ... - ValueError: input 'parameter' does not satisfy the weight-0 condition + ValueError: parameter does not satisfy the weight-0 condition sage: phi.j_invariant(((1, 2), (4, 3, 7)), check=False) 1/T^13 """ @@ -1494,46 +1490,43 @@ def j_invariant(self, parameter=None, check=True): q = self._Fq.order() if parameter is None: if r != 2: - raise TypeError("input 'parameter' must be different from " + raise TypeError("parameter must be different from " "NoneType if the rank is greater than 2") return self._gen[1]**(q+1)/self._gen[2] if parameter in ZZ: if parameter <= 0 or parameter >= r: - raise ValueError("input 'parameter' must be >= 1 or < the " + raise ValueError("integer parameter must be >= 1 and < the " f"rank (={r})") dk = Integer((q**r - 1)/(q**gcd(parameter, r) - 1)) dr = Integer((q**parameter - 1)/(q**gcd(parameter, r) - 1)) return self._gen[parameter]**dk / self._gen[-1]**dr elif isinstance(parameter, (tuple, list)): if len(parameter) != 2: - raise ValueError("input 'parameter' must be of length 2") - if not isinstance(parameter[0], (tuple, list)): - raise TypeError("first component of input 'parameter' must be " - "a tuple or a list") - if not isinstance(parameter[1], (tuple, list)): - raise TypeError("second component of input 'parameter' must " - "be a tuple or a list") + raise ValueError("list or tuple parameter must be of length 2") + if not isinstance(parameter[0], (tuple, list)) \ + or not isinstance(parameter[1], (tuple, list)): + raise TypeError("list or tuple parameter must contain tuples " + "or lists") if not len(parameter[0]) < r or\ not len(parameter[1]) == len(parameter[0]) + 1: - raise ValueError("components of input 'parameters' are of " + raise ValueError("components of tuple or list parameter have " "incorrect length") - if not all(p in ZZ for p in parameter[0]): - raise TypeError("first component of input 'parameter' must " - "contain only integers") - if not all(p in ZZ for p in parameter[1]): - raise TypeError("second component of input 'parameter' must " + if not all(p in ZZ for p in parameter[0])\ + or not all(p in ZZ for p in parameter[1]): + raise TypeError("components of tuple or list parameter must " "contain only integers") # Check that the weight-0 condition is satisfied: - # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) = d_r (q^r - 1) + # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) + # = d_r (q^r - 1) if check: right = parameter[1][-1]*(q**r - 1) left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in range(len(parameter[0]))) if left != right: - raise ValueError("input 'parameter' does not satisfy the " + raise ValueError("parameter does not satisfy the " "weight-0 condition") else: - raise TypeError("input 'parameter' must be a tuple or a list of " + raise TypeError("parameter must be a tuple or a list of " "length 2 or an integer") num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) From a173c5736f4bf7a0505d59274bfa6c6c1083c5e0 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 15 Mar 2023 08:22:13 -0400 Subject: [PATCH 37/62] minor change to doc --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a12709ccfb0..c9ee23312b7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1356,7 +1356,7 @@ def j_invariant(self, parameter=None, check=True): INPUT: - ``parameter`` (tuple or list, integer or NoneType; default: - ``None``): + ``None``) -- the `j`-invariant parameter: - If ``parameter`` is a list or a tuple, then it must be of the form: From b000ddb5cea58ed2d5b56fbbff309641051da126 Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Thu, 16 Mar 2023 09:24:52 -0400 Subject: [PATCH 38/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c9ee23312b7..195acdf4abb 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1524,7 +1524,7 @@ def j_invariant(self, parameter=None, check=True): range(len(parameter[0]))) if left != right: raise ValueError("parameter does not satisfy the " - "weight-0 condition") + "weight-0 condition") else: raise TypeError("parameter must be a tuple or a list of " "length 2 or an integer") From 9638eaa560a658b305b7037a6631c7fd3eddc878 Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Thu, 16 Mar 2023 09:51:45 -0400 Subject: [PATCH 39/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) 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 195acdf4abb..f9e1d762ed8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -895,8 +895,7 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): r""" Return the list of basic `j`-invariant parameters. - See the method :meth:`j_invariant` for the definition of the - basic `j`-invariant parameters. + See the method :meth:`j_invariant` for definitions. INPUT: From ad447e03a52b6f3c5c533ac47ad1e3695d6533e6 Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:23:51 -0400 Subject: [PATCH 40/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 f9e1d762ed8..090aa68b5e4 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1057,7 +1057,7 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): def basic_j_invariants(self): r""" - Return a dictionary whose keys are the basic `j`-invariants + Return a dictionary whose keys are all the basic `j`-invariants parameters and values are the corresponding `j`-invariant. See the method :meth:`j_invariant` for the definition of the From 71d000e14c8f70fe83bf8122484e2b6a905335ad Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:27:28 -0400 Subject: [PATCH 41/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 090aa68b5e4..765210d191c 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1273,8 +1273,8 @@ def is_finite(self): def is_isomorphic(self, psi): r""" - Return ``True`` whether ``self`` is isomorphic to the Drinfeld module - `\psi`. + Return ``True`` whether ``self`` is isomorphic to the other + Drinfeld module EXAMPLES:: From dc90be1e59c0d6ec6752440fe997c248aa6a91ce Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:30:43 -0400 Subject: [PATCH 42/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 765210d191c..3da336843c8 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1285,8 +1285,8 @@ def is_isomorphic(self, psi): sage: psi = DrinfeldModule(A, [T, c^4*T^3, c^(24)*T^9, c^(124)*T]) sage: phi.is_isomorphic(psi) True - sage: phi0 = DrinfeldModule(A, [T, 1, 1, 1]) - sage: phi.is_isomorphic(phi0) + sage: rho = DrinfeldModule(A, [T, 1, 1, 1]) + sage: phi.is_isomorphic(rho) False :: From 84d09bf0a53baa70be2bfd4d448f1e8451388450 Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:34:37 -0400 Subject: [PATCH 43/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3da336843c8..eb39afd2bbc 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1529,7 +1529,7 @@ def j_invariant(self, parameter=None, check=True): "length 2 or an integer") num = prod(self._gen[k]**d for k, d in zip(parameter[0], parameter[1][:-1])) - return num/(self._gen[-1]**parameter[1][-1]) + return num / (self._gen[-1]**parameter[1][-1]) def morphism(self): r""" From 6d3832956f92f313e566d40af069af4dc1bac153 Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:36:51 -0400 Subject: [PATCH 44/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 eb39afd2bbc..a6fa4b1212e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1515,8 +1515,8 @@ def j_invariant(self, parameter=None, check=True): raise TypeError("components of tuple or list parameter must " "contain only integers") # Check that the weight-0 condition is satisfied: - # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) - # = d_r (q^r - 1) + # d_1 (q - 1) + ... + d_{r-1} (q^{r-1} - 1) + # = d_r (q^r - 1) if check: right = parameter[1][-1]*(q**r - 1) left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in From 40310ecba9800ef490a7157d8451eedd27e5ebfa Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Thu, 16 Mar 2023 10:37:05 -0400 Subject: [PATCH 45/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a6fa4b1212e..d30ffbd6d2d 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1026,8 +1026,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): equation = [0] inequalities = [] # Create the equation: - # d_1 (q - 1) + d_2 (q^2 - 1) + ... + d_{r-1} (q^{r-1} - 1) - # = d_r (q^r - 1) + # d_1 (q - 1) + ... + d_{r-1} (q^{r-1} - 1) + # = d_r (q^r - 1) for idx, i in enumerate(coeff_indices): equation.append(q**i - 1) From 78e3ef1a68829d3bc24c30bb08ba89914c8480cb Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Thu, 16 Mar 2023 11:11:12 -0400 Subject: [PATCH 46/62] minor fixes --- .../drinfeld_modules/drinfeld_module.py | 44 ++++++++++--------- 1 file changed, 24 insertions(+), 20 deletions(-) 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 d30ffbd6d2d..954527b4c8f 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -911,8 +911,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): .. WARNING:: - The usage of this method can be very computationally - expensive. This can happen if the rank is greater than four, + The usage of this method can be computationally + expensive e.g. if the rank is greater than four, or if `q` is large. Setting the ``nonzero`` flag to ``True`` can speed up the computation considerably if the Drinfeld module generator possesses multiple zero coefficients. @@ -983,18 +983,22 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): Traceback (most recent call last): ... TypeError: coefficients indices must be integers + sage: phi.basic_j_invariant_parameters([1, 10]) Traceback (most recent call last): ... ValueError: indices must be > 0 and < 3 + sage: phi.basic_j_invariant_parameters([1, 1]) Traceback (most recent call last): ... ValueError: indices must be distinct and sorted + sage: phi.basic_j_invariant_parameters([2, 1]) Traceback (most recent call last): ... ValueError: indices must be distinct and sorted + sage: phi.basic_j_invariant_parameters('x') Traceback (most recent call last): ... @@ -1030,28 +1034,21 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): # = d_r (q^r - 1) for idx, i in enumerate(coeff_indices): equation.append(q**i - 1) - # Create inequalities of the form 0 <= delta_i lower_bounds = [0] * (len(coeff_indices) + 2) lower_bounds[idx + 1] = 1 - # Create inequalities of the form - # delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) + # delta_i <= (q^r - 1)/(q^{gcd(i,r)} - 1) upper_bounds = [Integer((q**r - 1) / (q**(gcd(i, r)) - 1))]\ + [0]*(len(coeff_indices) + 1) upper_bounds[idx + 1] = -1 - inequalities.extend((lower_bounds, upper_bounds)) - equation.append(1 - q**r) - # Create the polyhedron defined by the equation and the # inequalities. polyhedron = Polyhedron(ieqs=inequalities, eqns=[equation]) - # Compute its integral points integral_points = polyhedron.integral_points() - return [(tuple(coeff_indices), tuple(p)) for p in integral_points if gcd(p) == 1] @@ -1060,8 +1057,7 @@ def basic_j_invariants(self): Return a dictionary whose keys are all the basic `j`-invariants parameters and values are the corresponding `j`-invariant. - See the method :meth:`j_invariant` for the definition of the - basic `j`-invariants. + See the method :meth:`j_invariant` for definitions. EXAMPLES:: @@ -1311,12 +1307,10 @@ def is_isomorphic(self, psi): psi._gen.coefficients(sparse=False))): return False # Check that all the nonzero basic j-invariants agree - isom = True for p in self.basic_j_invariant_parameters(nonzero=True): - if self.j_invariant(p, check=False) != psi.j_invariant(p,check=False): - isom = False - break - return isom + if self.j_invariant(p, check=False) != psi.j_invariant(p, check=False): + return False + return True def j_invariant(self, parameter=None, check=True): r""" @@ -1445,43 +1439,53 @@ def j_invariant(self, parameter=None, check=True): sage: phi.j_invariant() Traceback (most recent call last): ... - TypeError: parameter must be different from NoneType if the rank is greater than 2 + TypeError: parameter must not be None if the rank is greater than 2 + sage: phi.j_invariant(-1) Traceback (most recent call last): ... ValueError: integer parameter must be >= 1 and < the rank (=4) + sage: phi.j_invariant('x') Traceback (most recent call last): ... TypeError: parameter must be a tuple or a list of length 2 or an integer + sage: phi.j_invariant((1, 2, 3)) Traceback (most recent call last): ... ValueError: list or tuple parameter must be of length 2 + sage: phi.j_invariant(('x', (1, 2, 3))) Traceback (most recent call last): ... TypeError: list or tuple parameter must contain tuples or lists + sage: phi.j_invariant(((1, 2), 'x')) Traceback (most recent call last): ... TypeError: list or tuple parameter must contain tuples or lists + sage: phi.j_invariant(((1, 2, 3, 4, 5), (2, 1))) Traceback (most recent call last): ... ValueError: components of tuple or list parameter have incorrect length + sage: phi.j_invariant(((1, 'x'), (2, 3, 8))) Traceback (most recent call last): ... TypeError: components of tuple or list parameter must contain only integers + sage: phi.j_invariant(((1, 2), (2, 3, 'x'))) Traceback (most recent call last): ... TypeError: components of tuple or list parameter must contain only integers + sage: phi.j_invariant(((1, 2), (4, 3, 7))) Traceback (most recent call last): ... ValueError: parameter does not satisfy the weight-0 condition + sage: phi.j_invariant(((1, 2), (4, 3, 7)), check=False) 1/T^13 """ @@ -1489,8 +1493,8 @@ def j_invariant(self, parameter=None, check=True): q = self._Fq.order() if parameter is None: if r != 2: - raise TypeError("parameter must be different from " - "NoneType if the rank is greater than 2") + raise TypeError("parameter must not be None " + "if the rank is greater than 2") return self._gen[1]**(q+1)/self._gen[2] if parameter in ZZ: if parameter <= 0 or parameter >= r: From 2db2bc2db3eaaefde8bd4f73b79215f385ebb17d Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:13:10 -0400 Subject: [PATCH 47/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 954527b4c8f..78124aea47a 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1494,7 +1494,7 @@ def j_invariant(self, parameter=None, check=True): if parameter is None: if r != 2: raise TypeError("parameter must not be None " - "if the rank is greater than 2") + "if the rank is greater than 2") return self._gen[1]**(q+1)/self._gen[2] if parameter in ZZ: if parameter <= 0 or parameter >= r: From 27cfc9c8ab2cf062e4261e2fa74e23bb779b5aa6 Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Fri, 17 Mar 2023 11:13:36 -0400 Subject: [PATCH 48/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 78124aea47a..1cd59aea691 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -944,8 +944,8 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): ((1, 2), (12, 29, 6)), ((1, 2), (31, 31, 7))] - Specify a list of indices to restrict which coefficients appear - in the parameters:: + One can specify the list of coefficients indices to be + considered in the computation:: sage: A = GF(3)['T'] sage: K. = Frac(A) From a4ac79c02da2dcba72a0bdb3becbddf604ccb672 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sat, 18 Mar 2023 07:15:35 -0400 Subject: [PATCH 49/62] add spacing between tests --- .../drinfeld_modules/drinfeld_module.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) 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 954527b4c8f..826f071039e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -984,21 +984,29 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): ... TypeError: coefficients indices must be integers + :: + sage: phi.basic_j_invariant_parameters([1, 10]) Traceback (most recent call last): ... ValueError: indices must be > 0 and < 3 + :: + sage: phi.basic_j_invariant_parameters([1, 1]) Traceback (most recent call last): ... ValueError: indices must be distinct and sorted + :: + sage: phi.basic_j_invariant_parameters([2, 1]) Traceback (most recent call last): ... ValueError: indices must be distinct and sorted + :: + sage: phi.basic_j_invariant_parameters('x') Traceback (most recent call last): ... @@ -1441,51 +1449,71 @@ def j_invariant(self, parameter=None, check=True): ... TypeError: parameter must not be None if the rank is greater than 2 + :: + sage: phi.j_invariant(-1) Traceback (most recent call last): ... ValueError: integer parameter must be >= 1 and < the rank (=4) + :: + sage: phi.j_invariant('x') Traceback (most recent call last): ... TypeError: parameter must be a tuple or a list of length 2 or an integer + :: + sage: phi.j_invariant((1, 2, 3)) Traceback (most recent call last): ... ValueError: list or tuple parameter must be of length 2 + :: + sage: phi.j_invariant(('x', (1, 2, 3))) Traceback (most recent call last): ... TypeError: list or tuple parameter must contain tuples or lists + :: + sage: phi.j_invariant(((1, 2), 'x')) Traceback (most recent call last): ... TypeError: list or tuple parameter must contain tuples or lists + :: + sage: phi.j_invariant(((1, 2, 3, 4, 5), (2, 1))) Traceback (most recent call last): ... ValueError: components of tuple or list parameter have incorrect length + :: + sage: phi.j_invariant(((1, 'x'), (2, 3, 8))) Traceback (most recent call last): ... TypeError: components of tuple or list parameter must contain only integers + :: + sage: phi.j_invariant(((1, 2), (2, 3, 'x'))) Traceback (most recent call last): ... TypeError: components of tuple or list parameter must contain only integers + :: + sage: phi.j_invariant(((1, 2), (4, 3, 7))) Traceback (most recent call last): ... ValueError: parameter does not satisfy the weight-0 condition + :: + sage: phi.j_invariant(((1, 2), (4, 3, 7)), check=False) 1/T^13 """ From b0d9093e954e7b044e59db62d5e64db8c3b535a6 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 19 Mar 2023 05:54:31 -0400 Subject: [PATCH 50/62] add characteristic check in is_isomorphic --- .../drinfeld_modules/drinfeld_module.py | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 f75f8e9416d..36bfc4bbce7 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1301,8 +1301,19 @@ def is_isomorphic(self, psi): sage: psi = DrinfeldModule(A, [T, 1, 0, 1, 0, T, 0, T^2, 1, 1]) sage: phi.is_isomorphic(psi) False + + TESTS:: + + sage: F. = GF(5^2) + sage: A = F['T'] + sage: phi = DrinfeldModule(A, [a, 1, 1]) + sage: psi = DrinfeldModule(A, [Frac(A).gen(), 1, 1]) + sage: phi.is_isomorphic(psi) + False """ # Trivial checks: + if self.characteristic() != psi.characteristic(): + return False if self._gen == psi._gen: return True if self._gen.degree() != psi._gen.degree(): From 10ddbd6e5ca5f364d31d323f40ec48290ff86e2f Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 19 Mar 2023 06:26:34 -0400 Subject: [PATCH 51/62] implement jk_invariants --- .../drinfeld_modules/drinfeld_module.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) 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 36bfc4bbce7..36bf7e1dad9 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1574,6 +1574,45 @@ def j_invariant(self, parameter=None, check=True): for k, d in zip(parameter[0], parameter[1][:-1])) return num / (self._gen[-1]**parameter[1][-1]) + def jk_invariants(self): + r""" + Return a dictionary whose keys are all the integers + `1 \leqslant k \leqslant r-1` and the values are the + `j_k`-invariants + + Recall that the `j_k`-invariants of self are defined by: + + .. MATH:: + + j_k := \frac{g_k^{(q^r - 1)/(\mathrm{gcd}(k, r) - 1)}}{g_r^{(q^k - 1)/(\mathrm{gcd}(k, r) - 1)}} + + where `g_i` is the `i`-th coefficient of the generator of self. + + EXAMPLES: + + sage: A = GF(3)['T'] + sage: K. = Frac(A) + sage: phi = DrinfeldModule(A, [T, 1, T+1, T^3, T^6]) + sage: jk_inv = phi.jk_invariants(); jk_inv + {1: 1/T^6, 2: (T^10 + T^9 + T + 1)/T^6, 3: T^42} + sage: jk_inv[2] + (T^10 + T^9 + T + 1)/T^6 + + :: + + sage: F = GF(7**2) + sage: A = F['T'] + sage: E. = F.extension(4) + sage: phi = DrinfeldModule(A, [z^2, 1, z+1, z^2, z, z+1]) + sage: phi.jk_invariants() + {1: 5*z^7 + 2*z^6 + 5*z^5 + 2*z^4 + 5*z^3 + z^2 + z + 2, + 2: 3*z^7 + 4*z^6 + 5*z^5 + 6*z^4 + 4*z, + 3: 5*z^7 + 6*z^6 + 6*z^5 + 4*z^3 + z^2 + 2*z + 1, + 4: 3*z^6 + 2*z^5 + 4*z^4 + 2*z^3 + 4*z^2 + 6*z + 2} + """ + r = self._gen.degree() # rank of self + return {k: self.j_invariant(k) for k in range(1, r)} + def morphism(self): r""" Return the morphism object that defines the Drinfeld module. From 3da8bad904b131643af3690f48c77a7ada5229ab Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 19 Mar 2023 07:02:38 -0400 Subject: [PATCH 52/62] fix typos --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 36bf7e1dad9..8cc9a543cb1 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1580,7 +1580,7 @@ def jk_invariants(self): `1 \leqslant k \leqslant r-1` and the values are the `j_k`-invariants - Recall that the `j_k`-invariants of self are defined by: + Recall that the `j_k`-invariant of self is defined by: .. MATH:: From f687d7f5dd9d87ce6f31b4b47e4ead2325634c8d Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Sun, 19 Mar 2023 09:16:58 -0400 Subject: [PATCH 53/62] add category and type check to is_isomorphic --- .../function_field/drinfeld_modules/drinfeld_module.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) 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 8cc9a543cb1..43b5df222dc 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1310,9 +1310,15 @@ def is_isomorphic(self, psi): sage: psi = DrinfeldModule(A, [Frac(A).gen(), 1, 1]) sage: phi.is_isomorphic(psi) False + sage: phi.is_isomorphic(A) + Traceback (most recent call last): + ... + TypeError: input must be a Drinfeld module """ + if not isinstance(psi, DrinfeldModule): + raise TypeError("input must be a Drinfeld module") # Trivial checks: - if self.characteristic() != psi.characteristic(): + if self.category() != psi.category(): return False if self._gen == psi._gen: return True From 20cbf833f8c140a1aaad8defd4acc2586de4d172 Mon Sep 17 00:00:00 2001 From: David Ayotte <34245930+DavidAyotte@users.noreply.github.com> Date: Wed, 22 Mar 2023 13:00:59 -0400 Subject: [PATCH 54/62] Update src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Antoine Leudière --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 8cc9a543cb1..215fed9ff99 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1578,7 +1578,7 @@ def jk_invariants(self): r""" Return a dictionary whose keys are all the integers `1 \leqslant k \leqslant r-1` and the values are the - `j_k`-invariants + corresponding `j_k`-invariants Recall that the `j_k`-invariant of self is defined by: From b698d129adc8248ed95e81a0820d861f66aec776 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Fri, 24 Mar 2023 13:50:31 -0400 Subject: [PATCH 55/62] raise error if categories differ --- .../function_field/drinfeld_modules/drinfeld_module.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 f55b3a919f8..3b4108078a3 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1309,7 +1309,9 @@ def is_isomorphic(self, psi): sage: phi = DrinfeldModule(A, [a, 1, 1]) sage: psi = DrinfeldModule(A, [Frac(A).gen(), 1, 1]) sage: phi.is_isomorphic(psi) - False + Traceback (most recent call last): + ... + ValueError: categories of the two Drinfeld modules must be the same sage: phi.is_isomorphic(A) Traceback (most recent call last): ... @@ -1319,7 +1321,8 @@ def is_isomorphic(self, psi): raise TypeError("input must be a Drinfeld module") # Trivial checks: if self.category() != psi.category(): - return False + raise ValueError("categories of the two Drinfeld modules " + "must be the same") if self._gen == psi._gen: return True if self._gen.degree() != psi._gen.degree(): From 6c974006d7bf9cf143c56eb7e13f487cfb2f55e8 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 12 Apr 2023 16:06:06 -0400 Subject: [PATCH 56/62] use smaller doctests without ellipsis --- .../drinfeld_modules/drinfeld_module.py | 42 ++++++++----------- 1 file changed, 17 insertions(+), 25 deletions(-) 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 f174ba3f6ea..eec3b335028 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -944,35 +944,27 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): ((1, 2), (12, 29, 6)), ((1, 2), (31, 31, 7))] - One can specify the list of coefficients indices to be - considered in the computation:: + Use the ``nonzero=True`` flag to display only the parameters + whose `j`-invariant value is nonzero:: - sage: A = GF(3)['T'] - sage: K. = Frac(A) - sage: phi = DrinfeldModule(A, [T, T, 2, T, 2*T, T^3, T^4 + T^2 + 1]) - sage: phi.basic_j_invariant_parameters([1, 5]) - [((1, 5), (273, 91, 31)), - ((1, 5), (297, 163, 55)), - ((1, 5), (295, 157, 53)), - ((1, 5), (265, 67, 23)), - ((1, 5), (357, 343, 115)), - ... - ((1, 5), (146, 74, 25))] + sage: phi.basic_j_invariant_parameters(nonzero=True) + [((2,), (31, 6))] - Use ``nonzero=True`` to speed up the computations for Drinfeld - modules having multiple zero coefficients:: - sage: A = GF(5)['T'] + One can specify the list of coefficients indices to be + considered in the computation:: + + sage: A = GF(2)['T'] sage: K. = Frac(A) - sage: phi = DrinfeldModule(A, [T, 0, T+1, 0, 1, 0, T]) - sage: phi.basic_j_invariant_parameters(nonzero=True) - [((2, 4), (260, 641, 26)), - ((2, 4), (157, 19, 1)), - ((2, 4), (27, 24, 1)), - ((2, 4), (188, 143, 6)), - ((2, 4), (401, 260, 11)), - ... - ((2, 4), (288, 39, 2))] + sage: phi = DrinfeldModule(A, [T, T, 1, T]) + sage: phi.basic_j_invariant_parameters([1, 2]) + [((1, 2), (1, 2, 1)), + ((1, 2), (4, 1, 1)), + ((1, 2), (7, 0, 1)), + ((1, 2), (5, 3, 2)), + ((1, 2), (0, 7, 3)), + ((1, 2), (6, 5, 3)), + ((1, 2), (7, 7, 4))] TESTS:: From 0af9c3f472602e3fe6952960ae6cb77072459908 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 12 Apr 2023 17:35:24 -0400 Subject: [PATCH 57/62] add missing colon in jk_invariant doc --- .../rings/function_field/drinfeld_modules/drinfeld_module.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 eec3b335028..287eeca9fe9 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1589,7 +1589,7 @@ def jk_invariants(self): where `g_i` is the `i`-th coefficient of the generator of self. - EXAMPLES: + EXAMPLES:: sage: A = GF(3)['T'] sage: K. = Frac(A) From 4308b63ad93d5a765035db58bc00bae4a8d2d447 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 19 Apr 2023 16:22:24 -0400 Subject: [PATCH 58/62] remove is_isomorphic method --- .../drinfeld_modules/drinfeld_module.py | 65 ------------------- 1 file changed, 65 deletions(-) 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 287eeca9fe9..7f44d942026 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1267,71 +1267,6 @@ def is_finite(self) -> bool: from sage.rings.function_field.drinfeld_modules.finite_drinfeld_module import FiniteDrinfeldModule return isinstance(self, FiniteDrinfeldModule) - def is_isomorphic(self, psi): - r""" - Return ``True`` whether ``self`` is isomorphic to the other - Drinfeld module - - EXAMPLES:: - - sage: A = GF(5)['T'] - sage: K. = Frac(A) - sage: c = T^4 + 3*T^2 + T - sage: phi = DrinfeldModule(A, [T, T^3, T^9, T]) - sage: psi = DrinfeldModule(A, [T, c^4*T^3, c^(24)*T^9, c^(124)*T]) - sage: phi.is_isomorphic(psi) - True - sage: rho = DrinfeldModule(A, [T, 1, 1, 1]) - sage: phi.is_isomorphic(rho) - False - - :: - - sage: phi = DrinfeldModule(A, [T, 1, 0, 1, 0, 0, 0, T^2, 1, 1]) - sage: phi.is_isomorphic(phi) - True - sage: psi = DrinfeldModule(A, [T, 1, 0, 1, 0, T, 0, T^2, 1, 1]) - sage: phi.is_isomorphic(psi) - False - - TESTS:: - - sage: F. = GF(5^2) - sage: A = F['T'] - sage: phi = DrinfeldModule(A, [a, 1, 1]) - sage: psi = DrinfeldModule(A, [Frac(A).gen(), 1, 1]) - sage: phi.is_isomorphic(psi) - Traceback (most recent call last): - ... - ValueError: categories of the two Drinfeld modules must be the same - sage: phi.is_isomorphic(A) - Traceback (most recent call last): - ... - TypeError: input must be a Drinfeld module - """ - if not isinstance(psi, DrinfeldModule): - raise TypeError("input must be a Drinfeld module") - # Trivial checks: - if self.category() != psi.category(): - raise ValueError("categories of the two Drinfeld modules " - "must be the same") - if self._gen == psi._gen: - return True - if self._gen.degree() != psi._gen.degree(): - return False - if self._gen.degree() == 1: - return True - # Check if self and psi are patterned alike: - if not all(bool(g_self) == bool(g_psi) for g_self, g_psi in\ - zip(self._gen.coefficients(sparse=False), - psi._gen.coefficients(sparse=False))): - return False - # Check that all the nonzero basic j-invariants agree - for p in self.basic_j_invariant_parameters(nonzero=True): - if self.j_invariant(p, check=False) != psi.j_invariant(p, check=False): - return False - return True - def j_invariant(self, parameter=None, check=True): r""" Return the `j`-invariant of the Drinfeld From a640a55e33de4c1ea3bdfc9d647704cf399773f8 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 19 Apr 2023 16:27:05 -0400 Subject: [PATCH 59/62] add nonzero parameter to basic_j_invariant --- .../drinfeld_modules/drinfeld_module.py | 24 +++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) 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 7f44d942026..c37695ffec0 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1052,13 +1052,27 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): return [(tuple(coeff_indices), tuple(p)) for p in integral_points if gcd(p) == 1] - def basic_j_invariants(self): + def basic_j_invariants(self, nonzero=False): r""" Return a dictionary whose keys are all the basic `j`-invariants parameters and values are the corresponding `j`-invariant. See the method :meth:`j_invariant` for definitions. + INPUT: + + - ``nonzero`` (boolean, default: ``False``) -- if this flag + is set to ``True``, then only the parameters for which the + corresponding basic `j`-invariant is nonzero are returned. + + .. WARNING:: + + The usage of this method can be computationally + expensive e.g. if the rank is greater than four, + or if `q` is large. Setting the ``nonzero`` flag to ``True`` + can speed up the computation considerably if the Drinfeld + module generator possesses multiple zero coefficients. + EXAMPLES:: sage: Fq = GF(25) @@ -1069,6 +1083,12 @@ def basic_j_invariants(self): sage: phi.basic_j_invariants() {((1,), (26, 1)): z12^10 + 4*z12^9 + 3*z12^8 + 2*z12^7 + 3*z12^6 + z12^5 + z12^3 + 4*z12^2 + z12 + 2} + :: + + sage: phi = DrinfeldModule(A, [p_root, 0, 1, z12]) + sage: phi.basic_j_invariants(nonzero=True) + {((2,), (651, 26)): z12^11 + 3*z12^10 + 4*z12^9 + 3*z12^8 + z12^7 + 2*z12^6 + 3*z12^4 + 2*z12^3 + z12^2 + 4*z12} + :: sage: A = GF(5)['T'] @@ -1087,7 +1107,7 @@ def basic_j_invariants(self): T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3 """ return {parameter: self.j_invariant(parameter, check=False) - for parameter in self.basic_j_invariant_parameters()} + for parameter in self.basic_j_invariant_parameters(nonzero=nonzero)} def coefficient(self, n): r""" From ccb497e570de8d4af37a0a1cd43d9f8dbe2b8c90 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 25 Jul 2023 06:19:00 -0400 Subject: [PATCH 60/62] normalize j-invariants parameters --- .../drinfeld_modules/drinfeld_module.py | 59 ++++++++++++------- 1 file changed, 37 insertions(+), 22 deletions(-) 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 2b3898ccb32..dd259617e4e 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -938,26 +938,26 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, 0, T+1, T^2 + 1]) sage: phi.basic_j_invariant_parameters() - [((1, 2), (1, 5, 1)), + [((1,), (31, 1)), + ((1, 2), (1, 5, 1)), ((1, 2), (7, 4, 1)), - ((1, 2), (13, 3, 1)), - ((1, 2), (19, 2, 1)), - ((1, 2), (25, 1, 1)), - ((1, 2), (31, 0, 1)), ((1, 2), (8, 9, 2)), - ((1, 2), (20, 7, 2)), ((1, 2), (9, 14, 3)), - ((1, 2), (15, 13, 3)), - ((1, 2), (27, 11, 3)), ((1, 2), (10, 19, 4)), - ((1, 2), (22, 17, 4)), ((1, 2), (11, 24, 5)), + ((1, 2), (12, 29, 6)), + ((1, 2), (13, 3, 1)), + ((1, 2), (15, 13, 3)), ((1, 2), (17, 23, 5)), + ((1, 2), (19, 2, 1)), + ((1, 2), (20, 7, 2)), + ((1, 2), (22, 17, 4)), ((1, 2), (23, 22, 5)), + ((1, 2), (25, 1, 1)), + ((1, 2), (27, 11, 3)), ((1, 2), (29, 21, 5)), - ((1, 2), (0, 31, 6)), - ((1, 2), (12, 29, 6)), - ((1, 2), (31, 31, 7))] + ((1, 2), (31, 31, 7)), + ((2,), (31, 6))] Use the ``nonzero=True`` flag to display only the parameters whose `j`-invariant value is nonzero:: @@ -973,13 +973,13 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, T, 1, T]) sage: phi.basic_j_invariant_parameters([1, 2]) - [((1, 2), (1, 2, 1)), + [((1,), (7, 1)), + ((1, 2), (1, 2, 1)), ((1, 2), (4, 1, 1)), - ((1, 2), (7, 0, 1)), ((1, 2), (5, 3, 2)), - ((1, 2), (0, 7, 3)), ((1, 2), (6, 5, 3)), - ((1, 2), (7, 7, 4))] + ((1, 2), (7, 7, 4)), + ((2,), (7, 3))] TESTS:: @@ -1064,8 +1064,23 @@ def basic_j_invariant_parameters(self, coeff_indices=None, nonzero=False): polyhedron = Polyhedron(ieqs=inequalities, eqns=[equation]) # Compute its integral points integral_points = polyhedron.integral_points() - return [(tuple(coeff_indices), tuple(p)) for p in integral_points - if gcd(p) == 1] + # Format the result + parameters = [] + for p in integral_points: + if gcd(p) != 1: + continue + ks = list(coeff_indices) + ds = p.list() + i = 0 + while i < len(ks): + if ds[i] == 0: + del ds[i] + del ks[i] + else: + i += 1 + parameters.append((tuple(ks), tuple(ds))) + parameters.sort() + return parameters def basic_j_invariants(self, nonzero=False): r""" @@ -1110,14 +1125,14 @@ def basic_j_invariants(self, nonzero=False): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, T + 2, T+1, 1]) sage: J_phi = phi.basic_j_invariants(); J_phi - {((1, 2), (0, 31, 6)): T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1, + {((1,), (31, 1)): T^31 + 2*T^30 + 2*T^26 + 4*T^25 + 2*T^6 + 4*T^5 + 4*T + 3, ((1, 2), (1, 5, 1)): T^6 + 2*T^5 + T + 2, ((1, 2), (7, 4, 1)): T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3, ((1, 2), (8, 9, 2)): T^17 + 2*T^15 + T^14 + 4*T^13 + 4*T^11 + 4*T^10 + 3*T^9 + 2*T^8 + 3*T^7 + 2*T^6 + 3*T^5 + 2*T^4 + 3*T^3 + 4*T^2 + 3*T + 1, ((1, 2), (9, 14, 3)): T^23 + 2*T^22 + 2*T^21 + T^19 + 4*T^18 + T^17 + 4*T^16 + T^15 + 4*T^14 + 2*T^12 + 4*T^11 + 4*T^10 + 2*T^8 + 4*T^7 + 4*T^6 + 2*T^4 + T^2 + 2*T + 2, ((1, 2), (10, 19, 4)): T^29 + 4*T^28 + T^27 + 4*T^26 + T^25 + 2*T^24 + 3*T^23 + 2*T^22 + 3*T^21 + 2*T^20 + 4*T^19 + T^18 + 4*T^17 + T^16 + 4*T^15 + T^9 + 4*T^8 + T^7 + 4*T^6 + T^5 + 4*T^4 + T^3 + 4*T^2 + T + 4, ... - ((1, 2), (31, 31, 7)): T^62 + 3*T^61 + 2*T^60 + 3*T^57 + 4*T^56 + T^55 + 2*T^52 + T^51 + 4*T^50 + 3*T^37 + 4*T^36 + T^35 + 4*T^32 + 2*T^31 + 3*T^30 + T^27 + 3*T^26 + 2*T^25 + 2*T^12 + T^11 + 4*T^10 + T^7 + 3*T^6 + 2*T^5 + 4*T^2 + 2*T + 3} + ((2,), (31, 6)): T^31 + T^30 + T^26 + T^25 + T^6 + T^5 + T + 1} sage: J_phi[((1, 2), (7, 4, 1))] T^11 + 3*T^10 + T^9 + 4*T^8 + T^7 + 2*T^6 + 2*T^4 + 3*T^3 + 2*T^2 + 3 """ @@ -1703,9 +1718,9 @@ def j_invariant(self, parameter=None, check=True): sage: K. = Frac(A) sage: phi = DrinfeldModule(A, [T, T^2 + T + 1, 0, T^4 + 1, T - 1]) sage: param = phi.basic_j_invariant_parameters(nonzero=True) - sage: phi.j_invariant(param[0]) - T^13 + 2*T^12 + T + 2 sage: phi.j_invariant(param[1]) + T^13 + 2*T^12 + T + 2 + sage: phi.j_invariant(param[2]) T^35 + 2*T^31 + T^27 + 2*T^8 + T^4 + 2 TESTS:: From 1dfd308b0560860e8b00b6ed1c5611a69b03fcb6 Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Tue, 25 Jul 2023 08:55:08 -0400 Subject: [PATCH 61/62] add type checks for parameters --- .../drinfeld_modules/drinfeld_module.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) 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 dd259617e4e..1e3fb212a82 100644 --- a/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py +++ b/src/sage/rings/function_field/drinfeld_modules/drinfeld_module.py @@ -1809,6 +1809,7 @@ def j_invariant(self, parameter=None, check=True): "if the rank is greater than 2") return self._gen[1]**(q+1)/self._gen[2] if parameter in ZZ: + parameter = ZZ(parameter) if parameter <= 0 or parameter >= r: raise ValueError("integer parameter must be >= 1 and < the " f"rank (={r})") @@ -1826,17 +1827,19 @@ def j_invariant(self, parameter=None, check=True): not len(parameter[1]) == len(parameter[0]) + 1: raise ValueError("components of tuple or list parameter have " "incorrect length") - if not all(p in ZZ for p in parameter[0])\ - or not all(p in ZZ for p in parameter[1]): + try: # Check parameter's type + parameter_0 = [ZZ(p) for p in parameter[0]] + parameter_1 = [ZZ(p) for p in parameter[1]] + except TypeError: raise TypeError("components of tuple or list parameter must " "contain only integers") # Check that the weight-0 condition is satisfied: # d_1 (q - 1) + ... + d_{r-1} (q^{r-1} - 1) # = d_r (q^r - 1) if check: - right = parameter[1][-1]*(q**r - 1) - left = sum(parameter[1][i]*(q**(parameter[0][i]) - 1) for i in - range(len(parameter[0]))) + right = parameter_1[-1]*(q**r - 1) + left = sum(parameter_1[i]*(q**(parameter_0[i]) - 1) for i in + range(len(parameter_0))) if left != right: raise ValueError("parameter does not satisfy the " "weight-0 condition") @@ -1844,8 +1847,8 @@ def j_invariant(self, parameter=None, check=True): raise TypeError("parameter must be a tuple or a list of " "length 2 or an integer") num = prod(self._gen[k]**d - for k, d in zip(parameter[0], parameter[1][:-1])) - return num / (self._gen[-1]**parameter[1][-1]) + for k, d in zip(parameter_0, parameter_1[:-1])) + return num / (self._gen[-1]**parameter_1[-1]) def jk_invariants(self): r""" From 93cb61f22dc1bb03307649a2b15c1efa9683912c Mon Sep 17 00:00:00 2001 From: DavidAyotte Date: Wed, 26 Jul 2023 04:19:26 -0400 Subject: [PATCH 62/62] fix merging problems --- src/doc/en/reference/references/index.rst | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/doc/en/reference/references/index.rst b/src/doc/en/reference/references/index.rst index 3487ad28cc3..6e8001e8d5e 100644 --- a/src/doc/en/reference/references/index.rst +++ b/src/doc/en/reference/references/index.rst @@ -1900,11 +1900,7 @@ REFERENCES: .. [CW2005] \J. E. Cremona and M. Watkins. Computing isogenies of elliptic curves. preprint, 2005. -<<<<<<< HEAD -.. [CHW2015] Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation -======= .. [CHW2015] Cui, Shawn X.; Hong, Seung-Moon; Wang, Zhenghan Universal quantum computation ->>>>>>> develop with weakly integral anyons. Quantum Inf. Process. 14 (2015), no. 8, 2687-2727. @@ -2728,9 +2724,9 @@ REFERENCES: doubles of finite groups*. PhD Thesis, University of California, Santa Cruz. 1999. -.. [GoMa2010] Christopher Goff and Geoffrey Mason, +.. [GoMa2010] Christopher Goff and Geoffrey Mason, *Generalized twisted quantum doubles and the McKay correspondence*, - J. Algebra 324 (2010), no. 11, 3007–3016. + J. Algebra 324 (2010), no. 11, 3007–3016. .. [GJ2016] Muddappa Seetharama Gowda and Juyoung Jeong. Spectral cones in Euclidean Jordan algebras.