From be7c5f113df9080ecc4843da0073a9fc8e71f1d2 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Wed, 6 Mar 2024 14:46:12 +0100 Subject: [PATCH 01/14] Implemented the (local) Hilbert symbol for global function fields Amends: Changed some documentation, adapted characteristic check to check ==2 instead of <= 2 --- .../rings/function_field/function_field.py | 127 ++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index af5b6c4d924..83ba61572c4 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1224,6 +1224,133 @@ def completion(self, place, name=None, prec=None, gen_name=None): from .maps import FunctionFieldCompletion return FunctionFieldCompletion(self, place, name=name, prec=prec, gen_name=gen_name) + def hilbert_symbol(self, a, b, P): + """ + Return the Hilbert symbol `(a,b)_{F_P}` (where `F_P` is the + completion of ``self`` at the place ``P``), i.e. the value 1 if + the quaternion algebra defined by `(a,b)` over `F_P` is split, + and -1 if it is division. + + We use the completion at the place `P` to compute the valuations `v(a)` + and `v(b)` as well as elements `a0` and `b0` such that, for a uniformizer + `pi` of the unique maximal ideal of the completion, the elememts + `a*pi^{-v(a))}` and `a0` respectively the elements `b*pi^{-v(b)}` and `b0` + are congruent modulo `pi`. Motivated by formula 12.4.10 in [Voi2021]_. + + Currently only tested for function fields separable over their base + since places are not fully supported for other function fields. Only + implemented for global function fields of odd characteristic. + + INPUT: + + - ``a`` and ``b``: Units in the function field ``self`` + + - ``P``: A place of the function field ``self`` + + EXAMPLES:: + + sage: K. = FunctionField(GF(17)) + sage: P = K.places()[0]; P + Place (1/x) + sage: a = (5*x + 6)/(x + 15) + sage: b = 7/x + sage: K.hilbert_symbol(a, b, P) + -1 + + sage: Q = K.places()[7]; Q + Place (x + 6) + sage: c = 15*x + 12 + sage: d = 16/(x + 13) + sage: K.hilbert_symbol(c, d, Q) + 1 + + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) + sage: f = (x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 + + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4 + sage: L. = K.extension(f) + sage: P = L.places_above(K.places()[0])[1] + sage: a = ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 + + ((3*x + 4)/(x + 4))*y^2 + + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y + + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2) + sage: b = ((x + 1)/(x + 4))*y^4 + + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 + + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y + + (x^3 + x)/(x^3 + x^2 + x + 2) + sage: L.hilbert_symbol(a, b, P) + 1 + sage: Q = L.places_above(K.places()[1])[0] + sage: c = ((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 + + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 + + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 + + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y + + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x) + sage: d = ((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 + + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 + + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y + + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4) + sage: L.hilbert_symbol(c, d, Q) + -1 + + sage: K. = FunctionField(GF(3)); R. = PolynomialRing(K) + sage: g = ((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 + + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2) + sage: L = K.extension(g) + sage: P = L.places_above(K.places()[1])[1] + sage: a = ((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 + + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 + + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1) + sage: b = ((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 + + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 + + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1 + sage: L.hilbert_symbol(a, b, P) + -1 + """ + if not self.is_global(): + raise NotImplementedError('only supported for global function fields') + + if self.characteristic() == 2: + raise NotImplementedError('only supported in odd characteristic') + + if not (a in self and b in self): + raise ValueError('a and b must be elements of the function field') + + if a.is_zero() or b.is_zero(): + raise ValueError('the invariants a and b must be nonzero') + + # Compute the completion map to precision 1 for computation of the + # valuations v(a), v(b) as well as the elements a_0, b_0 + try: + sigma = self.completion(P, prec=1, gen_name='i') + except AttributeError: + raise ValueError('P must be a place of the function field F') + + # Apply the completion map to a to get v(a) and a_0 + ser_a = sigma(a) + v_a = ser_a.valuation() + a0 = ser_a.coefficients()[0] + + # Apply the completion map to b to get v(b) and b_0 + ser_b = sigma(b) + v_b = ser_b.valuation() + b0 = ser_b.coefficients()[0] + + # Get the residue field of the completion, together with the residue map + k, _, tau = self.residue_field(P) + e = (k.order()-1)/2 + + # Use Euler's criterion to compute the power of Legendre symbols + a_rd_pw = tau(a0)**(v_b * e) + b_rd_pw = tau(b0)**(v_a * e) + + # Finally, put the result together + res = k(-1)**(v_a * v_b * e) * a_rd_pw * b_rd_pw + + if res == k(1): + return ZZ(1) + else: + return ZZ(-1) + def extension_constant_field(self, k): """ Return the constant field extension with constant field `k`. From b7a9614b3b77d3adbae8cb4b9fe9496b7959f25d Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Wed, 6 Mar 2024 17:17:29 +0100 Subject: [PATCH 02/14] Corrected code in examples --- .../rings/function_field/function_field.py | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 83ba61572c4..1486fedec98 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1266,43 +1266,43 @@ def hilbert_symbol(self, a, b, P): sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) sage: f = (x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 - + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4 + + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4 sage: L. = K.extension(f) sage: P = L.places_above(K.places()[0])[1] sage: a = ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 - + ((3*x + 4)/(x + 4))*y^2 - + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y - + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2) + + ((3*x + 4)/(x + 4))*y^2 + + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y + + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2) sage: b = ((x + 1)/(x + 4))*y^4 - + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 - + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y - + (x^3 + x)/(x^3 + x^2 + x + 2) + + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 + + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y + + (x^3 + x)/(x^3 + x^2 + x + 2) sage: L.hilbert_symbol(a, b, P) 1 sage: Q = L.places_above(K.places()[1])[0] sage: c = ((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 - + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 - + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 - + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y - + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x) + + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 + + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 + + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y + + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x) sage: d = ((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 - + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 - + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y - + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4) + + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 + + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y + + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4) sage: L.hilbert_symbol(c, d, Q) -1 sage: K. = FunctionField(GF(3)); R. = PolynomialRing(K) sage: g = ((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 - + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2) + + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2) sage: L = K.extension(g) sage: P = L.places_above(K.places()[1])[1] sage: a = ((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 - + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 - + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1) + + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 + + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1) sage: b = ((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 - + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 - + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1 + + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 + + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1 sage: L.hilbert_symbol(a, b, P) -1 """ From 02c46a94009c598cebb1d1b4ced677b3b8ee2397 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Wed, 6 Mar 2024 23:55:45 +0100 Subject: [PATCH 03/14] Small optimization and docstring modifications --- .../rings/function_field/function_field.py | 67 +++++++++---------- 1 file changed, 32 insertions(+), 35 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 1486fedec98..1ea0fb4820f 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1265,44 +1265,44 @@ def hilbert_symbol(self, a, b, P): 1 sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) - sage: f = (x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 - + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4 + sage: f = ((x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 + + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4) sage: L. = K.extension(f) sage: P = L.places_above(K.places()[0])[1] - sage: a = ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 - + ((3*x + 4)/(x + 4))*y^2 - + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y - + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2) - sage: b = ((x + 1)/(x + 4))*y^4 - + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 - + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y - + (x^3 + x)/(x^3 + x^2 + x + 2) + sage: a = (((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 + + ((3*x + 4)/(x + 4))*y^2 + + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y + + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2)) + sage: b = (((x + 1)/(x + 4))*y^4 + + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 + + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y + + (x^3 + x)/(x^3 + x^2 + x + 2)) sage: L.hilbert_symbol(a, b, P) 1 sage: Q = L.places_above(K.places()[1])[0] - sage: c = ((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 - + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 - + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 - + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y - + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x) - sage: d = ((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 - + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 - + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y - + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4) + sage: c = (((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 + + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 + + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 + + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y + + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x)) + sage: d = (((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 + + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 + + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y + + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4)) sage: L.hilbert_symbol(c, d, Q) -1 sage: K. = FunctionField(GF(3)); R. = PolynomialRing(K) - sage: g = ((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 - + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2) + sage: g = (((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 + + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2)) sage: L = K.extension(g) sage: P = L.places_above(K.places()[1])[1] - sage: a = ((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 - + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 - + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1) - sage: b = ((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 - + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 - + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1 + sage: a = (((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 + + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 + + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1)) + sage: b = (((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 + + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 + + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1) sage: L.hilbert_symbol(a, b, P) -1 """ @@ -1319,18 +1319,18 @@ def hilbert_symbol(self, a, b, P): raise ValueError('the invariants a and b must be nonzero') # Compute the completion map to precision 1 for computation of the - # valuations v(a), v(b) as well as the elements a_0, b_0 + # valuations v(a), v(b) as well as the elements a0, b0 try: sigma = self.completion(P, prec=1, gen_name='i') except AttributeError: raise ValueError('P must be a place of the function field F') - # Apply the completion map to a to get v(a) and a_0 + # Apply the completion map to a to get v(a) and a0 ser_a = sigma(a) v_a = ser_a.valuation() a0 = ser_a.coefficients()[0] - # Apply the completion map to b to get v(b) and b_0 + # Apply the completion map to b to get v(b) and b0 ser_b = sigma(b) v_b = ser_b.valuation() b0 = ser_b.coefficients()[0] @@ -1339,17 +1339,14 @@ def hilbert_symbol(self, a, b, P): k, _, tau = self.residue_field(P) e = (k.order()-1)/2 - # Use Euler's criterion to compute the power of Legendre symbols + # Use Euler's criterion to compute the powers of Legendre symbols a_rd_pw = tau(a0)**(v_b * e) b_rd_pw = tau(b0)**(v_a * e) # Finally, put the result together res = k(-1)**(v_a * v_b * e) * a_rd_pw * b_rd_pw - if res == k(1): - return ZZ(1) - else: - return ZZ(-1) + return ZZ(1) if res == k(1) else ZZ(-1) def extension_constant_field(self, k): """ From c12a9de520ec9461e9e0a0faa14c94dd8c2ee6e0 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Thu, 7 Mar 2024 23:40:11 +0100 Subject: [PATCH 04/14] Docstring corrections Amend: Added missing multiline continuation markers --- .../rings/function_field/function_field.py | 62 ++++++++++--------- 1 file changed, 33 insertions(+), 29 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 1ea0fb4820f..d542dfd353c 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -220,6 +220,9 @@ - Brent Baccala (2019-12-20): added function fields over number fields and QQbar +- Sebastian A. Spindler (2024-03-06): implemented Hilbert symbols for global + function fields + """ # ***************************************************************************** @@ -247,6 +250,7 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import LazyImport +from sage.rings.integer_ring import ZZ from sage.rings.ring import Field from sage.categories.homset import Hom from sage.categories.function_fields import FunctionFields @@ -1227,9 +1231,9 @@ def completion(self, place, name=None, prec=None, gen_name=None): def hilbert_symbol(self, a, b, P): """ Return the Hilbert symbol `(a,b)_{F_P}` (where `F_P` is the - completion of ``self`` at the place ``P``), i.e. the value 1 if + completion of ``self`` at the place ``P``), i.e. the value `1` if the quaternion algebra defined by `(a,b)` over `F_P` is split, - and -1 if it is division. + and `-1` if it is division. We use the completion at the place `P` to compute the valuations `v(a)` and `v(b)` as well as elements `a0` and `b0` such that, for a uniformizer @@ -1265,44 +1269,44 @@ def hilbert_symbol(self, a, b, P): 1 sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) - sage: f = ((x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 - + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4) + sage: f = (x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 + ....: + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4 sage: L. = K.extension(f) sage: P = L.places_above(K.places()[0])[1] - sage: a = (((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 - + ((3*x + 4)/(x + 4))*y^2 - + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y - + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2)) - sage: b = (((x + 1)/(x + 4))*y^4 - + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 - + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y - + (x^3 + x)/(x^3 + x^2 + x + 2)) + sage: a = ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 + ....: + ((3*x + 4)/(x + 4))*y^2 + ....: + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y + ....: + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2) + sage: b = ((x + 1)/(x + 4))*y^4 + ....: + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 + ....: + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y + ....: + (x^3 + x)/(x^3 + x^2 + x + 2) sage: L.hilbert_symbol(a, b, P) 1 sage: Q = L.places_above(K.places()[1])[0] - sage: c = (((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 - + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 - + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 - + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y - + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x)) - sage: d = (((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 - + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 - + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y - + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4)) + sage: c = ((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 + ....: + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 + ....: + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 + ....: + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y + ....: + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x) + sage: d = ((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 + ....: + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 + ....: + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y + ....: + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4) sage: L.hilbert_symbol(c, d, Q) -1 sage: K. = FunctionField(GF(3)); R. = PolynomialRing(K) - sage: g = (((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 - + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2)) + sage: g = ((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 + ....: + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2) sage: L = K.extension(g) sage: P = L.places_above(K.places()[1])[1] - sage: a = (((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 - + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 - + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1)) - sage: b = (((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 - + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 - + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1) + sage: a = ((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 + ....: + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 + ....: + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1) + sage: b = ((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 + ....: + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 + ....: + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1 sage: L.hilbert_symbol(a, b, P) -1 """ From 0db2eb1437503742d95c66ed7b8bb62b022665f3 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Fri, 8 Mar 2024 08:38:41 +0100 Subject: [PATCH 05/14] Fixed docstrings and small modifications - Simplified return command, removed `ZZ` from import again - Return `0` instead of an error for zero inputs --- .../rings/function_field/function_field.py | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index d542dfd353c..d6e323d3810 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -250,7 +250,6 @@ from sage.misc.cachefunc import cached_method from sage.misc.lazy_import import LazyImport -from sage.rings.integer_ring import ZZ from sage.rings.ring import Field from sage.categories.homset import Hom from sage.categories.function_fields import FunctionFields @@ -1231,9 +1230,11 @@ def completion(self, place, name=None, prec=None, gen_name=None): def hilbert_symbol(self, a, b, P): """ Return the Hilbert symbol `(a,b)_{F_P}` (where `F_P` is the - completion of ``self`` at the place ``P``), i.e. the value `1` if - the quaternion algebra defined by `(a,b)` over `F_P` is split, - and `-1` if it is division. + completion of ``self`` at the place ``P``). + + The Hilbert symbol `(a,b)_{F_P}` is `0` if one of ``a`` or ``b`` is + zero. Otherwise it takes the value `1` if the quaternion algebra + defined by `(a,b)` over `F_P` is split, and `-1` if it is division. We use the completion at the place `P` to compute the valuations `v(a)` and `v(b)` as well as elements `a0` and `b0` such that, for a uniformizer @@ -1269,44 +1270,44 @@ def hilbert_symbol(self, a, b, P): 1 sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) - sage: f = (x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 - ....: + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4 + sage: (f = (x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 + ....: + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4) sage: L. = K.extension(f) sage: P = L.places_above(K.places()[0])[1] - sage: a = ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 + sage: (a = ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 ....: + ((3*x + 4)/(x + 4))*y^2 ....: + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y - ....: + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2) - sage: b = ((x + 1)/(x + 4))*y^4 + ....: + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2)) + sage: (b = ((x + 1)/(x + 4))*y^4 ....: + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 ....: + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y - ....: + (x^3 + x)/(x^3 + x^2 + x + 2) + ....: + (x^3 + x)/(x^3 + x^2 + x + 2)) sage: L.hilbert_symbol(a, b, P) 1 sage: Q = L.places_above(K.places()[1])[0] - sage: c = ((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 + sage: (c = ((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 ....: + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 ....: + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 ....: + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y - ....: + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x) - sage: d = ((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 + ....: + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x)) + sage: (d = ((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 ....: + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 ....: + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y - ....: + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4) + ....: + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4)) sage: L.hilbert_symbol(c, d, Q) -1 sage: K. = FunctionField(GF(3)); R. = PolynomialRing(K) - sage: g = ((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 - ....: + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2) + sage: (g = ((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 + ....: + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2)) sage: L = K.extension(g) sage: P = L.places_above(K.places()[1])[1] - sage: a = ((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 + sage: (a = ((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 ....: + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 - ....: + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1) - sage: b = ((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 + ....: + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1)) + sage: (b = ((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 ....: + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 - ....: + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1 + ....: + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1) sage: L.hilbert_symbol(a, b, P) -1 """ @@ -1320,7 +1321,7 @@ def hilbert_symbol(self, a, b, P): raise ValueError('a and b must be elements of the function field') if a.is_zero() or b.is_zero(): - raise ValueError('the invariants a and b must be nonzero') + return 0 # Compute the completion map to precision 1 for computation of the # valuations v(a), v(b) as well as the elements a0, b0 @@ -1341,16 +1342,15 @@ def hilbert_symbol(self, a, b, P): # Get the residue field of the completion, together with the residue map k, _, tau = self.residue_field(P) - e = (k.order()-1)/2 + e = (k.order() - 1) >> 1 # Use Euler's criterion to compute the powers of Legendre symbols a_rd_pw = tau(a0)**(v_b * e) b_rd_pw = tau(b0)**(v_a * e) - # Finally, put the result together + # Finally, put the result together and transform it into the correct output res = k(-1)**(v_a * v_b * e) * a_rd_pw * b_rd_pw - - return ZZ(1) if res == k(1) else ZZ(-1) + return (res.is_one() << 1) - 1 def extension_constant_field(self, k): """ From b8f80569a9fa90f610686f8ed38c1e04bc01bfee Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Fri, 8 Mar 2024 18:43:20 +0100 Subject: [PATCH 06/14] Fixed LINT and docstring issues --- .../rings/function_field/function_field.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index d6e323d3810..20610b1be35 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1270,27 +1270,27 @@ def hilbert_symbol(self, a, b, P): 1 sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) - sage: (f = (x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 + sage: f = ((x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 ....: + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4) sage: L. = K.extension(f) sage: P = L.places_above(K.places()[0])[1] - sage: (a = ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 + sage: a = (((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 ....: + ((3*x + 4)/(x + 4))*y^2 ....: + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y ....: + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2)) - sage: (b = ((x + 1)/(x + 4))*y^4 + sage: b = (((x + 1)/(x + 4))*y^4 ....: + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 ....: + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y ....: + (x^3 + x)/(x^3 + x^2 + x + 2)) sage: L.hilbert_symbol(a, b, P) 1 sage: Q = L.places_above(K.places()[1])[0] - sage: (c = ((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 + sage: c = (((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 ....: + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 ....: + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 ....: + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y ....: + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x)) - sage: (d = ((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 + sage: d = (((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 ....: + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 ....: + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y ....: + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4)) @@ -1298,14 +1298,14 @@ def hilbert_symbol(self, a, b, P): -1 sage: K. = FunctionField(GF(3)); R. = PolynomialRing(K) - sage: (g = ((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 + sage: g = (((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 ....: + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2)) sage: L = K.extension(g) sage: P = L.places_above(K.places()[1])[1] - sage: (a = ((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 + sage: a = (((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 ....: + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 ....: + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1)) - sage: (b = ((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 + sage: b = (((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 ....: + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 ....: + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1) sage: L.hilbert_symbol(a, b, P) @@ -1348,7 +1348,7 @@ def hilbert_symbol(self, a, b, P): a_rd_pw = tau(a0)**(v_b * e) b_rd_pw = tau(b0)**(v_a * e) - # Finally, put the result together and transform it into the correct output + # Finally, put the result together and transform it into the correct output res = k(-1)**(v_a * v_b * e) * a_rd_pw * b_rd_pw return (res.is_one() << 1) - 1 From 24beec30cd7d385dc1e0f9f0c988ff0d07dae5a0 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Fri, 8 Mar 2024 20:03:32 +0100 Subject: [PATCH 07/14] Small input adaption, finally fixed docstrings --- src/sage/rings/function_field/function_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 20610b1be35..ea63ce337ae 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1248,9 +1248,9 @@ def hilbert_symbol(self, a, b, P): INPUT: - - ``a`` and ``b``: Units in the function field ``self`` + - ``a`` and ``b``: Elements of ``self`` - - ``P``: A place of the function field ``self`` + - ``P``: A place of ``self`` EXAMPLES:: @@ -1300,7 +1300,7 @@ def hilbert_symbol(self, a, b, P): sage: K. = FunctionField(GF(3)); R. = PolynomialRing(K) sage: g = (((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 ....: + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2)) - sage: L = K.extension(g) + sage: L. = K.extension(g) sage: P = L.places_above(K.places()[1])[1] sage: a = (((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 ....: + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 From 53e601e0207700226ca22e1876ec915175c3b61f Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 26 Mar 2024 13:23:27 +0100 Subject: [PATCH 08/14] Implemented reviewer feedback - Fixed errors caused by unexpected behavior of reduction map - Added examples for bimultiplicativity - Corrected docstring --- .../rings/function_field/function_field.py | 86 ++++++++----------- 1 file changed, 36 insertions(+), 50 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index b98636308b6..ab5f1de7681 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1228,19 +1228,20 @@ def completion(self, place, name=None, prec=None, gen_name=None): return FunctionFieldCompletion(self, place, name=name, prec=prec, gen_name=gen_name) def hilbert_symbol(self, a, b, P): - """ + r""" Return the Hilbert symbol `(a,b)_{F_P}` (where `F_P` is the - completion of ``self`` at the place ``P``). + completion of this function field at the place `P`). - The Hilbert symbol `(a,b)_{F_P}` is `0` if one of ``a`` or ``b`` is + The Hilbert symbol `(a,b)_{F_P}` is `0` if one of `a` or `b` is zero. Otherwise it takes the value `1` if the quaternion algebra defined by `(a,b)` over `F_P` is split, and `-1` if it is division. - We use the completion at the place `P` to compute the valuations `v(a)` - and `v(b)` as well as elements `a0` and `b0` such that, for a uniformizer - `pi` of the unique maximal ideal of the completion, the elememts - `a*pi^{-v(a))}` and `a0` respectively the elements `b*pi^{-v(b)}` and `b0` - are congruent modulo `pi`. Motivated by formula 12.4.10 in [Voi2021]_. + For the valuation `\nu` belonging to the completion of the function + field at `P`, we compute the valuations `\nu(a)` and `\nu(b)` as well + as elements `a_0` and `b_0` such that, for a uniformizer `\pi` of + `\nu`, the elememts `a*\pi^{-\nu(a))}` and `a_0` respectively the + elements `b*\pi^{-\nu(b)}` and `b_0` are congruent modulo `\pi`. + Motivated by formula 12.4.10 in [Voi2021]_. Currently only tested for function fields separable over their base since places are not fully supported for other function fields. Only @@ -1248,9 +1249,9 @@ def hilbert_symbol(self, a, b, P): INPUT: - - ``a`` and ``b``: Elements of ``self`` + - ``a`` and ``b``: elements of this function field - - ``P``: A place of ``self`` + - ``P``: a place of this function field EXAMPLES:: @@ -1269,53 +1270,38 @@ def hilbert_symbol(self, a, b, P): sage: K.hilbert_symbol(c, d, Q) 1 + Check that the Hilbert symbol is bimultiplicative:: + sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) sage: f = ((x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 ....: + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4) sage: L. = K.extension(f) + sage: a = L.random_element() + ....: b = L.random_element() + ....: c = L.random_element() + sage: P = L.places_above(K.places()[0])[1] - sage: a = (((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 - ....: + ((3*x + 4)/(x + 4))*y^2 - ....: + ((x^4 + 2*x^3 + 3*x^2 + x + 2)/(x^4 + x^3 + x^2 + 2))*y - ....: + (x^4 + x^2 + 2*x + 4)/(x^2 + 2*x + 2)) - sage: b = (((x + 1)/(x + 4))*y^4 - ....: + ((2*x^4 + 3*x^3 + 3*x^2 + 3)/(x^2 + 2*x + 2))*y^3 + (2/(x + 4))*y^2 - ....: + ((4*x^4 + 4*x^3 + 4*x^2 + 2)/(x^4 + x^3 + x^2 + 2))*y - ....: + (x^3 + x)/(x^3 + x^2 + x + 2)) - sage: L.hilbert_symbol(a, b, P) - 1 - sage: Q = L.places_above(K.places()[1])[0] - sage: c = (((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4 - ....: + ((3*x^4 + 2*x^3 + 2*x^2 + 4*x + 3)/(x^3 + 3*x^2 + x))*y^3 - ....: + ((3*x + 4)/(x^3 + 3*x^2 + x))*y^2 - ....: + ((4*x + 2)/(x^5 + 2*x^4 + 4*x^3 + 2*x^2 + x))*y - ....: + (x^3 + 2*x^2 + 4)/(x^3 + 3*x^2 + x)) - sage: d = (((2*x^4 + x^2 + 2*x + 3)/(x^2 + 2*x + 2))*y^4 - ....: + ((4*x^2 + 2)/(x^2 + 2*x + 2))*y^3 + ((3*x^2 + 3*x + 3)/(x^2 + 1))*y^2 - ....: + ((x^6 + 4*x^5 + 4*x^4 + 3*x^3 + 3*x^2 + x + 3)/(x^4 + x^3 + x^2 + 2))*y - ....: + (4*x^3 + 4*x^2 + 2*x + 2)/(x + 4)) - sage: L.hilbert_symbol(c, d, Q) - -1 + sage: (L.hilbert_symbol(a, b, P) * L.hilbert_symbol(a, c, P) + ....: == L.hilbert_symbol(a, b*c, P)) + True + sage: (L.hilbert_symbol(a, c, P) * L.hilbert_symbol(b, c, P) + ....: == L.hilbert_symbol(a*b, c, P) + True - sage: K. = FunctionField(GF(3)); R. = PolynomialRing(K) - sage: g = (((2*x + 1)/x)*T^5 + ((2*x + 1)/(x + 1))*T^4 + ((x^2 + 1)/x)*T^3 - ....: + (2*x/(x^2 + 2*x + 2))*T^2 + 2*T + (2*x + 2)/(x + 2)) - sage: L. = K.extension(g) - sage: P = L.places_above(K.places()[1])[1] - sage: a = (((x + 2)/(x + 1))*y^4 + ((x^4 + 2*x^2 + 1)/(x^2 + 2*x))*y^3 - ....: + ((x^5 + x^2 + 2*x + 1)/(x^4 + 2*x^3 + x^2 + x + 1))*y^2 - ....: + ((2*x + 2)/(x + 2))*y + (2*x^4 + x^3 + x^2 + 2*x + 2)/(x^2 + x + 1)) - sage: b = (((x^2 + x + 1)/(x^2 + 2*x + 1))*y^4 + ((2*x^2 + x + 1)/(x + 1))*y^3 - ....: + ((x^5 + x^4 + 2*x^3 + x^2 + 2*x + 2)/(x^3 + x + 2))*y^2 - ....: + (2*x/(x^2 + 2))*y + 2*x^2 + 2*x + 1) - sage: L.hilbert_symbol(a, b, P) - -1 + sage: Q = L.places_above(K.places()[1])[0] + sage: (L.hilbert_symbol(a, b, Q) * L.hilbert_symbol(a, c, Q) + ....: == L.hilbert_symbol(a, b*c, Q)) + True + sage: (L.hilbert_symbol(a, c, Q) * L.hilbert_symbol(b, c, Q) + ....: == L.hilbert_symbol(a*b, c, Q) + True """ if not self.is_global(): raise NotImplementedError('only supported for global function fields') if self.characteristic() == 2: - raise NotImplementedError('only supported in odd characteristic') + raise NotImplementedError('Hilbert symbol currently only implemented' + ' for odd characteristic function fields') if not (a in self and b in self): raise ValueError('a and b must be elements of the function field') @@ -1340,13 +1326,13 @@ def hilbert_symbol(self, a, b, P): v_b = ser_b.valuation() b0 = ser_b.coefficients()[0] - # Get the residue field of the completion, together with the residue map - k, _, tau = self.residue_field(P) + # Get the residue field of the completion together with the necssary exponent + k = sigma.codomain().base_ring() e = (k.order() - 1) >> 1 # Use Euler's criterion to compute the powers of Legendre symbols - a_rd_pw = tau(a0)**(v_b * e) - b_rd_pw = tau(b0)**(v_a * e) + a_rd_pw = a0**(v_b * e) + b_rd_pw = b0**(v_a * e) # Finally, put the result together and transform it into the correct output res = k(-1)**(v_a * v_b * e) * a_rd_pw * b_rd_pw From a06d3f059b68759739dd1a324f927d76404e8c4c Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 26 Mar 2024 13:29:03 +0100 Subject: [PATCH 09/14] Removed redundant function call in examples --- src/sage/rings/function_field/function_field.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index ab5f1de7681..668433d5f84 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1281,19 +1281,17 @@ def hilbert_symbol(self, a, b, P): ....: c = L.random_element() sage: P = L.places_above(K.places()[0])[1] - sage: (L.hilbert_symbol(a, b, P) * L.hilbert_symbol(a, c, P) - ....: == L.hilbert_symbol(a, b*c, P)) + sage: hs_a_c = L.hilbert_symbol(a, c, P) + sage: L.hilbert_symbol(a, b, P) * hs_a_c == L.hilbert_symbol(a, b*c, P) True - sage: (L.hilbert_symbol(a, c, P) * L.hilbert_symbol(b, c, P) - ....: == L.hilbert_symbol(a*b, c, P) + sage: hs_a_c * L.hilbert_symbol(b, c, P) == L.hilbert_symbol(a*b, c, P) True sage: Q = L.places_above(K.places()[1])[0] - sage: (L.hilbert_symbol(a, b, Q) * L.hilbert_symbol(a, c, Q) - ....: == L.hilbert_symbol(a, b*c, Q)) + sage: hs_a_c = L.hilbert_symbol(a, c, P) + sage: L.hilbert_symbol(a, b, Q) * hs_a_c == L.hilbert_symbol(a, b*c, Q) True - sage: (L.hilbert_symbol(a, c, Q) * L.hilbert_symbol(b, c, Q) - ....: == L.hilbert_symbol(a*b, c, Q) + sage: hs_a_c * L.hilbert_symbol(b, c, Q) == L.hilbert_symbol(a*b, c, Q) True """ if not self.is_global(): From 894a6b4b38d04a65dc6aec70130bc5ad6e0ef880 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 26 Mar 2024 13:54:01 +0100 Subject: [PATCH 10/14] Change characteristic error type Amend: Fixed weird spacing --- src/sage/rings/function_field/function_field.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index 668433d5f84..ee2a985e9a9 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1298,8 +1298,8 @@ def hilbert_symbol(self, a, b, P): raise NotImplementedError('only supported for global function fields') if self.characteristic() == 2: - raise NotImplementedError('Hilbert symbol currently only implemented' - ' for odd characteristic function fields') + raise ValueError('Hilbert symbol is only defined for' + ' odd characteristic function fields') if not (a in self and b in self): raise ValueError('a and b must be elements of the function field') From 0e6ef5cb7272da3d41d90aa205873f66e1cc0fc1 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Tue, 26 Mar 2024 16:52:07 +0100 Subject: [PATCH 11/14] Fixed doctest --- src/sage/rings/function_field/function_field.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index ee2a985e9a9..b328749f8f9 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1277,8 +1277,8 @@ def hilbert_symbol(self, a, b, P): ....: + (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4) sage: L. = K.extension(f) sage: a = L.random_element() - ....: b = L.random_element() - ....: c = L.random_element() + sage: b = L.random_element() + sage: c = L.random_element() sage: P = L.places_above(K.places()[0])[1] sage: hs_a_c = L.hilbert_symbol(a, c, P) @@ -1288,7 +1288,7 @@ def hilbert_symbol(self, a, b, P): True sage: Q = L.places_above(K.places()[1])[0] - sage: hs_a_c = L.hilbert_symbol(a, c, P) + sage: hs_a_c = L.hilbert_symbol(a, c, Q) sage: L.hilbert_symbol(a, b, Q) * hs_a_c == L.hilbert_symbol(a, b*c, Q) True sage: hs_a_c * L.hilbert_symbol(b, c, Q) == L.hilbert_symbol(a*b, c, Q) From 38259b6e19dee72a0cd7352622b2067627c4745d Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Wed, 27 Mar 2024 10:44:38 +0100 Subject: [PATCH 12/14] Small changes - Fixed Lint issue - Implemented reviewer feedback - Added checks for symmetry --- .../rings/function_field/function_field.py | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index b328749f8f9..d1652873c71 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1238,7 +1238,7 @@ def hilbert_symbol(self, a, b, P): For the valuation `\nu` belonging to the completion of the function field at `P`, we compute the valuations `\nu(a)` and `\nu(b)` as well - as elements `a_0` and `b_0` such that, for a uniformizer `\pi` of + as elements `a_0` and `b_0` such that, for a uniformizer `\pi` of `\nu`, the elememts `a*\pi^{-\nu(a))}` and `a_0` respectively the elements `b*\pi^{-\nu(b)}` and `b_0` are congruent modulo `\pi`. Motivated by formula 12.4.10 in [Voi2021]_. @@ -1249,9 +1249,9 @@ def hilbert_symbol(self, a, b, P): INPUT: - - ``a`` and ``b``: elements of this function field + - ``a`` and ``b`` -- elements of this function field - - ``P``: a place of this function field + - ``P`` -- a place of this function field EXAMPLES:: @@ -1270,7 +1270,7 @@ def hilbert_symbol(self, a, b, P): sage: K.hilbert_symbol(c, d, Q) 1 - Check that the Hilbert symbol is bimultiplicative:: + Check that the Hilbert symbol is symmetric and bimultiplicative:: sage: K. = FunctionField(GF(5)); R. = PolynomialRing(K) sage: f = ((x^2 + 2*x + 2)*T^5 + (4*x^2 + 2*x + 3)*T^4 + 3*T^3 + 4*T^2 @@ -1279,19 +1279,23 @@ def hilbert_symbol(self, a, b, P): sage: a = L.random_element() sage: b = L.random_element() sage: c = L.random_element() - sage: P = L.places_above(K.places()[0])[1] - sage: hs_a_c = L.hilbert_symbol(a, c, P) - sage: L.hilbert_symbol(a, b, P) * hs_a_c == L.hilbert_symbol(a, b*c, P) + sage: Q = L.places_above(K.places()[1])[0] + + sage: hP_a_c = L.hilbert_symbol(a, c, P) + sage: hP_a_c == L.hilbert_symbol(c, a, P) + True + sage: L.hilbert_symbol(a, b, P) * hP_a_c == L.hilbert_symbol(a, b*c, P) True - sage: hs_a_c * L.hilbert_symbol(b, c, P) == L.hilbert_symbol(a*b, c, P) + sage: hP_a_c * L.hilbert_symbol(b, c, P) == L.hilbert_symbol(a*b, c, P) True - sage: Q = L.places_above(K.places()[1])[0] - sage: hs_a_c = L.hilbert_symbol(a, c, Q) - sage: L.hilbert_symbol(a, b, Q) * hs_a_c == L.hilbert_symbol(a, b*c, Q) + sage: hQ_a_c = L.hilbert_symbol(a, c, Q) + sage: hQ_a_c == L.hilbert_symbol(c, a, Q) + True + sage: L.hilbert_symbol(a, b, Q) * hQ_a_c == L.hilbert_symbol(a, b*c, Q) True - sage: hs_a_c * L.hilbert_symbol(b, c, Q) == L.hilbert_symbol(a*b, c, Q) + sage: hQ_a_c * L.hilbert_symbol(b, c, Q) == L.hilbert_symbol(a*b, c, Q) True """ if not self.is_global(): @@ -1326,7 +1330,7 @@ def hilbert_symbol(self, a, b, P): # Get the residue field of the completion together with the necssary exponent k = sigma.codomain().base_ring() - e = (k.order() - 1) >> 1 + e = (k.order() - 1) // 2 # Use Euler's criterion to compute the powers of Legendre symbols a_rd_pw = a0**(v_b * e) @@ -1334,7 +1338,9 @@ def hilbert_symbol(self, a, b, P): # Finally, put the result together and transform it into the correct output res = k(-1)**(v_a * v_b * e) * a_rd_pw * b_rd_pw - return (res.is_one() << 1) - 1 + + from sage.rings.integer import Integer + return Integer(1) if res.is_one() else Integer(-1) def extension_constant_field(self, k): """ From f25c72c614c924f2cb56ad130927825e77c5e492 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Thu, 28 Mar 2024 02:54:13 +0100 Subject: [PATCH 13/14] Implemented review feedback - Rewrote docstring accordings to suggestions - Fixed typo in comment --- .../rings/function_field/function_field.py | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index d1652873c71..c7538a2bdb7 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1229,23 +1229,10 @@ def completion(self, place, name=None, prec=None, gen_name=None): def hilbert_symbol(self, a, b, P): r""" - Return the Hilbert symbol `(a,b)_{F_P}` (where `F_P` is the - completion of this function field at the place `P`). + Return the Hilbert symbol `(a,b)_{F_P}` for the local field `F_P`. - The Hilbert symbol `(a,b)_{F_P}` is `0` if one of `a` or `b` is - zero. Otherwise it takes the value `1` if the quaternion algebra - defined by `(a,b)` over `F_P` is split, and `-1` if it is division. - - For the valuation `\nu` belonging to the completion of the function - field at `P`, we compute the valuations `\nu(a)` and `\nu(b)` as well - as elements `a_0` and `b_0` such that, for a uniformizer `\pi` of - `\nu`, the elememts `a*\pi^{-\nu(a))}` and `a_0` respectively the - elements `b*\pi^{-\nu(b)}` and `b_0` are congruent modulo `\pi`. - Motivated by formula 12.4.10 in [Voi2021]_. - - Currently only tested for function fields separable over their base - since places are not fully supported for other function fields. Only - implemented for global function fields of odd characteristic. + The local field `F_P` is the completion of this function field `F` + at the place `P`. INPUT: @@ -1253,6 +1240,21 @@ def hilbert_symbol(self, a, b, P): - ``P`` -- a place of this function field + The Hilbert symbol `(a,b)_{F_P}` is `0` if `a` or `b` is zero. + Otherwise it takes the value `1` if the quaternion algebra defined + by `(a,b)` over `F_P` is split, and `-1` if it is a division ring. + + ALGORITHM: + + For the valuation `\nu = \nu_P` of `F`, we compute the valuations + `\nu(a)` and `\nu(b)` as well as elements `a_0` and `b_0` of the + residue field such that for a uniformizer `\pi` at `P`, + `a\pi^{-\nu(a))}` respectively `b\pi^{-\nu(b)}`has the residue class + `a_0` respectively `b_0` modulo `P`. Then the Hilbert symbol is + computed by formula 12.4.10 in [Voi2021]_. + + Currently only implemented for global function fields. + EXAMPLES:: sage: K. = FunctionField(GF(17)) @@ -1328,7 +1330,7 @@ def hilbert_symbol(self, a, b, P): v_b = ser_b.valuation() b0 = ser_b.coefficients()[0] - # Get the residue field of the completion together with the necssary exponent + # Get the residue field of the completion together with the necessary exponent k = sigma.codomain().base_ring() e = (k.order() - 1) // 2 From 4f1e025ac846849d6aebc9297553db4ec27afaa7 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Thu, 28 Mar 2024 04:05:18 +0100 Subject: [PATCH 14/14] Small corrections --- src/sage/rings/function_field/function_field.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/function_field/function_field.py b/src/sage/rings/function_field/function_field.py index c7538a2bdb7..16f83aaea84 100644 --- a/src/sage/rings/function_field/function_field.py +++ b/src/sage/rings/function_field/function_field.py @@ -1241,16 +1241,17 @@ def hilbert_symbol(self, a, b, P): - ``P`` -- a place of this function field The Hilbert symbol `(a,b)_{F_P}` is `0` if `a` or `b` is zero. - Otherwise it takes the value `1` if the quaternion algebra defined - by `(a,b)` over `F_P` is split, and `-1` if it is a division ring. + Otherwise it takes the value `1` if the quaternion algebra + defined by `(a,b)` over `F_P` is split, and `-1` if said + algebra is a division ring. ALGORITHM: For the valuation `\nu = \nu_P` of `F`, we compute the valuations `\nu(a)` and `\nu(b)` as well as elements `a_0` and `b_0` of the residue field such that for a uniformizer `\pi` at `P`, - `a\pi^{-\nu(a))}` respectively `b\pi^{-\nu(b)}`has the residue class - `a_0` respectively `b_0` modulo `P`. Then the Hilbert symbol is + `a\pi^{-\nu(a))}` respectively `b\pi^{-\nu(b)}` has the residue class + `a_0` respectively `b_0` modulo `\pi`. Then the Hilbert symbol is computed by formula 12.4.10 in [Voi2021]_. Currently only implemented for global function fields.