Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

serialization of more group attributes #4193

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 143 additions & 2 deletions src/Serialization/GAP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,62 @@ function install_GAP_deserialization(filtsymbol::Symbol, meth::Function)
return
end

#############################################################################
#
# attributes handling
#
const GapObj_attributes = [
# boolean
:IsAbelian, :IsAlmostSimpleGroup, :IsCyclic, :IsElementaryAbelian,
:IsFinite, :IsNilpotentGroup, :IsPerfectGroup, :IsQuasisimpleGroup,
:IsSimpleGroup, :IsSolvableGroup, :IsSporadicSimpleGroup,
:IsSupersolvableGroup,
# numeric Int
:DerivedLength, :NilpotencyClassOfGroup, :NrConjugacyClasses, :NrMovedPoints,
# numeric ZZRingElem
:Exponent,
# numeric ZZRingElem or infinity
:Size,
# array of numeric
# :AbelianInvariants, :IdGroup, :TransitiveIdentification,
#TODO: GAP lists of integers
# more involved GapObj
:Center,
# :DerivedSeriesOfGroup,
#TODO: GAP lists of GAP objects
]

function save_attrs(s::SerializerState, G::T) where T <: GapObj
save_data_dict(s, :attrs) do
for attr in attrs_list(s, T)
testerfunc = getproperty(GAP.Globals, Symbol(string("Has", attr)))
if testerfunc(G)
getterfunc = getproperty(GAP.Globals, attr)
attr_value = getterfunc(G)
save_typed_object(s, attr_value, attr)
end
end
end
end

function load_attrs(s::DeserializerState, G::T) where T <: GapObj
!with_attrs(s) && return
haskey(s, :attrs) && load_node(s, :attrs) do d
for attr in attrs_list(s, T)
if haskey(d, attr)
func = getproperty(GAP.Globals, Symbol(string("Set", attr)))
attr_value = load_typed_object(s, attr)
func(G, attr_value)
end
end
end
end

#############################################################################
#
# the Oscar (de)serialization methods that delegate to GAP's method selection
#
@register_serialization_type GapObj uses_id
@register_serialization_type GapObj uses_id [GapObj_attributes;]

function save_object(s::SerializerState, X::GapObj)
GAP.Globals.SerializeInOscar(X, s)
Expand All @@ -43,7 +94,9 @@ function load_object(s::DeserializerState, T::Type{GapObj})
GAP_T = load_node(s, :GapType) do gap_type_data
return GapObj(gap_type_data)
end
return GAP.Globals.DeserializeInOscar(GAPWrap.ValueGlobal(GAP_T), s, T)
X = GAP.Globals.DeserializeInOscar(GAPWrap.ValueGlobal(GAP_T), s, T)
load_attrs(s, X)
return X
end
end

Expand Down Expand Up @@ -73,6 +126,88 @@ install_GAP_serialization(:IsFamily,
error("serialization of GAP family object $X is deliberately not supported")
end)

# - `IsInt`, `IsInfinity`, `IsNegInfinity`:
# used for large group orders (that are `GapObj`s)
install_GAP_serialization(:IsInt,
function(X::GapObj, s::SerializerState)
save_data_dict(s) do
save_object(s, "IsInt", :GapType)
save_object(s, ZZRingElem(X), :int)
end
end)

install_GAP_deserialization(
:IsInt,
function(filt::GapObj, s::DeserializerState, T)
val = load_object(s, ZZRingElem, :int)
return GapObj(val)
end)

install_GAP_serialization(:IsInfinity,
function(X::GapObj, s::SerializerState)
save_data_dict(s) do
save_object(s, "IsInfinity", :GapType)
end
end)

install_GAP_deserialization(
:IsInfinity,
function(filt::GapObj, s::DeserializerState, T)
return GAP.Globals.infinity
end)

install_GAP_serialization(:IsNegInfinity,
function(X::GapObj, s::SerializerState)
save_data_dict(s) do
save_object(s, "IsNegInfinity", :GapType)
end
end)

install_GAP_deserialization(
:IsNegInfinity,
function(filt::GapObj, s::DeserializerState, T)
return GAP.Globals.Ninfinity
end)

# # - `IsList`:
# # used for lists of `GapObj`s
# install_GAP_serialization(:IsList,
# function(X::GapObj, s::SerializerState)
# save_data_dict(s) do
# save_object(s, "IsList", :GapType)
# save_object(s, Vector{GAP.Obj}(X), :list)
# end
# end)
#
# install_GAP_deserialization(
# :IsList,
# function(filt::GapObj, s::DeserializerState, T)
# val = load_object(s, Vector{GAP.Obj}, :list)
# return GapObj(val)
# end)

# - `IsPermGroup`:
# Just store generators as lists of images.
install_GAP_serialization(:IsPermGroup,
function(X::GapObj, s::SerializerState)
save_data_dict(s) do
save_object(s, "IsPermGroup", :GapType)
# generators
save_object(s, [Vector{Int}(GAP.Globals.ListPerm(x))
for x in GAP.Globals.GeneratorsOfGroup(X)], :gens)
save_attrs(s, X)
end
end)

install_GAP_deserialization(
:IsPermGroup,
function(filt::GapObj, s::DeserializerState, T)
vgens = load_object(s, Vector, Vector{Int}, :gens)
length(vgens) == 0 && return GAP.Globals.TrivialGroup(GAP.Globals.IsPermGroup)
ggens = [GAP.Globals.PermList(GapObj(x)) for x in vgens]
return GAP.Globals.Group(GapObj(ggens))
end)

# - `IsFreeGroup`:
# full free group or subgroup of it,
# distinguished by presence of `:freeGroup` and `:gens` in case of a subgroup
Expand Down Expand Up @@ -108,6 +243,7 @@ install_GAP_serialization(:IsFreeGroup,
names = Vector{String}(Xnames)
end
save_object(s, names, :names)
save_attrs(s, X)
end
else
# subgroup of a full free group: save the full group and generators
Expand All @@ -117,6 +253,7 @@ install_GAP_serialization(:IsFreeGroup,
save_typed_object(s, F, :freeGroup)
# store generators
save_object(s, [Vector{Int}(GAPWrap.ExtRepOfObj(x)) for x in GAPWrap.GeneratorsOfGroup(X)], :gens)
save_attrs(s, X)
end
end
end)
Expand Down Expand Up @@ -179,6 +316,7 @@ install_GAP_serialization(:IsSubgroupFpGroup,
# relators
relators = GAP.getbangproperty(elfam, :relators)::GapObj
save_object(s, [Vector{Int}(GAPWrap.ExtRepOfObj(x)) for x in relators], :relators)
save_attrs(s, X)
end
else
# subgroup of a full f.p. group: save the full group and generators
Expand All @@ -188,6 +326,7 @@ install_GAP_serialization(:IsSubgroupFpGroup,
save_typed_object(s, F, :wholeGroup)
# store generators
save_object(s, [Vector{Int}(GAPWrap.ExtRepOfObj(x)) for x in GAPWrap.GeneratorsOfGroup(X)], :gens)
save_attrs(s, X)
end
end
end)
Expand Down Expand Up @@ -275,6 +414,7 @@ install_GAP_serialization(:IsPcGroup,
end
end
save_object(s, rels, :comm_rels)
save_attrs(s, X)
end
else
# save full group and generators
Expand All @@ -284,6 +424,7 @@ install_GAP_serialization(:IsPcGroup,
save_typed_object(s, G, :fullGroup)
save_object(s, [Vector{Int}(GAP.Globals.ExponentsOfPcElement(fullpcgs, x)::GapObj)
for x in GAP.Globals.InducedPcgsWrtHomePcgs(X)::GapObj], :gens)
save_attrs(s, X)
end
end
end)
Expand Down
41 changes: 3 additions & 38 deletions src/Serialization/Groups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -110,57 +110,22 @@ function load_type_params(s::DeserializerState, ::Type{<:GrpElemUnionType})
end


#############################################################################
# attributes handling
const GAPGroup_attributes = [
:order, :is_abelian, :is_nilpotent, :is_perfect, :is_simple, :is_solvable
]

function save_attrs(s::SerializerState, G::T) where T <: GAPGroup
save_data_dict(s, :attrs) do
for attr in attrs_list(s, T)
func = Symbol(string("has_", attr))
if @eval $func($G)
attr_value = @eval $attr($G)
save_typed_object(s, attr_value, attr)
end
end
end
end

function load_attrs(s::DeserializerState, G::T) where T <: GAPGroup
!with_attrs(s) && return
haskey(s, :attrs) && load_node(s, :attrs) do d
for attr in attrs_list(s, T)
if haskey(d, attr)
func = Symbol(string("set_", attr))
attr_value = load_typed_object(s, attr)
@eval $func($G, $attr_value)
end
end
end
end

##############################################################################
# PermGroup

@register_serialization_type PermGroup uses_id [GAPGroup_attributes;]
@register_serialization_type PermGroup uses_id

function save_object(s::SerializerState, G::PermGroup)
n = degree(G)
save_data_dict(s) do
save_object(s, n, :degree)
save_object(s, [Vector{Int}(GAPWrap.ListPerm(GapObj(x))) for x in gens(G)], :gens)
save_attrs(s, G)
save_typed_object(s, GapObj(G), :X)
end
end

function load_object(s::DeserializerState, ::Type{PermGroup})
n = load_object(s, Int, :degree)
generators = load_object(s, Vector, (Vector{Int}, Int), :gens)
G = permutation_group(n, [perm(x) for x in generators])
load_attrs(s, G)
return G
return PermGroup(load_typed_object(s, :X), n)
end


Expand Down
42 changes: 42 additions & 0 deletions test/Serialization/GAP.jl
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,38 @@
@test_throws ErrorException Oscar.save(filenamex, x)
end

@testset "IsPermGroup" begin
G = GAP.Globals.SymmetricGroup(6)
test_save_load_roundtrip(path, G) do loaded
@test G == loaded
end

# load into a new Julia session
filenameG = joinpath(path, "G")
Oscar.save(filenameG, G)
Oscar.reset_global_serializer_state()
loaded = Oscar.load(filenameG)
@test GAP.Globals.GeneratorsOfGroup(loaded) == GAP.Globals.GeneratorsOfGroup(G)
@test GAP.Globals.HasSize(G) # attribute value

G = GAP.Globals.SymmetricGroup(20)
Oscar.save(filenameG, G)
Oscar.reset_global_serializer_state()
loaded = Oscar.load(filenameG)
@test GAP.Globals.GeneratorsOfGroup(loaded) == GAP.Globals.GeneratorsOfGroup(G)
@test GAP.Globals.HasSize(G)
@test !GAP.Globals.IsSmallIntRep(GAP.Globals.Size(G))

G = GAP.Globals.DihedralGroup(GAP.Globals.IsPermGroup, 8)
Z = GAP.Globals.Center(G)
@test GAP.Globals.HasCenter(G)
Oscar.save(filenameG, G)
Oscar.reset_global_serializer_state()
loaded = Oscar.load(filenameG)
@test GAP.Globals.HasCenter(loaded)
@test GAP.Globals.Center(loaded) == Z
end

@testset "IsFreeGroup" begin
wfilts = [:IsSyllableWordsFamily, :IsLetterWordsFamily]
for wfilt in [getproperty(GAP.Globals, x) for x in wfilts]
Expand Down Expand Up @@ -67,6 +99,16 @@
loaded = Oscar.load(filenamev)
@test GAP.Globals.GeneratorsOfGroup(loaded[2])[1] in loaded[1]
@test GAP.Globals.GeneratorsOfGroup(loaded[3])[1] in loaded[1]

# attributes
@test !GAP.Globals.HasSize(F)
GAP.Globals.Size(F)
@test GAP.Globals.HasSize(F)
Oscar.save(filenameF, F)
Oscar.reset_global_serializer_state()
loadedF = Oscar.load(filenameF)
@test GAP.Globals.HasSize(loadedF)
@test GAP.Globals.Size(loadedF) == GAP.Globals.Size(F)
end
end

Expand Down
24 changes: 22 additions & 2 deletions test/Serialization/Groups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@
mktempdir() do path
@testset "Permutation groups" begin
G = symmetric_group(5)
is_finite(G)
is_abelian(G)
is_nilpotent(G)
is_perfect(G)
is_simple(G)
is_solvable(G)

# single element
x = gen(G, 1)
Expand Down Expand Up @@ -43,6 +49,14 @@
Oscar.reset_global_serializer_state()
loadedv = load(filenamev)
@test parent(loadedv[1]) === loadedv[3]
loadedG = loadedv[3]
@test has_order(loadedG) && order(loadedG) == order(G)
@test has_is_finite(G) && is_finite(loadedG) == is_finite(G)
@test has_is_abelian(G) && is_abelian(loadedG) == is_abelian(G)
@test has_is_nilpotent(G) && is_nilpotent(loadedG) == is_nilpotent(G)
@test has_is_perfect(G) && is_perfect(loadedG) == is_perfect(G)
@test has_is_simple(G) && is_simple(loadedG) == is_simple(G)
@test has_is_solvable(G) && is_solvable(loadedG) == is_solvable(G)

loadedw = load(filenamew)
@test parent(loadedw[1]) === parent(loadedw[2])
Expand Down Expand Up @@ -172,6 +186,9 @@
paras = [(1, 1), (5, 1), (24, 12)]
for (n, i) in paras
G = small_group(n, i)
order(G)
# center(G)
#TODO: problem with cyclic references

# single element
x = rand(G)
Expand Down Expand Up @@ -212,9 +229,12 @@
# simulate loading into a fresh Julia session
Oscar.reset_global_serializer_state()
loadedv = load(filenamev)
@test parent(loadedv[1]) === loadedv[3]
loadedG = loadedv[3]
@test parent(loadedv[1]) === loadedG
@test parent(loadedv[2]) === loadedv[4]
@test is_subgroup(loadedv[4], loadedv[3])[1]
@test is_subgroup(loadedv[4], loadedG)[1]
@test has_order(loadedG)
# @test has_center(loadedG) && order(center(loadedG)[1]) == order(center(G)[1])

loadedw = load(filenamew)
@test parent(loadedv[1]) === parent(loadedw[2])
Expand Down
Loading