\(p\)-Selmer groups of number fields

This file contains code to compute \(K(S,p)\) where

  • \(K\) is a number field

  • \(S\) is a finite set of primes of \(K\)

  • \(p\) is a prime number

For \(m\ge2\), \(K(S,m)\) is defined to be the finite subgroup of \(K^*/(K^*)^m\) consisting of elements represented by \(a\in K^*\) whose valuation at all primes not in \(S\) is a multiple of \(m\). It fits in the short exact sequence

\[1 \rightarrow O^*_{K,S}/(O^*_{K,S})^m \rightarrow K(S,m) \rightarrow Cl_{K,S}[m] \rightarrow 1\]

where \(O^*_{K,S}\) is the group of \(S\)-units of \(K\) and \(Cl_{K,S}\) the \(S\)-class group. When \(m=p\) is prime, \(K(S,p)\) is a finite-dimensional vector space over \(GF(p)\). Its generators come from three sources: units (modulo \(p\)-th powers); generators of the \(p\)-th powers of ideals which are not principal but whose \(p\)-th powers are principal; and generators coming from the prime ideals in \(S\).

The main function here is pSelmerGroup(). This will not normally be used by users, who instead will access it through a method of the NumberField class.

AUTHORS:

  • John Cremona (2005-2021)

sage.rings.number_field.selmer_group.basis_for_p_cokernel(S, C, p)[source]

Return a basis for the group of ideals supported on S (mod \(p\)-th-powers) whose class in the class group C is a \(p\)-th power, together with a function which takes the S-exponents of such an ideal and returns its coordinates on this basis.

INPUT:

  • S – list of prime ideals in a number field K

  • C – (class group) the ideal class group of K

  • p – prime number

OUTPUT:

(tuple) (b, f) where

  • b is a list of ideals which is a basis for the group of ideals supported on S (modulo \(p\)-th powers) whose ideal class is a \(p\)-th power;

  • f is a function which takes such an ideal and returns its coordinates with respect to this basis.

EXAMPLES:

sage: from sage.rings.number_field.selmer_group import basis_for_p_cokernel
sage: x = polygen(ZZ, 'x')
sage: K.<a> = NumberField(x^2 - x + 58)
sage: S = K.ideal(30).support(); S
[Fractional ideal (2, a),
Fractional ideal (2, a + 1),
Fractional ideal (3, a + 1),
Fractional ideal (5, a + 1),
Fractional ideal (5, a + 3)]
sage: C = K.class_group()
sage: C.gens_orders()
(6, 2)
sage: [C(P).exponents() for P in S]
[(5, 0), (1, 0), (3, 1), (1, 1), (5, 1)]
sage: b, f = basis_for_p_cokernel(S, C, 2); b
[Fractional ideal (2), Fractional ideal (15, a + 13), Fractional ideal (5)]
sage: b, f = basis_for_p_cokernel(S, C, 3); b
[Fractional ideal (50, a + 18),
Fractional ideal (10, a + 3),
Fractional ideal (3, a + 1),
Fractional ideal (5)]
sage: b, f = basis_for_p_cokernel(S, C, 5); b
[Fractional ideal (2, a),
Fractional ideal (2, a + 1),
Fractional ideal (3, a + 1),
Fractional ideal (5, a + 1),
Fractional ideal (5, a + 3)]
>>> from sage.all import *
>>> from sage.rings.number_field.selmer_group import basis_for_p_cokernel
>>> x = polygen(ZZ, 'x')
>>> K = NumberField(x**Integer(2) - x + Integer(58), names=('a',)); (a,) = K._first_ngens(1)
>>> S = K.ideal(Integer(30)).support(); S
[Fractional ideal (2, a),
Fractional ideal (2, a + 1),
Fractional ideal (3, a + 1),
Fractional ideal (5, a + 1),
Fractional ideal (5, a + 3)]
>>> C = K.class_group()
>>> C.gens_orders()
(6, 2)
>>> [C(P).exponents() for P in S]
[(5, 0), (1, 0), (3, 1), (1, 1), (5, 1)]
>>> b, f = basis_for_p_cokernel(S, C, Integer(2)); b
[Fractional ideal (2), Fractional ideal (15, a + 13), Fractional ideal (5)]
>>> b, f = basis_for_p_cokernel(S, C, Integer(3)); b
[Fractional ideal (50, a + 18),
Fractional ideal (10, a + 3),
Fractional ideal (3, a + 1),
Fractional ideal (5)]
>>> b, f = basis_for_p_cokernel(S, C, Integer(5)); b
[Fractional ideal (2, a),
Fractional ideal (2, a + 1),
Fractional ideal (3, a + 1),
Fractional ideal (5, a + 1),
Fractional ideal (5, a + 3)]
from sage.rings.number_field.selmer_group import basis_for_p_cokernel
x = polygen(ZZ, 'x')
K.<a> = NumberField(x^2 - x + 58)
S = K.ideal(30).support(); S
C = K.class_group()
C.gens_orders()
[C(P).exponents() for P in S]
b, f = basis_for_p_cokernel(S, C, 2); b
b, f = basis_for_p_cokernel(S, C, 3); b
b, f = basis_for_p_cokernel(S, C, 5); b
sage.rings.number_field.selmer_group.coords_in_U_mod_p(u, U, p)[source]

Return coordinates of a unit u with respect to a basis of the \(p\)-cotorsion \(U/U^p\) of the unit group U.

INPUT:

  • u – (algebraic unit) a unit in a number field K

  • U – (unit group) the unit group of K

  • p – prime number

OUTPUT:

The coordinates of the unit \(u\) in the \(p\)-cotorsion group \(U/U^p\).

ALGORITHM:

Take the coordinate vector of \(u\) with respect to the generators of the unit group, drop the coordinate of the roots of unity factor if it is prime to \(p\), and reduce the vector mod \(p\).

EXAMPLES:

sage: from sage.rings.number_field.selmer_group import coords_in_U_mod_p
sage: x = polygen(ZZ, 'x')
sage: K.<a> = NumberField(x^4 - 5*x^2 + 1)
sage: U = K.unit_group()
sage: U
Unit group with structure C2 x Z x Z x Z of Number Field in a with defining polynomial x^4 - 5*x^2 + 1
sage: u0, u1, u2, u3 = U.gens_values()
sage: u = u1*u2^2*u3^3
sage: coords_in_U_mod_p(u,U,2)
[0, 1, 0, 1]
sage: coords_in_U_mod_p(u,U,3)
[1, 2, 0]
sage: u*=u0
sage: coords_in_U_mod_p(u,U,2)
[1, 1, 0, 1]
sage: coords_in_U_mod_p(u,U,3)
[1, 2, 0]
>>> from sage.all import *
>>> from sage.rings.number_field.selmer_group import coords_in_U_mod_p
>>> x = polygen(ZZ, 'x')
>>> K = NumberField(x**Integer(4) - Integer(5)*x**Integer(2) + Integer(1), names=('a',)); (a,) = K._first_ngens(1)
>>> U = K.unit_group()
>>> U
Unit group with structure C2 x Z x Z x Z of Number Field in a with defining polynomial x^4 - 5*x^2 + 1
>>> u0, u1, u2, u3 = U.gens_values()
>>> u = u1*u2**Integer(2)*u3**Integer(3)
>>> coords_in_U_mod_p(u,U,Integer(2))
[0, 1, 0, 1]
>>> coords_in_U_mod_p(u,U,Integer(3))
[1, 2, 0]
>>> u*=u0
>>> coords_in_U_mod_p(u,U,Integer(2))
[1, 1, 0, 1]
>>> coords_in_U_mod_p(u,U,Integer(3))
[1, 2, 0]
from sage.rings.number_field.selmer_group import coords_in_U_mod_p
x = polygen(ZZ, 'x')
K.<a> = NumberField(x^4 - 5*x^2 + 1)
U = K.unit_group()
U
u0, u1, u2, u3 = U.gens_values()
u = u1*u2^2*u3^3
coords_in_U_mod_p(u,U,2)
coords_in_U_mod_p(u,U,3)
u*=u0
coords_in_U_mod_p(u,U,2)
coords_in_U_mod_p(u,U,3)
sage.rings.number_field.selmer_group.pSelmerGroup(K, S, p, proof=None, debug=False)[source]

Return the \(p\)-Selmer group \(K(S,p)\) of the number field \(K\) with respect to the prime ideals in S.

INPUT:

  • K – a number field or \(\QQ\)

  • S – list of prime ideals in \(K\), or prime numbers when \(K\) is \(\QQ\)

  • p – a prime number

  • proof – if True, compute the class group provably correctly. Default is True. Call proof.number_field() to change this default globally.

  • debug – boolean (default: False); debug flag

OUTPUT:

(tuple) KSp, KSp_gens, from_KSp, to_KSp where

  • KSp is an abstract vector space over \(GF(p)\) isomorphic to \(K(S,p)\);

  • KSp_gens is a list of elements of \(K^*\) generating \(K(S,p)\);

  • from_KSp is a function from KSp to \(K^*\) implementing the isomorphism from the abstract \(K(S,p)\) to \(K(S,p)\) as a subgroup of \(K^*/(K^*)^p\);

  • to_KSP is a partial function from \(K^*\) to KSp, defined on elements \(a\) whose image in \(K^*/(K^*)^p\) lies in \(K(S,p)\), mapping them via the inverse isomorphism to the abstract vector space KSp.

ALGORITHM:

The list of generators of \(K(S,p)\) is the concatenation of three sublists, called alphalist, betalist and ulist in the code. Only alphalist depends on the primes in \(S\).

  • ulist is a basis for \(U/U^p\) where \(U\) is the unit group. This is the list of fundamental units, including the generator of the group of roots of unity if its order is divisible by \(p\). These have valuation \(0\) at all primes.

  • betalist is a list of the generators of the \(p\)-th powers of ideals which generate the \(p\)-torsion in the class group (so is empty if the class number is prime to \(p\)). These have valuation divisible by \(p\) at all primes.

  • alphalist is a list of generators for each ideal \(A\) in a basis of those ideals supported on \(S\) (modulo \(p\)-th powers of ideals) which are \(p\)-th powers in the class group. We find \(B\) such that \(A/B^p\) is principal and take a generator of it, for each \(A\) in a generating set. As a special case, if all the ideals in \(S\) are principal then alphalist is a list of their generators.

The map from the abstract space to \(K^*\) is easy: we just take the product of the generators to powers given by the coefficient vector. No attempt is made to reduce the resulting product modulo \(p\)-th powers.

The reverse map is more complicated. Given \(a\in K^*\):

  • write the principal ideal \((a)\) in the form \(AB^p\) with \(A\) supported by \(S\) and \(p\)-th power free. If this fails, then \(a\) does not represent an element of \(K(S,p)\) and an error is raised.

  • set \(I_S\) to be the group of ideals spanned by \(S\) mod \(p\)-th powers, and \(I_{S,p}\) the subgroup of \(I_S\) which maps to \(0\) in \(C/C^p\).

  • Convert \(A\) to an element of \(I_{S,p}\), hence find the coordinates of \(a\) with respect to the generators in alphalist.

  • after dividing out by \(A\), now \((a)=B^p\) (with a different \(a\) and \(B\)). Write the ideal class \([B]\), whose \(p\)-th power is trivial, in terms of the generators of \(C[p]\); then \(B=(b)B_1\), where the coefficients of \(B_1\) with respect to generators of \(C[p]\) give the coordinates of the result with respect to the generators in betalist.

  • after dividing out by \(B\), and by \(b^p\), we now have \((a)=(1)\), so \(a\) is a unit, which can be expressed in terms of the unit generators.

EXAMPLES:

Over \(\QQ\) the unit contribution is trivial unless \(p=2\) and the class group is trivial:

sage: from sage.rings.number_field.selmer_group import pSelmerGroup
sage: QS2, gens, fromQS2, toQS2 = pSelmerGroup(QQ, [2,3], 2)
sage: QS2
Vector space of dimension 3 over Finite Field of size 2
sage: gens
[2, 3, -1]
sage: a = fromQS2([1,1,1]); a.factor()
-1 * 2 * 3
sage: toQS2(-6)
(1, 1, 1)

sage: QS3, gens, fromQS3, toQS3 = pSelmerGroup(QQ, [2,13], 3)
sage: QS3
Vector space of dimension 2 over Finite Field of size 3
sage: gens
[2, 13]
sage: a = fromQS3([5,4]); a.factor()
2^5 * 13^4
sage: toQS3(a)
(2, 1)
sage: toQS3(a) == QS3([5,4])
True
>>> from sage.all import *
>>> from sage.rings.number_field.selmer_group import pSelmerGroup
>>> QS2, gens, fromQS2, toQS2 = pSelmerGroup(QQ, [Integer(2),Integer(3)], Integer(2))
>>> QS2
Vector space of dimension 3 over Finite Field of size 2
>>> gens
[2, 3, -1]
>>> a = fromQS2([Integer(1),Integer(1),Integer(1)]); a.factor()
-1 * 2 * 3
>>> toQS2(-Integer(6))
(1, 1, 1)

>>> QS3, gens, fromQS3, toQS3 = pSelmerGroup(QQ, [Integer(2),Integer(13)], Integer(3))
>>> QS3
Vector space of dimension 2 over Finite Field of size 3
>>> gens
[2, 13]
>>> a = fromQS3([Integer(5),Integer(4)]); a.factor()
2^5 * 13^4
>>> toQS3(a)
(2, 1)
>>> toQS3(a) == QS3([Integer(5),Integer(4)])
True
from sage.rings.number_field.selmer_group import pSelmerGroup
QS2, gens, fromQS2, toQS2 = pSelmerGroup(QQ, [2,3], 2)
QS2
gens
a = fromQS2([1,1,1]); a.factor()
toQS2(-6)
QS3, gens, fromQS3, toQS3 = pSelmerGroup(QQ, [2,13], 3)
QS3
gens
a = fromQS3([5,4]); a.factor()
toQS3(a)
toQS3(a) == QS3([5,4])

A real quadratic field with class number 2, where the fundamental unit is a generator, and the class group provides another generator when \(p=2\):

sage: K.<a> = QuadraticField(-5)
sage: K.class_number()
2
sage: P2 = K.ideal(2, -a+1)
sage: P3 = K.ideal(3, a+1)
sage: P5 = K.ideal(a)
sage: KS2, gens, fromKS2, toKS2 = pSelmerGroup(K, [P2, P3, P5], 2)
sage: KS2
Vector space of dimension 4 over Finite Field of size 2
sage: gens
[a + 1, a, 2, -1]
>>> from sage.all import *
>>> K = QuadraticField(-Integer(5), names=('a',)); (a,) = K._first_ngens(1)
>>> K.class_number()
2
>>> P2 = K.ideal(Integer(2), -a+Integer(1))
>>> P3 = K.ideal(Integer(3), a+Integer(1))
>>> P5 = K.ideal(a)
>>> KS2, gens, fromKS2, toKS2 = pSelmerGroup(K, [P2, P3, P5], Integer(2))
>>> KS2
Vector space of dimension 4 over Finite Field of size 2
>>> gens
[a + 1, a, 2, -1]
K.<a> = QuadraticField(-5)
K.class_number()
P2 = K.ideal(2, -a+1)
P3 = K.ideal(3, a+1)
P5 = K.ideal(a)
KS2, gens, fromKS2, toKS2 = pSelmerGroup(K, [P2, P3, P5], 2)
KS2
gens

Each generator must have even valuation at primes not in \(S\):

sage: [K.ideal(g).factor() for g in gens]
[(Fractional ideal (2, a + 1)) * (Fractional ideal (3, a + 1)),
Fractional ideal (a),
(Fractional ideal (2, a + 1))^2,
1]

sage: toKS2(10)
(0, 0, 1, 1)
sage: fromKS2([0,0,1,1])
-2
sage: K(10/(-2)).is_square()
True

sage: KS3, gens, fromKS3, toKS3 = pSelmerGroup(K, [P2, P3, P5], 3)
sage: KS3
Vector space of dimension 3 over Finite Field of size 3
sage: gens
[1/2, 1/4*a + 1/4, a]
>>> from sage.all import *
>>> [K.ideal(g).factor() for g in gens]
[(Fractional ideal (2, a + 1)) * (Fractional ideal (3, a + 1)),
Fractional ideal (a),
(Fractional ideal (2, a + 1))^2,
1]

>>> toKS2(Integer(10))
(0, 0, 1, 1)
>>> fromKS2([Integer(0),Integer(0),Integer(1),Integer(1)])
-2
>>> K(Integer(10)/(-Integer(2))).is_square()
True

>>> KS3, gens, fromKS3, toKS3 = pSelmerGroup(K, [P2, P3, P5], Integer(3))
>>> KS3
Vector space of dimension 3 over Finite Field of size 3
>>> gens
[1/2, 1/4*a + 1/4, a]
[K.ideal(g).factor() for g in gens]
toKS2(10)
fromKS2([0,0,1,1])
K(10/(-2)).is_square()
KS3, gens, fromKS3, toKS3 = pSelmerGroup(K, [P2, P3, P5], 3)
KS3
gens

The to and from maps are inverses of each other:

sage: K.<a> = QuadraticField(-5)
sage: S = K.ideal(30).support()
sage: KS2, gens, fromKS2, toKS2 = pSelmerGroup(K, S, 2)
sage: KS2
Vector space of dimension 5 over Finite Field of size 2
sage: assert all(toKS2(fromKS2(v))==v for v in KS2)
sage: KS3, gens, fromKS3, toKS3 = pSelmerGroup(K, S, 3)
sage: KS3
Vector space of dimension 4 over Finite Field of size 3
sage: assert all(toKS3(fromKS3(v))==v for v in KS3)
>>> from sage.all import *
>>> K = QuadraticField(-Integer(5), names=('a',)); (a,) = K._first_ngens(1)
>>> S = K.ideal(Integer(30)).support()
>>> KS2, gens, fromKS2, toKS2 = pSelmerGroup(K, S, Integer(2))
>>> KS2
Vector space of dimension 5 over Finite Field of size 2
>>> assert all(toKS2(fromKS2(v))==v for v in KS2)
>>> KS3, gens, fromKS3, toKS3 = pSelmerGroup(K, S, Integer(3))
>>> KS3
Vector space of dimension 4 over Finite Field of size 3
>>> assert all(toKS3(fromKS3(v))==v for v in KS3)
K.<a> = QuadraticField(-5)
S = K.ideal(30).support()
KS2, gens, fromKS2, toKS2 = pSelmerGroup(K, S, 2)
KS2
assert all(toKS2(fromKS2(v))==v for v in KS2)
KS3, gens, fromKS3, toKS3 = pSelmerGroup(K, S, 3)
KS3
assert all(toKS3(fromKS3(v))==v for v in KS3)