Skip to content

Commit

Permalink
Implemented the (local) Hilbert symbol for global function fields
Browse files Browse the repository at this point in the history
Amends: Changed some documentation, adapted characteristic check to check ==2 instead of <= 2
  • Loading branch information
S17A05 committed Mar 6, 2024
1 parent cb8e15b commit be7c5f1
Showing 1 changed file with 127 additions and 0 deletions.
127 changes: 127 additions & 0 deletions src/sage/rings/function_field/function_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.<x> = 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)

Check failure on line 1257 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Exception raised: Traceback (most recent call last): File "/sage/src/sage/doctest/forker.py", line 712, in _run self.compile_and_execute(example, compiler, test.globs) File "/sage/src/sage/doctest/forker.py", line 1147, in compile_and_execute exec(compiled, globs) File "<doctest sage.rings.function_field.function_field.FunctionField.hilbert_symbol[4]>", line 1, in <module> K.hilbert_symbol(a, b, P) File "/sage/src/sage/rings/function_field/function_field.py", line 1352, in hilbert_symbol return ZZ(-1) ^^ NameError: name 'ZZ' is not defined
-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)

Check failure on line 1264 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Exception raised: Traceback (most recent call last): File "/sage/src/sage/doctest/forker.py", line 712, in _run self.compile_and_execute(example, compiler, test.globs) File "/sage/src/sage/doctest/forker.py", line 1147, in compile_and_execute exec(compiled, globs) File "<doctest sage.rings.function_field.function_field.FunctionField.hilbert_symbol[8]>", line 1, in <module> K.hilbert_symbol(c, d, Q) File "/sage/src/sage/rings/function_field/function_field.py", line 1350, in hilbert_symbol return ZZ(1) ^^ NameError: name 'ZZ' is not defined
1
sage: K.<x> = FunctionField(GF(5)); R.<T> = 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

Check failure on line 1268 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Got:
+ (2/(x^2 + 4*x + 1))*T + 3*x^2 + 2*x + 4
sage: L.<y> = K.extension(f)
sage: P = L.places_above(K.places()[0])[1]

Check failure on line 1271 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Exception raised: Traceback (most recent call last): File "sage/structure/category_object.pyx", line 855, in sage.structure.category_object.CategoryObject.getattr_from_category return self._cached_methods[name] KeyError: 'places_above' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/sage/src/sage/doctest/forker.py", line 712, in _run self.compile_and_execute(example, compiler, test.globs) File "/sage/src/sage/doctest/forker.py", line 1147, in compile_and_execute exec(compiled, globs) File "<doctest sage.rings.function_field.function_field.FunctionField.hilbert_symbol[12]>", line 1, in <module> P = L.places_above(K.places()[Integer(0)])[Integer(1)] ^^^^^^^^^^^^^^ File "sage/structure/category_object.pyx", line 849, in sage.structure.category_object.CategoryObject.__getattr__ return self.getattr_from_category(name) File "sage/structure/category_object.pyx", line 864, in sage.structure.category_object.CategoryObject.getattr_from_category attr = getattr_from_other_class(self, cls, name) File "sage/cpython/getattr.pyx", line 357, in sage.cpython.getattr.getattr_from_other_class raise AttributeError(dummy_error_message) AttributeError: 'FunctionField_polymod_with_category' object has no attribute 'places_above'
sage: a = ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3

Check failure on line 1272 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Got:
+ ((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

Check failure on line 1276 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Got:
+ ((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)

Check failure on line 1280 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Exception raised: Traceback (most recent call last): File "/sage/src/sage/doctest/forker.py", line 712, in _run self.compile_and_execute(example, compiler, test.globs) File "/sage/src/sage/doctest/forker.py", line 1147, in compile_and_execute exec(compiled, globs) File "<doctest sage.rings.function_field.function_field.FunctionField.hilbert_symbol[15]>", line 1, in <module> L.hilbert_symbol(a, b, P) File "/sage/src/sage/rings/function_field/function_field.py", line 1329, in hilbert_symbol ser_a = sigma(a) ^^^^^^^^ File "sage/categories/map.pyx", line 800, in sage.categories.map.Map.__call__ return self._call_(x) File "sage/categories/map.pyx", line 819, in sage.categories.map.Map._call_ cpdef Element _call_(self, x) noexcept: File "/sage/src/sage/rings/function_field/maps.py", line 851, in _call_ return self._expand(f, prec=None) ^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/sage/src/sage/rings/function_field/maps.py", line 903, in _expand val = f.valuation(place) ^^^^^^^^^^^^^^^^^^ File "sage/rings/function_field/element.pyx", line 625, in sage.rings.function_field.element.FunctionFieldElement.valuation ideal = prime.ring().ideal(self) File "/sage/src/sage/rings/function_field/order_rational.py", line 568, in ideal gens = [K(g) for g in gens] ^^^^^^^^^^^^^^^^^^^^ File "/sage/src/sage/rings/function_field/order_rational.py", line 568, in <listcomp> gens = [K(g) for g in gens] ^^^^ File "sage/structure/parent.pyx", line 901, in sage.structure.parent.Parent.__call__ return mor._call_(x) File "sage/categories/map.pyx", line 1726, in sage.categories.map.FormalCompositeMap._call_ x = f._call_(x) File "sage/categories/morphism.pyx", line 590, in sage.categories.morphism.SetMorphism._call_ cpdef Element _call_(self, x) noexcept: File "sage/categories/morphism.pyx", line 609, in sage.categories.morphism.SetMorphism._call_ return self._function(x) File "/sage/src/sage/rings/function_field/function_field_polymod.py", line 283, in _to_base_field raise ValueError("%r is not an element of the base field" % (f,)) ValueError: ((3*x^3 + 2*x^2 + x + 1)/(x + 2))*y^4 + (4/(x + 4))*y^3 is not an element of the base field
1
sage: Q = L.places_above(K.places()[1])[0]

Check failure on line 1282 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Exception raised: Traceback (most recent call last): File "sage/structure/category_object.pyx", line 855, in sage.structure.category_object.CategoryObject.getattr_from_category return self._cached_methods[name] KeyError: 'places_above' During handling of the above exception, another exception occurred: Traceback (most recent call last): File "/sage/src/sage/doctest/forker.py", line 712, in _run self.compile_and_execute(example, compiler, test.globs) File "/sage/src/sage/doctest/forker.py", line 1147, in compile_and_execute exec(compiled, globs) File "<doctest sage.rings.function_field.function_field.FunctionField.hilbert_symbol[16]>", line 1, in <module> Q = L.places_above(K.places()[Integer(1)])[Integer(0)] ^^^^^^^^^^^^^^ File "sage/structure/category_object.pyx", line 849, in sage.structure.category_object.CategoryObject.__getattr__ return self.getattr_from_category(name) File "sage/structure/category_object.pyx", line 864, in sage.structure.category_object.CategoryObject.getattr_from_category attr = getattr_from_other_class(self, cls, name) File "sage/cpython/getattr.pyx", line 357, in sage.cpython.getattr.getattr_from_other_class raise AttributeError(dummy_error_message) AttributeError: 'FunctionField_polymod_with_category' object has no attribute 'places_above'
sage: c = ((3*x^3 + 3*x^2 + 3*x + 3)/(x^3 + 3*x^2 + x))*y^4

Check failure on line 1283 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Got:
+ ((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

Check failure on line 1288 in src/sage/rings/function_field/function_field.py

View workflow job for this annotation

GitHub Actions / build

Failed example:

Failed example:: Got:
+ ((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.<x> = FunctionField(GF(3)); R.<T> = 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`.
Expand Down

0 comments on commit be7c5f1

Please sign in to comment.