Number Fields: Galois Groups and Class Groups¶
Galois Groups¶
We can compute the Galois group of a number field using the galois_group
function, which by default calls Pari (http://pari.math.u-bordeaux.fr/). You do
not have to worry about installing Pari, since Pari is part of Sage. In
fact, despite appearances much of the difficult algebraic number theory in Sage
is actually done by the Pari C library (be sure to also cite Pari in papers
that use Sage).
sage: K.<alpha> = NumberField(x^6 + 40*x^3 + 1372)
sage: G = K.galois_group()
sage: G
Galois group 6T2 ([3]2) with order 6 of x^6 + 40*x^3 + 1372
>>> from sage.all import *
>>> K = NumberField(x**Integer(6) + Integer(40)*x**Integer(3) + Integer(1372), names=('alpha',)); (alpha,) = K._first_ngens(1)
>>> G = K.galois_group()
>>> G
Galois group 6T2 ([3]2) with order 6 of x^6 + 40*x^3 + 1372
K.<alpha> = NumberField(x^6 + 40*x^3 + 1372) G = K.galois_group() G
Internally G is represented as a group of permutations, but we can also apply any element of G to any element of the field:
sage: G.order()
6
sage: G.gens()
[(1,2)(3,4)(5,6), (1,4,6)(2,5,3)]
sage: f = G.1; f(alpha)
1/36*alpha^4 + 1/18*alpha
>>> from sage.all import *
>>> G.order()
6
>>> G.gens()
[(1,2)(3,4)(5,6), (1,4,6)(2,5,3)]
>>> f = G.gen(1); f(alpha)
1/36*alpha^4 + 1/18*alpha
G.order() G.gens() f = G.1; f(alpha)
Some more advanced number-theoretical tools are available via G:
sage: P = K.primes_above(2)[0]
sage: G.inertia_group(P)
Subgroup generated by [(1,4,6)(2,5,3)] of (Galois group 6T2 ([3]2) with order 6 of x^6 + 40*x^3 + 1372)
sage: sorted([G.artin_symbol(Q) for Q in K.primes_above(5)]) # random order, see Issue #18308
[(1,3)(2,6)(4,5), (1,2)(3,4)(5,6), (1,5)(2,4)(3,6)]
>>> from sage.all import *
>>> P = K.primes_above(Integer(2))[Integer(0)]
>>> G.inertia_group(P)
Subgroup generated by [(1,4,6)(2,5,3)] of (Galois group 6T2 ([3]2) with order 6 of x^6 + 40*x^3 + 1372)
>>> sorted([G.artin_symbol(Q) for Q in K.primes_above(Integer(5))]) # random order, see Issue #18308
[(1,3)(2,6)(4,5), (1,2)(3,4)(5,6), (1,5)(2,4)(3,6)]
P = K.primes_above(2)[0] G.inertia_group(P) sorted([G.artin_symbol(Q) for Q in K.primes_above(5)]) # random order, see Issue #18308
If the number field is not Galois over galois_group
command will construct its Galois closure and return the Galois group of that:
sage: K.<a> = NumberField(x^3 - 2)
sage: G = K.galois_group(names='b'); G
Galois group 3T2 (S3) with order 6 of x^3 - 2
sage: G.order()
6
>>> from sage.all import *
>>> K = NumberField(x**Integer(3) - Integer(2), names=('a',)); (a,) = K._first_ngens(1)
>>> G = K.galois_group(names='b'); G
Galois group 3T2 (S3) with order 6 of x^3 - 2
>>> G.order()
6
K.<a> = NumberField(x^3 - 2) G = K.galois_group(names='b'); G G.order()
Some more Galois groups¶
We compute two more Galois groups of degree
sage: NumberField(x^5 - 2, 'a').galois_group()
Galois group 5T3 (5:4) with order 20 of x^5 - 2
sage: NumberField(x^5 - x + 2, 'a').galois_group()
Galois group 5T5 (S5) with order 120 of x^5 - x + 2
>>> from sage.all import *
>>> NumberField(x**Integer(5) - Integer(2), 'a').galois_group()
Galois group 5T3 (5:4) with order 20 of x^5 - 2
>>> NumberField(x**Integer(5) - x + Integer(2), 'a').galois_group()
Galois group 5T5 (S5) with order 120 of x^5 - x + 2
NumberField(x^5 - 2, 'a').galois_group() NumberField(x^5 - x + 2, 'a').galois_group()
Magma’s Galois group command¶
Recent versions of Magma have an algorithm for computing Galois groups that in
theory applies when the input polynomial has any degree. There are no open
source implementation of this algorithm (as far as I know). If you have Magma,
you can use this algorithm from Sage by calling the galois_group
function
and giving the algorithm='magma'
option. The return value is one of the
groups in the GAP transitive groups database.
sage: K.<a> = NumberField(x^3 - 2)
sage: K.galois_group(type="gap", algorithm='magma') # optional - magma
Galois group Transitive group number 2 of degree 3 of
the Number Field in a with defining polynomial x^3 - 2
>>> from sage.all import *
>>> K = NumberField(x**Integer(3) - Integer(2), names=('a',)); (a,) = K._first_ngens(1)
>>> K.galois_group(type="gap", algorithm='magma') # optional - magma
Galois group Transitive group number 2 of degree 3 of
the Number Field in a with defining polynomial x^3 - 2
K.<a> = NumberField(x^3 - 2) K.galois_group(type="gap", algorithm='magma') # optional - magma
We emphasize that the above example should not work if you don’t have Magma.
Computing complex embeddings¶
You can also enumerate all complex embeddings of a number field:
sage: K.complex_embeddings()
[Ring morphism:
From: Number Field in a with defining polynomial x^3 - 2
To: Complex Field with 53 bits of precision
Defn: a |--> -0.629960524947437 - 1.09112363597172*I,
Ring morphism:
From: Number Field in a with defining polynomial x^3 - 2
To: Complex Field with 53 bits of precision
Defn: a |--> -0.629960524947437 + 1.09112363597172*I,
Ring morphism:
From: Number Field in a with defining polynomial x^3 - 2
To: Complex Field with 53 bits of precision
Defn: a |--> 1.25992104989487]
>>> from sage.all import *
>>> K.complex_embeddings()
[Ring morphism:
From: Number Field in a with defining polynomial x^3 - 2
To: Complex Field with 53 bits of precision
Defn: a |--> -0.629960524947437 - 1.09112363597172*I,
Ring morphism:
From: Number Field in a with defining polynomial x^3 - 2
To: Complex Field with 53 bits of precision
Defn: a |--> -0.629960524947437 + 1.09112363597172*I,
Ring morphism:
From: Number Field in a with defining polynomial x^3 - 2
To: Complex Field with 53 bits of precision
Defn: a |--> 1.25992104989487]
K.complex_embeddings()
Class Numbers and Class Groups¶
The class group class number
command.
sage: L.<a> = NumberField(x^2 + 23)
sage: L.class_number()
3
>>> from sage.all import *
>>> L = NumberField(x**Integer(2) + Integer(23), names=('a',)); (a,) = L._first_ngens(1)
>>> L.class_number()
3
L.<a> = NumberField(x^2 + 23) L.class_number()
Quadratic imaginary fields with class number 1¶
There are only 9 quadratic imaginary field
To find this list using Sage, we first experiment with making lists
in Sage. For example, typing [1..10]
makes the
list of integers between
sage: [1..10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
>>> from sage.all import *
>>> (ellipsis_range(Integer(1),Ellipsis,Integer(10)))
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
[1..10]
We can also make the list of odd integers between [1,3,..,11]
, i.e., by giving the second term
in the arithmetic progression.
sage: [1,3,..,11]
[1, 3, 5, 7, 9, 11]
>>> from sage.all import *
>>> (ellipsis_range(Integer(1),Integer(3),Ellipsis,Integer(11)))
[1, 3, 5, 7, 9, 11]
[1,3,..,11]
Applying this idea, we make the list of negative numbers from
sage: [-1,-2,..,-10]
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
>>> from sage.all import *
>>> (ellipsis_range(-Integer(1),-Integer(2),Ellipsis,-Integer(10)))
[-1, -2, -3, -4, -5, -6, -7, -8, -9, -10]
[-1,-2,..,-10]
Enumerating quadratic imaginary fields with class number 1¶
The first two lines below makes a list
Note
Note that you will not see the … in the output below; this … notation just means that part of the output is omitted below.
sage: w = [-1,-2,..,-200]
sage: v = [D for D in w if D.is_fundamental_discriminant()]
sage: v
[-3, -4, -7, -8, -11, -15, -19, -20, ..., -195, -199]
>>> from sage.all import *
>>> w = (ellipsis_range(-Integer(1),-Integer(2),Ellipsis,-Integer(200)))
>>> v = [D for D in w if D.is_fundamental_discriminant()]
>>> v
[-3, -4, -7, -8, -11, -15, -19, -20, ..., -195, -199]
w = [-1,-2,..,-200] v = [D for D in w if D.is_fundamental_discriminant()] v
Finally, we make the list of QuadraticField(D)
is a shorthand for
NumberField(x^2 - D)
.
sage: [D for D in v if QuadraticField(D,'a').class_number()==1]
[-3, -4, -7, -8, -11, -19, -43, -67, -163]
>>> from sage.all import *
>>> [D for D in v if QuadraticField(D,'a').class_number()==Integer(1)]
[-3, -4, -7, -8, -11, -19, -43, -67, -163]
[D for D in v if QuadraticField(D,'a').class_number()==1]
Of course, we have not proved that this is the list of all
negative
Class number 1 fields¶
A frustrating open problem is to prove that there are infinitely many
number fields with class number
sage: w = [1..1000]
sage: v = [D for D in w if D.is_fundamental_discriminant()]
sage: len(v)
302
sage: len([D for D in v if QuadraticField(D,'a').class_number() == 1])
176
sage: 176.0/302
0.582781456953642
>>> from sage.all import *
>>> w = (ellipsis_range(Integer(1),Ellipsis,Integer(1000)))
>>> v = [D for D in w if D.is_fundamental_discriminant()]
>>> len(v)
302
>>> len([D for D in v if QuadraticField(D,'a').class_number() == Integer(1)])
176
>>> RealNumber('176.0')/Integer(302)
0.582781456953642
w = [1..1000] v = [D for D in w if D.is_fundamental_discriminant()] len(v) len([D for D in v if QuadraticField(D,'a').class_number() == 1]) 176.0/302
For more intuition about what is going on, read about the Cohen-Lenstra heuristics.
Class numbers of cyclotomic fields¶
Sage can also compute class numbers of extensions of higher degree,
within reason. Here we use the shorthand CyclotomicField(n)
to
create the number field
sage: CyclotomicField(7)
Cyclotomic Field of order 7 and degree 6
sage: for n in [2..15]:
....: print("{} {}".format(n, CyclotomicField(n).class_number()))
2 1
3 1
...
15 1
>>> from sage.all import *
>>> CyclotomicField(Integer(7))
Cyclotomic Field of order 7 and degree 6
>>> for n in (ellipsis_range(Integer(2),Ellipsis,Integer(15))):
... print("{} {}".format(n, CyclotomicField(n).class_number()))
2 1
3 1
...
15 1
CyclotomicField(7) for n in [2..15]: print("{} {}".format(n, CyclotomicField(n).class_number()))
In the code above, the notation for n in [2..15]: ...
means
“do … for
Note
Exercise: Compute what is omitted (replaced by …) in the output of the previous example.
Assuming conjectures to speed computations¶
Computations of class numbers and class groups in Sage is done by the
Pari C library, and unlike in Pari, by default Sage tells Pari not
to assume any conjectures. This can make some commands vastly slower
than they might be directly in Pari, which does assume unproved
conjectures by default. Fortunately, it is easy to tell Sage to be
more permissive and allow Pari to assume conjectures, either just for
this one call or henceforth for all number field functions. For
example, with proof=False
it takes only a few seconds to verify,
modulo the conjectures assumed by Pari, that the class number of
sage: CyclotomicField(23).class_number(proof=False)
3
>>> from sage.all import *
>>> CyclotomicField(Integer(23)).class_number(proof=False)
3
CyclotomicField(23).class_number(proof=False)
Note
Exercise: What is the smallest
Class group structure¶
In addition to computing class numbers, Sage can also compute the
group structure and generators for class groups. For example, the
quadratic field
sage: K.<a> = QuadraticField(-30)
sage: C = K.class_group()
sage: C
Class group of order 4 with structure C2 x C2 of Number Field in a with defining polynomial x^2 + 30 with a = 5.477225575051661?*I
sage: category(C)
Category of finite enumerated commutative groups
sage: C.gens()
(Fractional ideal class (5, a), Fractional ideal class (3, a))
>>> from sage.all import *
>>> K = QuadraticField(-Integer(30), names=('a',)); (a,) = K._first_ngens(1)
>>> C = K.class_group()
>>> C
Class group of order 4 with structure C2 x C2 of Number Field in a with defining polynomial x^2 + 30 with a = 5.477225575051661?*I
>>> category(C)
Category of finite enumerated commutative groups
>>> C.gens()
(Fractional ideal class (5, a), Fractional ideal class (3, a))
K.<a> = QuadraticField(-30) C = K.class_group() C category(C) C.gens()
Arithmetic in the class group¶
In Sage, the notation C.i
means “the C.0 \* C.1
, this
means “the product of the 0th and 1st generators of the class group
sage: K.<a> = QuadraticField(-30)
sage: C = K.class_group()
sage: C.0
Fractional ideal class (5, a)
sage: C.0.ideal()
Fractional ideal (5, a)
sage: I = C.0 * C.1
sage: I
Fractional ideal class (2, a)
>>> from sage.all import *
>>> K = QuadraticField(-Integer(30), names=('a',)); (a,) = K._first_ngens(1)
>>> C = K.class_group()
>>> C.gen(0)
Fractional ideal class (5, a)
>>> C.gen(0).ideal()
Fractional ideal (5, a)
>>> I = C.gen(0) * C.gen(1)
>>> I
Fractional ideal class (2, a)
K.<a> = QuadraticField(-30) C = K.class_group() C.0 C.0.ideal() I = C.0 * C.1 I
Next we find that the class of the fractional ideal
sage: A = K.ideal([2, a+4/3])
sage: J = C(A)
sage: J
Fractional ideal class (2/3, 1/3*a)
sage: J == C.0*C.1
True
>>> from sage.all import *
>>> A = K.ideal([Integer(2), a+Integer(4)/Integer(3)])
>>> J = C(A)
>>> J
Fractional ideal class (2/3, 1/3*a)
>>> J == C.gen(0)*C.gen(1)
True
A = K.ideal([2, a+4/3]) J = C(A) J J == C.0*C.1
Unfortunately, there is currently no Sage function that writes a fractional ideal class in terms of the generators for the class group.