From be7c5f113df9080ecc4843da0073a9fc8e71f1d2 Mon Sep 17 00:00:00 2001 From: Sebastian Spindler Date: Wed, 6 Mar 2024 14:46:12 +0100 Subject: [PATCH] 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`.