From 8fbd6c2d583807d8302baac64a53bba289e97d69 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Wed, 24 May 2023 15:31:53 +0200 Subject: [PATCH 1/3] first attempt to serialize some groups --- src/Serialization/Groups.jl | 141 ++++++++++++++++++++++++++++++++++++ src/Serialization/main.jl | 1 + 2 files changed, 142 insertions(+) create mode 100644 src/Serialization/Groups.jl diff --git a/src/Serialization/Groups.jl b/src/Serialization/Groups.jl new file mode 100644 index 000000000000..a78986a00afe --- /dev/null +++ b/src/Serialization/Groups.jl @@ -0,0 +1,141 @@ +################################################################################ +# PermGroup + +@registerSerializationType(PermGroup) + +function save_internal(s::SerializerState, G::PermGroup) + n = degree(G) + generators = [Vector{Int}(GAP.Globals.ListPerm(x.X)) for x in gens(G)] + + return Dict( + :degree => save_type_dispatch(s, n), + :gens => save_type_dispatch(s, generators), + ) +end + +function load_internal(s::DeserializerState, ::Type{PermGroup}, dict::Dict) + n = load_type_dispatch(s, Int, dict[:degree]) + generators = load_type_dispatch(s, Vector{Vector{Int}}, dict[:gens]) + + return permutation_group(n, [perm(x) for x in generators]) +end + + +################################################################################ +# PermGroupElem + +@registerSerializationType(PermGroupElem) + +function save_internal(s::SerializerState, p::PermGroupElem) + return Dict( + :parent => save_type_dispatch(s, parent(p)), + :imgs => save_type_dispatch(s, Vector{Int}(GAP.Globals.ListPerm(p.X))), + ) +end + +function load_internal(s::DeserializerState, T::Type{PermGroupElem}, dict::Dict) + parent_group = load_unknown_type(s, dict[:parent]) + return load_internal_with_parent(s, T, dict, parent_group) +end + +function load_internal_with_parent(s::DeserializerState, + ::Type{PermGroupElem}, + dict::Dict, + parent_group::PermGroup) + imgs = load_type_dispatch(s, Vector{Int}, dict[:imgs]) + + return perm(parent_group, imgs) +end + + +################################################################################ +# FPGroup + +@registerSerializationType(FPGroup) + +function save_internal(s::SerializerState, G::FPGroup) + elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(G.X)) + free = GAP.getbangproperty(elfam, :freeGroup)::GapObj + elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(free)) + names = GAP.getbangproperty(elfam, :names)::GapObj + if is_full_fp_group(G) + rels = map(syllables, Oscar.relators(G)) + rels = [vcat([[x[1], x[2]] for x in l]...) for l in rels] + return Dict( + :symbols => save_type_dispatch(s, Vector{Symbol}(names)), + :relators => save_type_dispatch(s, rels), + ) + else + # We have no Oscar object corresponding to the full f.p. group. + whole = GAP.getbangproperty(GAPWrap.FamilyObj(G.X), :wholeGroup)::GapObj + rels = Vector{Vector{Int}}(GAP.Globals.List(GAPWrap.RelatorsOfFpGroup(whole), GAPWrap.ExtRepOfObj)) + generators = Vector{Vector{Int}}(GAP.Globals.List(GAPWrap.GeneratorsOfGroup(G.X), GAPWrap.ExtRepOfObj)) + return Dict( + :symbols => save_type_dispatch(s, Vector{Symbol}(names)), + :relators => save_type_dispatch(s, rels), + :generators => save_type_dispatch(s, generators), + ) + end +end + +function load_internal(s::DeserializerState, ::Type{FPGroup}, dict::Dict) + names = load_type_dispatch(s, Vector{Symbol}, dict[:symbols]) + F = free_group(names) + elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F.X)) + rels = load_type_dispatch(s, Vector{Vector{Int}}, dict[:relators]) + rels_pairs = Vector{Pair{Int, Int}}[] + for l in rels + rel = Pair{Int, Int}[] + for i in 1:2:(length(l)-1) + push!(rel, Pair{Int, Int}(l[i], l[i+1])) + end + push!(rels_pairs, rel) + end + G = quo(F, [F(l) for l in rels_pairs])[1] + + if haskey(dict, :generators) + generators = load_type_dispatch(s, Vector{Vector{Int}}, dict[:generators]) + gens_pairs = Vector{Pair{Int, Int}}[] + for l in generators + gen = Pair{Int, Int}[] + for i in 1:2:(length(l)-1) + push!(gen, Pair{Int, Int}(l[i], l[i+1])) + end + push!(gens_pairs, gen) + end + + G = sub(G, [G(l) for l in gens_pairs])[1] + end + return G +end + +################################################################################ +# FPGroupElem + +@registerSerializationType(FPGroupElem) + +function save_internal(s::SerializerState, g::FPGroupElem) + return Dict( + :parent => save_type_dispatch(s, parent(g)), + :extrep => save_type_dispatch(s, vcat([[x[1], x[2]] for x in syllables(g)]...)) + ) +end + +function load_internal(s::DeserializerState, T::Type{FPGroupElem}, dict::Dict) + parent_group = load_unknown_type(s, dict[:parent]) + return load_internal_with_parent(s, T, dict, parent_group) +end + +function load_internal_with_parent(s::DeserializerState, + ::Type{FPGroupElem}, + dict::Dict, + parent_group::FPGroup) + l = load_type_dispatch(s, Vector{Int}, dict[:extrep]) + elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(parent_group.X)) + free = GAP.getbangproperty(elfam, :freeGroup)::GapObj + freefam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(free)) + v = GapObj(l) + w = GAPWrap.ObjByExtRep(freefam, v) + return group_element(parent_group, GAP.Globals.ElementOfFpGroup(elfam, w)) +end + diff --git a/src/Serialization/main.jl b/src/Serialization/main.jl index 58eb057a3558..64fb2b80849e 100644 --- a/src/Serialization/main.jl +++ b/src/Serialization/main.jl @@ -215,6 +215,7 @@ include("Rings.jl") include("polymake.jl") include("TropicalGeometry.jl") include("QuadForm.jl") +include("Groups.jl") ################################################################################ # Include upgrade scripts From cff50905efa61f56d6469433c765b917e2dc41ff Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Wed, 24 May 2023 15:42:49 +0200 Subject: [PATCH 2/3] undo previous commit (erroneously pushed to master) --- src/Serialization/Groups.jl | 141 ------------------------------------ src/Serialization/main.jl | 1 - 2 files changed, 142 deletions(-) delete mode 100644 src/Serialization/Groups.jl diff --git a/src/Serialization/Groups.jl b/src/Serialization/Groups.jl deleted file mode 100644 index a78986a00afe..000000000000 --- a/src/Serialization/Groups.jl +++ /dev/null @@ -1,141 +0,0 @@ -################################################################################ -# PermGroup - -@registerSerializationType(PermGroup) - -function save_internal(s::SerializerState, G::PermGroup) - n = degree(G) - generators = [Vector{Int}(GAP.Globals.ListPerm(x.X)) for x in gens(G)] - - return Dict( - :degree => save_type_dispatch(s, n), - :gens => save_type_dispatch(s, generators), - ) -end - -function load_internal(s::DeserializerState, ::Type{PermGroup}, dict::Dict) - n = load_type_dispatch(s, Int, dict[:degree]) - generators = load_type_dispatch(s, Vector{Vector{Int}}, dict[:gens]) - - return permutation_group(n, [perm(x) for x in generators]) -end - - -################################################################################ -# PermGroupElem - -@registerSerializationType(PermGroupElem) - -function save_internal(s::SerializerState, p::PermGroupElem) - return Dict( - :parent => save_type_dispatch(s, parent(p)), - :imgs => save_type_dispatch(s, Vector{Int}(GAP.Globals.ListPerm(p.X))), - ) -end - -function load_internal(s::DeserializerState, T::Type{PermGroupElem}, dict::Dict) - parent_group = load_unknown_type(s, dict[:parent]) - return load_internal_with_parent(s, T, dict, parent_group) -end - -function load_internal_with_parent(s::DeserializerState, - ::Type{PermGroupElem}, - dict::Dict, - parent_group::PermGroup) - imgs = load_type_dispatch(s, Vector{Int}, dict[:imgs]) - - return perm(parent_group, imgs) -end - - -################################################################################ -# FPGroup - -@registerSerializationType(FPGroup) - -function save_internal(s::SerializerState, G::FPGroup) - elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(G.X)) - free = GAP.getbangproperty(elfam, :freeGroup)::GapObj - elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(free)) - names = GAP.getbangproperty(elfam, :names)::GapObj - if is_full_fp_group(G) - rels = map(syllables, Oscar.relators(G)) - rels = [vcat([[x[1], x[2]] for x in l]...) for l in rels] - return Dict( - :symbols => save_type_dispatch(s, Vector{Symbol}(names)), - :relators => save_type_dispatch(s, rels), - ) - else - # We have no Oscar object corresponding to the full f.p. group. - whole = GAP.getbangproperty(GAPWrap.FamilyObj(G.X), :wholeGroup)::GapObj - rels = Vector{Vector{Int}}(GAP.Globals.List(GAPWrap.RelatorsOfFpGroup(whole), GAPWrap.ExtRepOfObj)) - generators = Vector{Vector{Int}}(GAP.Globals.List(GAPWrap.GeneratorsOfGroup(G.X), GAPWrap.ExtRepOfObj)) - return Dict( - :symbols => save_type_dispatch(s, Vector{Symbol}(names)), - :relators => save_type_dispatch(s, rels), - :generators => save_type_dispatch(s, generators), - ) - end -end - -function load_internal(s::DeserializerState, ::Type{FPGroup}, dict::Dict) - names = load_type_dispatch(s, Vector{Symbol}, dict[:symbols]) - F = free_group(names) - elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F.X)) - rels = load_type_dispatch(s, Vector{Vector{Int}}, dict[:relators]) - rels_pairs = Vector{Pair{Int, Int}}[] - for l in rels - rel = Pair{Int, Int}[] - for i in 1:2:(length(l)-1) - push!(rel, Pair{Int, Int}(l[i], l[i+1])) - end - push!(rels_pairs, rel) - end - G = quo(F, [F(l) for l in rels_pairs])[1] - - if haskey(dict, :generators) - generators = load_type_dispatch(s, Vector{Vector{Int}}, dict[:generators]) - gens_pairs = Vector{Pair{Int, Int}}[] - for l in generators - gen = Pair{Int, Int}[] - for i in 1:2:(length(l)-1) - push!(gen, Pair{Int, Int}(l[i], l[i+1])) - end - push!(gens_pairs, gen) - end - - G = sub(G, [G(l) for l in gens_pairs])[1] - end - return G -end - -################################################################################ -# FPGroupElem - -@registerSerializationType(FPGroupElem) - -function save_internal(s::SerializerState, g::FPGroupElem) - return Dict( - :parent => save_type_dispatch(s, parent(g)), - :extrep => save_type_dispatch(s, vcat([[x[1], x[2]] for x in syllables(g)]...)) - ) -end - -function load_internal(s::DeserializerState, T::Type{FPGroupElem}, dict::Dict) - parent_group = load_unknown_type(s, dict[:parent]) - return load_internal_with_parent(s, T, dict, parent_group) -end - -function load_internal_with_parent(s::DeserializerState, - ::Type{FPGroupElem}, - dict::Dict, - parent_group::FPGroup) - l = load_type_dispatch(s, Vector{Int}, dict[:extrep]) - elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(parent_group.X)) - free = GAP.getbangproperty(elfam, :freeGroup)::GapObj - freefam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(free)) - v = GapObj(l) - w = GAPWrap.ObjByExtRep(freefam, v) - return group_element(parent_group, GAP.Globals.ElementOfFpGroup(elfam, w)) -end - diff --git a/src/Serialization/main.jl b/src/Serialization/main.jl index 64fb2b80849e..58eb057a3558 100644 --- a/src/Serialization/main.jl +++ b/src/Serialization/main.jl @@ -215,7 +215,6 @@ include("Rings.jl") include("polymake.jl") include("TropicalGeometry.jl") include("QuadForm.jl") -include("Groups.jl") ################################################################################ # Include upgrade scripts From 0be6fec740065a1fd11f28b2d93d951a4c529804 Mon Sep 17 00:00:00 2001 From: ThomasBreuer Date: Mon, 3 Jul 2023 23:46:52 +0200 Subject: [PATCH 3/3] extend `iso_oscar_gap(FO::AnticNumberField)` to `iso_oscar_gap(FO::NumField)`, where the field can be simple or non-simple, absolute or non-absolute --- src/GAP/iso_oscar_gap.jl | 70 ++++++++++++++++++++++++++++++++++++--- test/GAP/iso_oscar_gap.jl | 27 +++++++++++++-- 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/src/GAP/iso_oscar_gap.jl b/src/GAP/iso_oscar_gap.jl index 4ebd119db083..720d44cd2d0b 100644 --- a/src/GAP/iso_oscar_gap.jl +++ b/src/GAP/iso_oscar_gap.jl @@ -275,7 +275,8 @@ function _iso_oscar_gap_field_quadratic_functions(FO::AnticNumberField, FG::GAP. return (f, finv) end -function _iso_oscar_gap(FO::AnticNumberField) +# Deal with simple extensions of Q. +function _iso_oscar_gap(FO::SimpleNumField{QQFieldElem}) flag1, N1 = Hecke.is_cyclotomic_type(FO) flag2, N2 = Hecke.is_quadratic_type(FO) if flag1 @@ -285,8 +286,7 @@ function _iso_oscar_gap(FO::AnticNumberField) FG = GAPWrap.Field(GAPWrap.Sqrt(GAP.Obj(N2))) f, finv = _iso_oscar_gap_field_quadratic_functions(FO, FG) else - polFO = FO.pol - N = degree(polFO) + polFO = defining_polynomial(FO) coeffs_polFO = collect(coefficients(polFO)) fam = GAP.Globals.CyclotomicsFamily::GapObj cfs = GAP.GapObj(coeffs_polFO, recursive = true)::GapObj @@ -294,7 +294,7 @@ function _iso_oscar_gap(FO::AnticNumberField) FG = GAPWrap.AlgebraicExtension(GAP.Globals.Rationals::GapObj, polFG) fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(FG)) - f = function(x::Nemo.nf_elem) + f = function(x::SimpleNumFieldElem{QQFieldElem}) coeffs = GAP.GapObj(coefficients(x), recursive = true)::GapObj return GAPWrap.AlgExtElm(fam, coeffs) end @@ -308,6 +308,68 @@ function _iso_oscar_gap(FO::AnticNumberField) return MapFromFunc(f, finv, FO, FG) end +# Deal with simple extensions of proper extensions of Q. +function _iso_oscar_gap(FO::SimpleNumField{nf_elem}) + B = base_field(FO) + isoB = iso_oscar_gap(B) + BG = codomain(isoB)::GapObj + + polFO = defining_polynomial(FO) + coeffs_polFO = collect(coefficients(polFO)) + fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(BG)) + cfs = GAP.GapObj([isoB(x) for x in coeffs_polFO])::GapObj + polFG = GAPWrap.UnivariatePolynomialByCoefficients(fam, cfs, 1) + FG = GAPWrap.AlgebraicExtension(BG, polFG) + fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(FG)) + + f = function(x::SimpleNumFieldElem{nf_elem}) + coeffs = GAP.GapObj([isoB(x) for x in coefficients(x)])::GapObj + return GAPWrap.AlgExtElm(fam, coeffs) + end + + finv = function(x::GapObj) + coeffs = [preimage(isoB, x) for x in GapObj(GAPWrap.ExtRepOfObj(x))] + return FO(coeffs) + end + + return MapFromFunc(f, finv, FO, FG) +end + +# Deal with non-simple extensions of Q or of extensions of Q. +function _iso_oscar_gap(FO::NumField) + @assert ! is_simple(FO) + if is_absolute(FO) + F, emb = absolute_simple_field(FO) + else + F, emb = simple_extension(FO) + end + iso = iso_oscar_gap(F) + FG = codomain(iso) + fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(FG)) + B = base_field(F) + isoB = iso_oscar_gap(B) + + f = function(x::NumFieldElem) + coeffs = GAP.GapObj([isoB(x) for x in coefficients(preimage(emb, x))])::GapObj + return GAPWrap.AlgExtElm(fam, coeffs) + end + + if is_absolute(FO) + finv = function(x::GapObj) + coeffs = Vector{QQFieldElem}(GAPWrap.ExtRepOfObj(x)) + return emb(F(coeffs)) + end + else + finv = function(x::GapObj) + coeffs = [preimage(isoB, y) for y in GAPWrap.ExtRepOfObj(x)] + return emb(F(coeffs)) + end + end + + return MapFromFunc(f, finv, FO, FG) +end + + # Assume that `FO` is a `QQAbField` and `FG` is `GAP.Globals.Cyclotomics`. function _iso_oscar_gap_abelian_closure_functions(FO::QQAbField, FG::GAP.GapObj) return (GAP.julia_to_gap, QQAbElem) diff --git a/test/GAP/iso_oscar_gap.jl b/test/GAP/iso_oscar_gap.jl index 850c006d737d..3e5a9ffa829f 100644 --- a/test/GAP/iso_oscar_gap.jl +++ b/test/GAP/iso_oscar_gap.jl @@ -183,6 +183,7 @@ end b = my_rand_bits(F, 5) @test f(a*b) == f(a)*f(b) @test f(a - b) == f(a) - f(b) + @test preimage(f, f(a)) == a end end @test_throws ErrorException f(cyclotomic_field(2)[2]) @@ -210,6 +211,7 @@ end b = my_rand_bits(F, 5) @test f(a*b) == f(a)*f(b) @test f(a - b) == f(a) - f(b) + @test preimage(f, f(a)) == a end end @@ -221,11 +223,21 @@ end @testset "number fields" begin # for computing random elements of the fields in question my_rand_bits(F::QQField, b::Int) = rand_bits(F, b) - my_rand_bits(F::AnticNumberField, b::Int) = F([rand_bits(QQ, b) for i in 1:degree(F)]) + my_rand_bits(F::NumField, b::Int) = F([my_rand_bits(base_field(F), b) for i in 1:degree(F)]) + # absolute number fields R, x = polynomial_ring(QQ, "x") - @testset for pol in [ x^2 - 5, x^2 + 3, x^3 - 2 ] - F, z = number_field(pol) + pols = [ x^2 - 5, x^2 + 3, x^3 - 2, # simple + [x^2 - 2, x^2 + 1] ] # non-simple + fields = Any[number_field(pol)[1] for pol in pols] + + # non-absolute number fields + F1, _ = number_field(x^2-2) + R1, x1 = polynomial_ring(F1, "x") + push!(fields, number_field(x1^2-3)[1]) # simple + push!(fields, number_field([x1^2-3, x1^2+1])[1]) # non-simple + + @testset for F in fields f = Oscar.iso_oscar_gap(F) @test f === Oscar.iso_oscar_gap(F) # test that everything gets cached for i in 1:10 @@ -234,9 +246,18 @@ end b = my_rand_bits(F, 5) @test f(a*b) == f(a)*f(b) @test f(a - b) == f(a) - f(b) + @test preimage(f, f(a)) == a end end end + + # an application + K = fields[4] + a, b = gens(K) + M1 = 1/a*matrix(K, [1 1; 1 -1]) + M2 = matrix(K, [1 0 ; 0 b]) + G = matrix_group(M1, M2) + @test small_group_identification(G) == (192, 963) end @testset "abelian closure" begin