diff --git a/docs/src/DeveloperDocumentation/serialization.md b/docs/src/DeveloperDocumentation/serialization.md index 087bf56e673b..76610706e99f 100644 --- a/docs/src/DeveloperDocumentation/serialization.md +++ b/docs/src/DeveloperDocumentation/serialization.md @@ -109,20 +109,32 @@ evaluation. There are three pairs of saving and loading functions that are used during serialization: 1. `save_typed_object`, `load_typed_object` -2. `save_object`, `load_object` -3. `save_type_params`, `load_type_params` +2. `save_type_params`, `load_type_params` +3. `save_object`, `load_object` + #### `save_type_object` / `load_type_object` -For the most part these functions should not be touched, they are high level +These functions should not be touched, they are high level functions and are used to (de)serialize the object with its type information as well as its data. The data and type nodes are set in `save_typed_object` resulting in a "data branch" and "type branch". -The usage of these functions can be used inside `save_object` / `load_object` -and `save_type_params` / `load_type_params`. However using `save_typed_object` inside -a `save_object` implementation will lead to a verbose format and should at some -point be moved to `save_type_params`. Their implementation can be found in the -`main.jl` file. + + +#### `save_type_params` / `load_type_params` + +The serialization mechanism stores data in the format of a tree, with the +exception that some nodes may point to a shared reference. The "data branch" +is anything that is a child node of a data node, whereas the "type branch" is +any information that is stored in a node that is a child of a type node. + +These functions should also not be touched, however they expect an implementation +of `type_params` whenever saving a type `T`. By default `type_params` will return +`nothing`. The `type_params` function does a shallow pass through an `obj` of type +`T` gathering the necessary parameters for serializing `obj`. +In most cases these parameters are the parameters of the `obj` that uses references. +For example if `obj` is of type `RingElem` than it is expected that `type_params` +should contain at least #### `save_object` / `load_object` @@ -265,24 +277,6 @@ end Note for now `save_typed_object` must be wrapped in either a `save_data_array` or `save_data_dict`. Otherwise you will get a key override error. -#### `save_type_params` / `load_type_params` - -The serialization mechanism stores data in the format of a tree, with the -exception that some nodes may point to a shared reference. The "data branch" -is anything that is a child node of a data node, whereas the "type branch" is -any information that is stored in a node that is a child of a type node. -Avoiding type information inside the data branch will lead to a more -efficient serialization format. When the `uses_params` is set when -registering the type with [`@register_serialization_type`](@ref) -(de)serialization will use `save_type_params` / `load_type_params` -to format the type information. -In general we expect that implementing a `save_type_params` and -`load_type_params` should not always be necessary. Many types -will serialize their types in a similar fashion for example serialization -of a `FieldElem` will use the `save_type_params` / `load_type_params` from -`RingElem` since in both cases the only parameter needed for such types -is their parent. - ### Import helper When implementing the serialization of a new type in a module that is not diff --git a/gap/OscarInterface/gap/OscarInterface.gd b/gap/OscarInterface/gap/OscarInterface.gd index 22561e85104f..15a1ceb577e6 100644 --- a/gap/OscarInterface/gap/OscarInterface.gd +++ b/gap/OscarInterface/gap/OscarInterface.gd @@ -26,5 +26,7 @@ DeclareProperty( "GroupGeneratorsDefinePresentation", IsGroup ); # Use GAP operations for the serialization of GAP objects. # (The methods will be Julia functions.) +DeclareOperation( "SerializationInOscarDependentObjects", [ IsObject ] ); DeclareOperation( "SerializeInOscar", [ IsObject, IsObject ] ); DeclareConstructor( "DeserializeInOscar", [ IsObject, IsObject, IsObject ] ); +DeclareConstructor( "DeserializeInOscar", [ IsObject, IsObject, IsObject, IsObject ] ); diff --git a/gap/OscarInterface/gap/OscarInterface.gi b/gap/OscarInterface/gap/OscarInterface.gi index 3304614a9437..0aa2968b47de 100644 --- a/gap/OscarInterface/gap/OscarInterface.gi +++ b/gap/OscarInterface/gap/OscarInterface.gi @@ -127,6 +127,13 @@ InstallMethod( GroupGeneratorsDefinePresentation, ############################################################################ +Perform( Oscar._GAP_type_params, + function( entry ) + InstallMethod( SerializationInOscarDependentObjects, + [ JuliaToGAP( IsString, entry[1] ) ], + Julia.GAP.WrapJuliaFunc( entry[2] ) ); + end ); + Perform( Oscar._GAP_serializations, function( entry ) InstallMethod( SerializeInOscar, @@ -136,9 +143,15 @@ Perform( Oscar._GAP_serializations, Perform( Oscar._GAP_deserializations, function( entry ) - InstallMethod( DeserializeInOscar, - [ JuliaToGAP( IsString, entry[1] ), "IsObject", "IsObject" ], - Julia.GAP.WrapJuliaFunc( entry[2] ) ); + if entry[3] then + InstallMethod( DeserializeInOscar, + [ JuliaToGAP( IsString, entry[1] ), "IsObject", "IsObject", "IsObject" ], + Julia.GAP.WrapJuliaFunc( entry[2] ) ); + else + InstallMethod( DeserializeInOscar, + [ JuliaToGAP( IsString, entry[1] ), "IsObject", "IsObject" ], + Julia.GAP.WrapJuliaFunc( entry[2] ) ); + fi; end ); ############################################################################ diff --git a/src/Combinatorics/SimplicialComplexes.jl b/src/Combinatorics/SimplicialComplexes.jl index 602d92846b44..8519c6fde0d8 100644 --- a/src/Combinatorics/SimplicialComplexes.jl +++ b/src/Combinatorics/SimplicialComplexes.jl @@ -637,7 +637,7 @@ function deletion(K::SimplicialComplex, face::Union{<:AbstractSet{Int},<:Abstrac end @doc raw""" - automorphism_group(K::SimplicialComplex; action=:on_vertices) + automorphism_group(K::SimplicialComplex) Given a simplicial complex `K` return its automorphism group as a `PermGroup`. The group can be returned as a subgroup of the permutation group of the vertices @@ -670,7 +670,7 @@ function automorphism_group(K::SimplicialComplex; action=:on_vertices) end @doc raw""" - on_simplicial_complex(K::SimplicialComplex, g::PermGroupElem) + on_simplicial_complex(K::SimplicialComplex, g::PermGroupElem;) Given a simplicial complex `K` return the simplicial complex corresponding to a permutation on it's vertices given by `g`. diff --git a/src/Serialization/Algebras.jl b/src/Serialization/Algebras.jl index a2b34ac2bcb9..6ccd23b49255 100644 --- a/src/Serialization/Algebras.jl +++ b/src/Serialization/Algebras.jl @@ -4,15 +4,15 @@ # Free associative algebra serialization @register_serialization_type FreeAssociativeAlgebra uses_id +type_params(R::T) where T <: FreeAssociativeAlgebra = base_ring(R) + function save_object(s::SerializerState, A::FreeAssociativeAlgebra) save_data_dict(s) do - save_typed_object(s, base_ring(A), :base_ring), save_object(s, symbols(A), :symbols) end end -function load_object(s::DeserializerState, ::Type{<:FreeAssociativeAlgebra}) - R = load_typed_object(s, :base_ring) +function load_object(s::DeserializerState, ::Type{<:FreeAssociativeAlgebra}, R::Ring) gens = load_object(s, Vector{Symbol}, :symbols) return free_associative_algebra(R, gens)[1] end @@ -20,7 +20,7 @@ end # Free associative algebra element serialization @register_serialization_type FreeAssociativeAlgebraElem uses_params -# see save_type_params in Rings +# see type_params in Rings function save_object(s::SerializerState, f::FreeAssociativeAlgebraElem) save_data_array(s) do @@ -33,8 +33,8 @@ function save_object(s::SerializerState, f::FreeAssociativeAlgebraElem) end end -function load_object(s::DeserializerState, ::Type{<:FreeAssociativeAlgebraElem}, parents::Vector) - parent_algebra = parents[end] +function load_object(s::DeserializerState, ::Type{<:FreeAssociativeAlgebraElem}, + parent_algebra::FreeAssociativeAlgebra) coeff_type = elem_type(base_ring(parent_algebra)) elem = MPolyBuildCtx(parent_algebra) diff --git a/src/Serialization/Fields.jl b/src/Serialization/Fields.jl index 043ff7c23184..09fe1b3a465c 100644 --- a/src/Serialization/Fields.jl +++ b/src/Serialization/Fields.jl @@ -1,57 +1,3 @@ -################################################################################ -# Utility functions for parent tree -function get_parents(parent_ring::Field) - # we have reached the end of the parent references and the current ring - # can be found as the base_ring of the previous parent without ambiguity - if !serialize_with_id(parent_ring) - return RingMatSpaceUnion[] - end - - if absolute_degree(parent_ring) == 1 - return RingMatSpaceUnion[] - end - base = parent(defining_polynomial(parent_ring)) - parents = get_parents(base) - push!(parents, parent_ring) - return parents -end - -function get_parents(e::EmbeddedNumField) - base = number_field(e) - parents = get_parents(base) - push!(parents, e) - return parents -end - -function get_parents(parent_ring::T) where T <: Union{AbsNonSimpleNumField, RelNonSimpleNumField} - n = ngens(parent_ring) - base = polynomial_ring(base_field(parent_ring), n; cached=false)[1] - parents = get_parents(base) - push!(parents, parent_ring) - return parents -end - -function get_parents(parent_ring::T) where T <: Union{FracField, - AbstractAlgebra.Generic.RationalFunctionField, - AbstractAlgebra.Generic.LaurentSeriesField} - # we have reached the end of the parent references and the current ring - # can be found as the base_ring of the previous parent without ambiguity - if !serialize_with_id(parent_ring) - return RingMatSpaceUnion[] - end - - base = base_ring(parent_ring) - parents = get_parents(base) - push!(parents, parent_ring) - return parents -end - -################################################################################ -# abstract Field load -function load_object(s:: DeserializerState, ::Type{Field}) - return load_typed_object(s) -end - ################################################################################ # floats @register_serialization_type AbstractAlgebra.Floats{Float64} "Floats" @@ -59,6 +5,15 @@ end ################################################################################ # field of rationals (singleton type) @register_serialization_type QQField +# exclude from ring union definition +type_params(::QQField) = nothing +type_params(::fpField) = nothing +type_params(::FpField) = nothing +type_params(::QQBarField) = nothing +type_params(::PadicField) = nothing + +################################################################################ +# type_params for field extension types ################################################################################ # non-ZZRingElem variant @@ -75,7 +30,7 @@ function load_object(s::DeserializerState, ::Type{fpField}) end # elements -@register_serialization_type fpFieldElem uses_params +@register_serialization_type fpFieldElem function save_object(s::SerializerState, elem::fpFieldElem) save_data_basic(s, string(elem)) @@ -102,7 +57,7 @@ function load_object(s::DeserializerState, ::Type{FpField}) end # elements -@register_serialization_type FpFieldElem uses_params +@register_serialization_type FpFieldElem function save_object(s::SerializerState, elem::FpFieldElem) save_data_basic(s, string(elem)) @@ -119,19 +74,20 @@ end @register_serialization_type Hecke.RelSimpleNumField uses_id @register_serialization_type AbsSimpleNumField uses_id +const SimNumFieldTypeUnion = Union{AbsSimpleNumField, Hecke.RelSimpleNumField} + +type_params(obj::T) where T <: SimpleNumField = type_params(defining_polynomial(obj)) function save_object(s::SerializerState, K::SimpleNumField) save_data_dict(s) do - save_typed_object(s, defining_polynomial(K), :def_pol) + save_object(s, defining_polynomial(K), :def_pol) save_object(s, var(K), :var) end end -function load_object(s::DeserializerState, ::Type{<: SimpleNumField}) - def_pol = load_typed_object(s, :def_pol) - var = load_node(s, :var) do var - Symbol(var) - end +function load_object(s::DeserializerState, ::Type{<: SimpleNumField}, params::PolyRing) + var = load_object(s, Symbol, :var) + def_pol = load_object(s, PolyRingElem, params, :def_pol) K, _ = number_field(def_pol, var, cached=false) return K end @@ -140,25 +96,25 @@ end # FqNmodfinitefield @register_serialization_type fqPolyRepField uses_id +type_params(K::fqPolyRepField) = type_params(defining_polynomial(K)) + function save_object(s::SerializerState, K::fqPolyRepField) - save_data_dict(s) do - save_typed_object(s, defining_polynomial(K), :def_pol) - end + save_object(s, defining_polynomial(K)) end -function load_object(s::DeserializerState, ::Type{<: fqPolyRepField}) - def_pol = load_typed_object(s, :def_pol) +function load_object(s::DeserializerState, ::Type{<: fqPolyRepField}, params::PolyRing) + def_pol = load_object(s, PolyRingElem, params) K, _ = Nemo.Native.finite_field(def_pol, cached=false) return K end #elements -@register_serialization_type fqPolyRepFieldElem uses_params -@register_serialization_type AbsSimpleNumFieldElem uses_params -@register_serialization_type Hecke.RelSimpleNumFieldElem uses_params -const NumFieldElemTypeUnion = Union{AbsSimpleNumFieldElem, fqPolyRepFieldElem, Hecke.RelSimpleNumFieldElem} +@register_serialization_type fqPolyRepFieldElem +@register_serialization_type AbsSimpleNumFieldElem +@register_serialization_type Hecke.RelSimpleNumFieldElem +const SimNumFieldElemTypeUnion = Union{AbsSimpleNumFieldElem, fqPolyRepFieldElem, Hecke.RelSimpleNumFieldElem} -function save_object(s::SerializerState, k::NumFieldElemTypeUnion) +function save_object(s::SerializerState, k::SimNumFieldElemTypeUnion) K = parent(k) polynomial = parent(defining_polynomial(K))(k) save_object(s, polynomial) @@ -170,13 +126,8 @@ function save_object(s::SerializerState, k::Hecke.RelSimpleNumFieldElem{AbsNonSi save_object(s, polynomial) end -function load_object(s::DeserializerState, ::Type{<: NumFieldElemTypeUnion}, - parents::Vector) - polynomial = load_node(s) do _ - load_object(s, PolyRingElem, parents[1:end - 1]) - end - - K = parents[end] +function load_object(s::DeserializerState, ::Type{<: SimNumFieldElemTypeUnion}, K::Union{SimNumFieldTypeUnion, fqPolyRepField}) + polynomial = load_object(s, PolyRingElem, parent(defining_polynomial(K))) loaded_terms = evaluate(polynomial, gen(K)) return K(loaded_terms) end @@ -184,28 +135,29 @@ end ################################################################################ # FqField @register_serialization_type FqField uses_id -@register_serialization_type FqFieldElem uses_params +@register_serialization_type FqFieldElem + +function type_params(K::FqField) + absolute_degree(K) == 1 && return nothing + return type_params(defining_polynomial(K)) +end function save_object(s::SerializerState, K::FqField) - if absolute_degree(K) == 1 - save_object(s, order(K)) - else - save_data_dict(s) do - save_typed_object(s, defining_polynomial(K), :def_pol) + save_data_dict(s) do + if absolute_degree(K) == 1 + save_object(s, order(K), :order) + else + save_object(s, defining_polynomial(K), :def_pol) end end end +function load_object(s::DeserializerState, ::Type{<: FqField}, params::PolyRing) + finite_field(load_object(s, PolyRingElem, params, :def_pol), cached=false)[1] +end + function load_object(s::DeserializerState, ::Type{<: FqField}) - load_node(s) do node - if node isa String - order = ZZRingElem(node) - return finite_field(order)[1] - else - def_pol = load_typed_object(s, :def_pol) - return finite_field(def_pol, cached=false)[1] - end - end + finite_field(load_object(s, ZZRingElem, ZZRing(), :order))[1] end # elements @@ -224,19 +176,12 @@ function save_object(s::SerializerState, k::FqFieldElem) end end -function load_object(s::DeserializerState, ::Type{<: FqFieldElem}, parents::Vector) - K = parents[end] +function load_object(s::DeserializerState, ::Type{<: FqFieldElem}, K::FqField) load_node(s) do _ - K(load_object(s, PolyRingElem, parents[1:end - 1])) - end -end - -function load_object(s::DeserializerState, ::Type{<: FqFieldElem}, parent::FqField) - if absolute_degree(parent) != 1 - return load_object(s, FqFieldElem, get_parents(parent)) - end - load_node(s) do str - return parent(parse(ZZRingElem, str)) + if absolute_degree(K) != 1 + return K(load_object(s, PolyRingElem, parent(defining_polynomial(K)))) + end + K(load_object(s, ZZRingElem, ZZRing())) end end @@ -246,17 +191,19 @@ end @register_serialization_type Hecke.RelNonSimpleNumField uses_id @register_serialization_type AbsNonSimpleNumField uses_id -function save_object(s::SerializerState, K::Union{AbsNonSimpleNumField, RelNonSimpleNumField}) - def_pols = defining_polynomials(K) +type_params(K::T) where T <: Union{AbsNonSimpleNumField, RelNonSimpleNumField} = type_params(defining_polynomials(K)[1]) + +function save_object(s::SerializerState, K::NonSimpleNumField) save_data_dict(s) do - save_typed_object(s, def_pols, :def_pols) + save_object(s, defining_polynomials(K), :def_pols) save_object(s, vars(K), :vars) end end -function load_object(s::DeserializerState, ::Type{<: Union{AbsNonSimpleNumField, RelNonSimpleNumField}}) - def_pols = load_typed_object(s, :def_pols) - +function load_object(s::DeserializerState, + ::Type{<: NonSimpleNumField}, + params::PolyRing) + def_pols = load_object(s, Vector{PolyRingElem}, params, :def_pols) vars = load_node(s, :vars) do vars_data return map(Symbol, vars_data) end @@ -267,24 +214,24 @@ function load_object(s::DeserializerState, ::Type{<: Union{AbsNonSimpleNumField, end #elements -@register_serialization_type Hecke.RelNonSimpleNumFieldElem uses_params -@register_serialization_type AbsNonSimpleNumFieldElem uses_params +@register_serialization_type Hecke.RelNonSimpleNumFieldElem +@register_serialization_type AbsNonSimpleNumFieldElem function save_object(s::SerializerState, k::Union{AbsNonSimpleNumFieldElem, Hecke.RelNonSimpleNumFieldElem}) polynomial = Oscar.Hecke.data(k) save_object(s, polynomial) end -function load_object(s::DeserializerState, ::Type{<: Union{AbsNonSimpleNumFieldElem, Hecke.RelNonSimpleNumFieldElem}}, - parents::Vector) - K = parents[end] +function load_object(s::DeserializerState, + ::Type{<: Union{AbsNonSimpleNumFieldElem, Hecke.RelNonSimpleNumFieldElem}}, + params::Union{AbsNonSimpleNumField, RelNonSimpleNumField}) + K = params n = ngens(K) # forces parent of MPolyRingElem - poly_ring = polynomial_ring(base_field(K), n; cached=false) - parents[end - 1], _ = poly_ring + poly_ring, _ = polynomial_ring(base_field(K), n; cached=false) poly_elem_type = elem_type load_node(s) do _ - polynomial = load_object(s, MPolyRingElem, parents[1:end - 1]) + polynomial = load_object(s, MPolyRingElem, poly_ring) end polynomial = evaluate(polynomial, gens(K)) return K(polynomial) @@ -295,21 +242,20 @@ end @register_serialization_type FracField uses_id -function save_object(s::SerializerState, K::FracField) +const FracUnionTypes = Union{MPolyRingElem, PolyRingElem, UniversalPolyRingElem} +# we use the union to prevent QQField from using these save methods + +function save_object(s::SerializerState, K::FracField{T}) where T <: FracUnionTypes save_data_dict(s) do - save_typed_object(s, base_ring(K), :base_ring) + save_object(s, base_ring(K), :base_ring) end end -function load_object(s::DeserializerState, ::Type{<: FracField}) - R = load_typed_object(s, :base_ring) - - return fraction_field(R, cached=false) -end +load_object(s::DeserializerState, ::Type{<: FracField}, base::Ring) = fraction_field(base, cached=false) # elements -# we use the union to prevent QQFieldElem being registered here -@register_serialization_type FracElem{<:Union{MPolyRingElem, PolyRingElem, UniversalPolyRingElem}} "FracElem" uses_params + +@register_serialization_type FracElem{<: FracUnionTypes} "FracElem" function save_object(s::SerializerState, f::FracElem) save_data_array(s) do @@ -318,17 +264,14 @@ function save_object(s::SerializerState, f::FracElem) end end -function load_object(s::DeserializerState, ::Type{<: FracElem}, parents::Vector) - parent_ring = parents[end] +function load_object(s::DeserializerState, ::Type{<: FracElem}, parent_ring::Ring) load_node(s) do _ - coeff_type = elem_type(base_ring(parent_ring)) - loaded_num = load_node(s, 1) do _ - load_object(s, coeff_type, parents[1:end - 1]) - end - loaded_den = load_node(s, 2) do _ - load_object(s, coeff_type, parents[1:end - 1]) - end - return parent_ring(loaded_num, loaded_den) + base = base_ring(parent_ring) + coeff_type = elem_type(base) + return parent_ring( + load_object(s, coeff_type, base, 1), + load_object(s, coeff_type, base, 2) + ) end end @@ -340,15 +283,13 @@ end function save_object(s::SerializerState, RF::AbstractAlgebra.Generic.RationalFunctionField) save_data_dict(s) do - save_typed_object(s, base_ring(RF), :base_ring) syms = symbols(RF) save_object(s, syms, :symbols) end end function load_object(s::DeserializerState, - ::Type{<: AbstractAlgebra.Generic.RationalFunctionField}) - R = load_typed_object(s, :base_ring) + ::Type{<: AbstractAlgebra.Generic.RationalFunctionField}, R::Ring) # ensure proper types of univariate case on load symbols = load_node(s, :symbols) do symbols_data if symbols_data isa Vector @@ -361,7 +302,7 @@ function load_object(s::DeserializerState, end #elements -@register_serialization_type AbstractAlgebra.Generic.RationalFunctionFieldElem "RationalFunctionFieldElem" uses_params +@register_serialization_type AbstractAlgebra.Generic.RationalFunctionFieldElem "RationalFunctionFieldElem" function save_object(s::SerializerState, f::AbstractAlgebra.Generic.RationalFunctionFieldElem) save_data_array(s) do @@ -372,19 +313,17 @@ end function load_object(s::DeserializerState, ::Type{<: AbstractAlgebra.Generic.RationalFunctionFieldElem}, - parents::Vector) - parent_ring = parents[end] + parent_ring::AbstractAlgebra.Generic.RationalFunctionField) base = base_ring(AbstractAlgebra.Generic.fraction_field(parent_ring)) - pushfirst!(parents, base) coeff_type = elem_type(base) return load_node(s) do _ loaded_num = load_node(s, 1) do _ - load_object(s, coeff_type, parents[1:end - 1]) + load_object(s, coeff_type, base) end loaded_den = load_node(s, 2) do _ - load_object(s, coeff_type, parents[1:end - 1]) + load_object(s, coeff_type, base) end parent_ring(loaded_num, loaded_den) end @@ -393,7 +332,7 @@ end ################################################################################ # ArbField @register_serialization_type ArbField -@register_serialization_type ArbFieldElem uses_params +@register_serialization_type ArbFieldElem function save_object(s::SerializerState, RR::ArbField) save_object(s, precision(RR)) @@ -426,7 +365,7 @@ end ################################################################################ # AcbField @register_serialization_type AcbField -@register_serialization_type AcbFieldElem uses_params +@register_serialization_type AcbFieldElem function save_object(s::SerializerState, CC::AcbField) save_object(s, precision(CC)) @@ -455,84 +394,96 @@ end ################################################################################ # Field Embeddings -const FieldEmbeddingTypes = Union{Hecke.AbsSimpleNumFieldEmbedding, Hecke.RelSimpleNumFieldEmbedding, Hecke.AbsNonSimpleNumFieldEmbedding, Hecke.RelNonSimpleNumFieldEmbedding} +const FieldEmbeddingTypes = Union{ + Hecke.AbsSimpleNumFieldEmbedding, + Hecke.RelSimpleNumFieldEmbedding, + Hecke.AbsNonSimpleNumFieldEmbedding, + Hecke.RelNonSimpleNumFieldEmbedding +} -@register_serialization_type Hecke.AbsNonSimpleNumFieldEmbedding uses_id +@register_serialization_type Hecke.AbsNonSimpleNumFieldEmbedding uses_id @register_serialization_type Hecke.RelNonSimpleNumFieldEmbedding uses_id @register_serialization_type Hecke.AbsSimpleNumFieldEmbedding uses_id @register_serialization_type Hecke.RelSimpleNumFieldEmbedding uses_id -function save_object(s::SerializerState, E::FieldEmbeddingTypes) +function type_params(E::T) where T <: FieldEmbeddingTypes K = number_field(E) - k = base_field(K) + base_K = base_field(K) + d = Dict{Symbol, Any}(:num_field => K) + + if !(base_field(K) isa QQField) + base_field_emb = restrict(E, base_K) + d[:base_field_emb] = base_field_emb + end + return d +end +function save_object(s::SerializerState, E::FieldEmbeddingTypes) + K = number_field(E) + base_K = base_field(K) save_data_dict(s) do - save_typed_object(s, K, :num_field) - if !(base_field(K) isa QQField) - save_typed_object(s, restrict(E, k), :base_field_emb) - end if is_simple(K) a = gen(K) - data = E(a) - if any(overlaps(data, e(a)) for e in complex_embeddings(K) if e != E && restrict(E, k) == restrict(e, k)) + gen_emb_approx = E(a) + if any(overlaps(gen_emb_approx, e(a)) for e in complex_embeddings(K) if e != E && restrict(E, base_K) == restrict(e, base_K)) error("Internal error in internal serialization.") end + save_object(s, gen_emb_approx, :gen_approx) else a = gens(K) data = E.(a) - if any(all(overlaps(t[1], t[2]) for t in zip(data, e.(a))) for e in complex_embeddings(K) if e != E && restrict(E, k) == restrict(e, k)) + if any(all(overlaps(t[1], t[2]) for t in zip(data, e.(a))) for e in complex_embeddings(K) if e != E && restrict(E, base_field(K)) == restrict(e, base_field(K))) error("Internal error in internal serialization.") end - data = tuple(data...) + save_object(s, tuple(data...), :gen_approx) end - save_typed_object(s, data, :data) end end -function load_object(s::DeserializerState, ::Type{<:FieldEmbeddingTypes}) - K = load_typed_object(s, :num_field) - data = load_typed_object(s, :data) - if data isa Tuple - data = collect(data) - end - if base_field(K) isa QQField - return complex_embedding(K, data) - else - return complex_embedding(K, load_typed_object(s, :base_field_emb), data) +function load_object(s::DeserializerState, T::Type{<:FieldEmbeddingTypes}, params::Dict) + K = params[:num_field] + load_node(s, :gen_approx) do _ + if !is_simple(K) + data = load_object(s, Vector{AcbFieldElem}, AcbField()) + else + data = load_object(s, AcbFieldElem, AcbField()) + end + if base_field(K) isa QQField + return complex_embedding(K, data) + else + return complex_embedding(K, params[:base_field_emb], data) + end end end @register_serialization_type EmbeddedNumField uses_id -function save_object(s::SerializerState, E::EmbeddedNumField) - K = number_field(E) - e = embedding(E) +type_params(E::EmbeddedNumField) = embedding(E) - save_data_dict(s) do - save_typed_object(s, K, :num_field) - save_typed_object(s, e, :embedding) +function save_object(s::SerializerState, E::EmbeddedNumField) + save_data_array(s) do + # no data required but we leave this function here to generate a valid json + # probably be a neater to way to do this end end -function load_object(s::DeserializerState, ::Type{EmbeddedNumField}) - K = load_typed_object(s, :num_field) - e = load_typed_object(s, :embedding) - - return Hecke.embedded_field(K, e)[1] +function load_object(s::DeserializerState, ::Type{<:EmbeddedNumField}, + E::T) where T <: FieldEmbeddingTypes + K = number_field(E) + return Hecke.embedded_field(K, E)[1] end -@register_serialization_type EmbeddedNumFieldElem uses_params +@register_serialization_type EmbeddedNumFieldElem function save_object(s::SerializerState, f::EmbeddedNumFieldElem) save_object(s, data(f)) end -function load_object(s::DeserializerState, ::Type{<:EmbeddedNumFieldElem}, parents::Vector) - parent_field = parents[end] - numfield_elem = terms - coeff_type = elem_type(parents[end - 1]) - loaded_alg_elem = load_object(s, coeff_type, parents[1:end - 1]) - return parent_field(loaded_alg_elem) +function load_object(s::DeserializerState, ::Type{<:EmbeddedNumFieldElem}, E::T) where T <: EmbeddedNumField + K = number_field(E) + coeff_type = elem_type(K) + loaded_alg_elem = load_object(s, coeff_type, K) + return E(loaded_alg_elem) end ################################################################################ @@ -566,7 +517,7 @@ function save_object(s::SerializerState, q::QQBarFieldElem) end end -function load_object(s::DeserializerState, ::Type{QQBarFieldElem}) +function load_object(s::DeserializerState, ::Type{QQBarFieldElem}, ::QQBarField) Qx, x = polynomial_ring(QQ, :x; cached=false) min_poly = load_object(s, PolyRingElem{QQ}, Qx, :minpoly) precision = load_object(s, Int, :precision) @@ -606,7 +557,7 @@ function load_object(s::DeserializerState, ::Type{PadicField}) end #elements -@register_serialization_type PadicFieldElem uses_params +@register_serialization_type PadicFieldElem function save_object(s::SerializerState, obj::PadicFieldElem) # currently it seems PadicFieldElems do not store the underlying polynomial @@ -614,6 +565,6 @@ function save_object(s::SerializerState, obj::PadicFieldElem) end function load_object(s::DeserializerState, ::Type{PadicFieldElem}, parent_field::PadicField) - rational_rep = load_object(s, QQFieldElem) + rational_rep = load_object(s, QQFieldElem, QQField()) return parent_field(rational_rep) end diff --git a/src/Serialization/GAP.jl b/src/Serialization/GAP.jl index 631ede44744c..7a50477746de 100644 --- a/src/Serialization/GAP.jl +++ b/src/Serialization/GAP.jl @@ -14,16 +14,22 @@ # # utilities for installing methods depending on GAP filters # +const _GAP_type_params = Pair{Symbol, Function}[] const _GAP_serializations = Pair{Symbol, Function}[] -const _GAP_deserializations = Pair{Symbol, Function}[] +const _GAP_deserializations = Tuple{Symbol, Function, Bool}[] + +function install_GAP_type_params(filtsymbol::Symbol, meth::Function) + push!(_GAP_type_params, filtsymbol => meth) + return +end function install_GAP_serialization(filtsymbol::Symbol, meth::Function) push!(_GAP_serializations, filtsymbol => meth) return end -function install_GAP_deserialization(filtsymbol::Symbol, meth::Function) - push!(_GAP_deserializations, filtsymbol => meth) +function install_GAP_deserialization(filtsymbol::Symbol, with_params::Bool, meth::Function) + push!(_GAP_deserializations, (filtsymbol, meth, with_params)) return end @@ -33,6 +39,10 @@ end # @register_serialization_type GapObj uses_id +function type_params(X::GapObj) + return GAP.Globals.SerializationInOscarDependentObjects(X)::Union{Nothing,GapObj} +end + function save_object(s::SerializerState, X::GapObj) GAP.Globals.SerializeInOscar(X, s) end @@ -47,6 +57,16 @@ function load_object(s::DeserializerState, T::Type{GapObj}) end end +function load_object(s::DeserializerState, T::Type{GapObj}, F::GapObj) + load_node(s) do d + @req haskey(s, :GapType) "cannot deserialize GapObj without key :GapType" + 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, F) + end +end + ############################################################################# # # the individual methods @@ -75,7 +95,18 @@ install_GAP_serialization(:IsFamily, # - `IsFreeGroup`: # full free group or subgroup of it, -# distinguished by presence of `:freeGroup` and `:gens` in case of a subgroup +# distinguished by type parameter `:freeGroup` and presence of `:gens` +# in case of a subgroup +install_GAP_type_params(:IsFreeGroup, + function(X::GapObj) + if GAP.Globals.HasIsWholeFamily(X) && GAPWrap.IsWholeFamily(X) + return nothing + else + elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(X)) + return GAP.getbangproperty(elfam, :freeGroup)::GapObj + end + end) + install_GAP_serialization(:IsFreeGroup, function(X::GapObj, s::SerializerState) elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(X)) @@ -112,59 +143,75 @@ install_GAP_serialization(:IsFreeGroup, else # subgroup of a full free group: save the full group and generators save_data_dict(s) do - F = GAP.getbangproperty(elfam, :freeGroup)::GapObj save_object(s, "IsFreeGroup", :GapType) - save_typed_object(s, F, :freeGroup) # store generators save_object(s, [Vector{Int}(GAPWrap.ExtRepOfObj(x)) for x in GAPWrap.GeneratorsOfGroup(X)], :gens) end end end) +# no type parameters: create a full free group install_GAP_deserialization( - :IsFreeGroup, + :IsFreeGroup, false, function(filt::GapObj, s::DeserializerState, T) load_node(s) do d - if haskey(s, :freeGroup) && haskey(s, :gens) - # Deserialize the full free group. - F = load_typed_object(s, :freeGroup) - # Deserialize the generators. - generators = load_object(s, Vector, (Vector{Int}, Int), :gens) - fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F)) - Ggens = [GAPWrap.ObjByExtRep(fam, GapObj(x, true)) for x in generators] - # Create the subgroup. - G = GAP.Globals.SubgroupNC(F, GapObj(Ggens))::GapObj - else - # Create a new full free group. - wfilt = getproperty(GAP.Globals, load_object(s, Symbol, :wfilt))::GapObj - if haskey(s, :nameprefix) - # infinite rank - prefix = load_node(s, :nameprefix) do nameprefix - GapObj(nameprefix) - end - init = load_node(s, :names) do names - if length(names) == 0 - GapObj([]) - else - # problem with `Union{}[]` - GapObj(names; recursive = true) - end - end - G = GAP.Globals.FreeGroup(wfilt, GAP.Globals.infinity, prefix, init)::GapObj - else - init = load_node(s, :names) do names + # Create a new full free group. + wfilt = getproperty(GAP.Globals, load_object(s, Symbol, :wfilt))::GapObj + if haskey(s, :nameprefix) + # infinite rank + prefix = load_node(s, :nameprefix) do nameprefix + GapObj(nameprefix) + end + init = load_node(s, :names) do names + if length(names) == 0 + GapObj([]) + else + # problem with `Union{}[]` GapObj(names; recursive = true) end - G = GAP.Globals.FreeGroup(wfilt, init)::GapObj end + G = GAP.Globals.FreeGroup(wfilt, GAP.Globals.infinity, prefix, init)::GapObj + else + init = load_node(s, :names) do names + GapObj(names; recursive = true) + end + G = GAP.Globals.FreeGroup(wfilt, init)::GapObj end return G end end) +# with type parameters: create a subgroup of a full free group +install_GAP_deserialization( + :IsFreeGroup, true, + function(filt::GapObj, s::DeserializerState, T, F) + load_node(s) do d + # Deserialize the generators. + generators = load_object(s, Vector{Vector{Int}}, :gens) + fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F)) + Ggens = [GAPWrap.ObjByExtRep(fam, GapObj(x, true)) for x in generators] + # Create the subgroup. + return GAP.Globals.SubgroupNC(F, GapObj(Ggens))::GapObj + end + end) + # - `IsSubgroupFpGroup`: # full f.p. group or subgroup of it, -# distinguished by presence of `:wholeGroup` and `:gens` in case of a subgroup +# distinguished by type parameter `:freeGroup` and presence of `:relators` +# in case of a full group, or +# type parameter `:wholeGroup` and presence of `:gens` in case of a subgroup +install_GAP_type_params(:IsSubgroupFpGroup, + function(X::GapObj) + Xfam = GAPWrap.FamilyObj(X) + if GAP.Globals.HasIsWholeFamily(X) && GAPWrap.IsWholeFamily(X) + elfam = GAPWrap.ElementsFamily(Xfam) + F = GAP.getbangproperty(elfam, :freeGroup)::GapObj + else + F = GAP.getbangproperty(Xfam, :wholeGroup)::GapObj + end + return F + end) + install_GAP_serialization(:IsSubgroupFpGroup, function(X::GapObj, s::SerializerState) Xfam = GAPWrap.FamilyObj(X) @@ -173,9 +220,6 @@ install_GAP_serialization(:IsSubgroupFpGroup, # full f.p. group: Save the defining data. save_data_dict(s) do save_object(s, "IsSubgroupFpGroup", :GapType) - # underlying free group - freegroup = GAP.getbangproperty(elfam, :freeGroup)::GapObj - save_typed_object(s, freegroup, :freeGroup) # relators relators = GAP.getbangproperty(elfam, :relators)::GapObj save_object(s, [Vector{Int}(GAPWrap.ExtRepOfObj(x)) for x in relators], :relators) @@ -183,40 +227,39 @@ install_GAP_serialization(:IsSubgroupFpGroup, else # subgroup of a full f.p. group: save the full group and generators save_data_dict(s) do - F = GAP.getbangproperty(Xfam, :wholeGroup)::GapObj save_object(s, "IsSubgroupFpGroup", :GapType) - save_typed_object(s, F, :wholeGroup) # store generators save_object(s, [Vector{Int}(GAPWrap.ExtRepOfObj(x)) for x in GAPWrap.GeneratorsOfGroup(X)], :gens) end end end) +# we have always a type parameter, +# in case of a full f.p. group the underlying free group, +# in case of a subgroup the full f.p. group install_GAP_deserialization( - :IsSubgroupFpGroup, - function(filt::GapObj, s::DeserializerState, T) + :IsSubgroupFpGroup, true, + function(filt::GapObj, s::DeserializerState, T, F) load_node(s) do d - if haskey(s, :wholeGroup) && haskey(s, :gens) + if haskey(s, :gens) # Deserialize the full f.p. group. - F = load_typed_object(s, :wholeGroup) Ffam = GAPWrap.FamilyObj(F) elfam = GAPWrap.ElementsFamily(Ffam) freegroup = GAP.getbangproperty(elfam, :freeGroup)::GapObj freefam = GAPWrap.FamilyObj(freegroup) elfreefam = GAPWrap.ElementsFamily(freefam) # Deserialize the generators. - generators = load_object(s, Vector, (Vector{Int}, Int), :gens) + generators = load_object(s, Vector{Vector{Int}}, :gens) gens = [GAPWrap.ObjByExtRep(elfreefam, GapObj(x, true)) for x in generators] Ggens = [GAPWrap.ElementOfFpGroup(elfam, x) for x in gens] # Create the subgroup. G = GAP.Globals.SubgroupNC(F, GapObj(Ggens))::GapObj else # Create a new full f.p. group. - F = load_typed_object(s, :freeGroup) - relators = load_object(s, Vector, (Vector{Int}, Int), :relators) + relators = load_object(s, Vector{Vector{Int}}, :relators) elfreefam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F)) rels = [GAPWrap.ObjByExtRep(elfreefam, GapObj(x, true)) for x in relators] - G = F/GapObj(rels) + G = F/GapObj(rels)::GapObj end return G end @@ -228,6 +271,17 @@ install_GAP_deserialization( # we do not support (de)serialization of the stored rws, # thus we need not (de)serialize its underlying free group etc. +install_GAP_type_params(:IsPcGroup, + function(X::GapObj) + elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(X)) + fullpcgs = GAP.getbangproperty(elfam, :DefiningPcgs)::GapObj + if fullpcgs === GAPWrap.Pcgs(X) + return nothing + else + return GAP.getbangproperty(fullpcgs, :GroupOfPcgs)::GapObj + end + end) + # utility, turn an exponent vector `[a_1, a_2, ..., a_n]` # into `[1, a_1, 2, a_2, ..., n, a_n]` function _free_group_extrep_from_exponents(exps::Vector{Int}) @@ -277,45 +331,44 @@ install_GAP_serialization(:IsPcGroup, save_object(s, rels, :comm_rels) end else - # save full group and generators + # save generators w.r.t. the full group save_data_dict(s) do save_object(s, "IsPcGroup", :GapType) - G = GAP.getbangproperty(fullpcgs, :GroupOfPcgs)::GapObj - 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) end end end) +# no type parameters: create a full pc group install_GAP_deserialization( - :IsPcGroup, + :IsPcGroup, false, function(filt::GapObj, s::DeserializerState, T) - if haskey(s, :relord) - # full pc group - relord = load_object(s, Vector, Int, :relord) - F = GAP.Globals.FreeGroup(GAP.Globals.IsSyllableWordsFamily, - length(relord))::GapObj - fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F)) - rws = GAP.Globals.SingleCollector(F, GapObj(relord))::GapObj - for (i, elm) in load_object(s, Vector, (Tuple, [Int, [Vector, Int]]), :power_rels) - GAP.Globals.SetPower(rws, i, GAPWrap.ObjByExtRep(fam, GapObj(elm))) - end - for (j, i, elm) in load_object(s, Vector, (Tuple, [Int, Int, [Vector, Int]]), :comm_rels) - GAP.Globals.SetCommutator(rws, j, i, GAPWrap.ObjByExtRep(fam, GapObj(elm))) - end - G = GAP.Globals.GroupByRwsNC(rws)::GapObj - else - # Deserialize the full pc group. - F = load_typed_object(s, :fullGroup) - elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F)) - fullpcgs = GAP.getbangproperty(elfam, :DefiningPcgs)::GapObj - # Deserialize the generators. - generators = load_object(s, Vector, (Vector{Int}, Int), :gens) - Ggens = [GAP.Globals.PcElementByExponentsNC(fullpcgs, GapObj(x, true))::GapObj - for x in generators] - # Create the subgroup. - G = GAP.Globals.SubgroupNC(F, GapObj(Ggens))::GapObj + relord = load_object(s, Vector{Int}, :relord) + F = GAP.Globals.FreeGroup(GAP.Globals.IsSyllableWordsFamily, + length(relord))::GapObj + fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F)) + rws = GAP.Globals.SingleCollector(F, GapObj(relord))::GapObj + for (i, elm) in load_object(s, Vector{Tuple{Int, Vector{Int}}}, :power_rels) + GAP.Globals.SetPower(rws, i, GAPWrap.ObjByExtRep(fam, GapObj(elm))) end - return G + for (j, i, elm) in load_object(s, Vector{Tuple{Int, Int, Vector{Int}}}, :comm_rels) + GAP.Globals.SetCommutator(rws, j, i, GAPWrap.ObjByExtRep(fam, GapObj(elm))) + end + return GAP.Globals.GroupByRwsNC(rws)::GapObj + end) + +# with type parameters: create a subgroup of a full pc group +install_GAP_deserialization( + :IsPcGroup, true, + function(filt::GapObj, s::DeserializerState, T, F) + # Deserialize the full pc group. + elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(F)) + fullpcgs = GAP.getbangproperty(elfam, :DefiningPcgs)::GapObj + # Deserialize the generators. + generators = load_object(s, Vector{Vector{Int}}, :gens) + Ggens = [GAP.Globals.PcElementByExponentsNC(fullpcgs, GapObj(x, true))::GapObj + for x in generators] + # Create the subgroup. + return GAP.Globals.SubgroupNC(F, GapObj(Ggens))::GapObj end) diff --git a/src/Serialization/Groups.jl b/src/Serialization/Groups.jl index 6f14d99f81dd..63d59effbd5d 100644 --- a/src/Serialization/Groups.jl +++ b/src/Serialization/Groups.jl @@ -90,24 +90,7 @@ # `GAPGroupElem` objects get serialized together with their parents. const GrpElemUnionType = Union{GAPGroupElem, FinGenAbGroupElem} -function save_type_params(s::SerializerState, p::T) where T <: GrpElemUnionType - # this has just been more or less copied from the Rings section - # and might be removed from this file during a future refactor - save_data_dict(s) do - save_object(s, encode_type(T), :name) - parent_p = parent(p) - if serialize_with_id(parent_p) - parent_ref = save_as_ref(s, parent_p) - save_object(s, parent_ref, :params) - else - save_typed_object(s, parent_p, :params) - end - end -end - -function load_type_params(s::DeserializerState, ::Type{<:GrpElemUnionType}) - return load_typed_object(s) -end +type_params(p::T) where T <: GrpElemUnionType = parent(p) ############################################################################# @@ -118,7 +101,7 @@ const GAPGroup_attributes = [ function save_attrs(s::SerializerState, G::T) where T <: GAPGroup save_data_dict(s, :attrs) do - for attr in attrs_list(s, T) + for attr in attrs_list(T) func = Symbol(string("has_", attr)) if @eval $func($G) attr_value = @eval $attr($G) @@ -131,7 +114,7 @@ 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) + for attr in attrs_list(T) if haskey(d, attr) func = Symbol(string("set_", attr)) attr_value = load_typed_object(s, attr) @@ -157,7 +140,7 @@ end function load_object(s::DeserializerState, ::Type{PermGroup}) n = load_object(s, Int, :degree) - generators = load_object(s, Vector, (Vector{Int}, Int), :gens) + generators = load_object(s, Vector{Vector{Int}}, :gens) G = permutation_group(n, [perm(x) for x in generators]) load_attrs(s, G) return G @@ -170,14 +153,16 @@ end # (Or would it be easier for other systems if the length of the array # is equal to the degree of the parent group?) -@register_serialization_type PermGroupElem uses_params +@register_serialization_type PermGroupElem + +type_params(x::T) where T <: GroupElem = parent(x) function save_object(s::SerializerState, p::PermGroupElem) save_object(s, Vector{Int}(GAPWrap.ListPerm(GapObj(p)))) end function load_object(s::DeserializerState, T::Type{PermGroupElem}, parent_group::PermGroup) - imgs = load_object(s, Vector, Int) + imgs = load_object(s, Vector{Int}) return perm(parent_group, imgs) end @@ -205,8 +190,8 @@ end # FPGroupElem, SubFPGroupElem # We need the parent and a description of the word that defines the element. -@register_serialization_type FPGroupElem uses_params -@register_serialization_type SubFPGroupElem uses_params +@register_serialization_type FPGroupElem +@register_serialization_type SubFPGroupElem function save_object(s::SerializerState, g::Union{FPGroupElem, SubFPGroupElem}) save_object(s, Vector{Int}(vcat([[x[1], x[2]] for x in syllables(g)]...))) @@ -219,7 +204,7 @@ typecombinations = ( for (eltype, type) in typecombinations @eval function load_object(s::DeserializerState, ::Type{$eltype}, parent_group::$type) - lo = load_object(s, Vector, Int) + lo = load_object(s, Vector{Int}) fam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(GapObj(parent_group))) if GAPWrap.IsElementOfFpGroupFamily(fam) # go via the underlying free group @@ -258,8 +243,8 @@ end # We need the parent and a description of the exponent vector # that defines the element. -@register_serialization_type PcGroupElem uses_params -@register_serialization_type SubPcGroupElem uses_params +@register_serialization_type PcGroupElem +@register_serialization_type SubPcGroupElem function save_object(s::SerializerState, g::Union{PcGroupElem, SubPcGroupElem}) elfam = GAPWrap.FamilyObj(GapObj(g)) @@ -274,7 +259,7 @@ typecombinations = ( for (eltype, type) in typecombinations @eval function load_object(s::DeserializerState, ::Type{$eltype}, parent_group::$type) - lo = load_object(s, Vector, Int) + lo = load_object(s, Vector{Int}) elfam = GAPWrap.ElementsFamily(GAPWrap.FamilyObj(GapObj(parent_group))) fullpcgs = GAP.getbangproperty(elfam, :DefiningPcgs)::GapObj gapelm = GAPWrap.PcElementByExponentsNC(fullpcgs, GapObj(lo, true))::GapObj @@ -293,16 +278,16 @@ function save_object(s::SerializerState, G::FinGenAbGroup) end function load_object(s::DeserializerState, ::Type{FinGenAbGroup}) - return abelian_group(load_object(s, Matrix, ZZRingElem)) + return abelian_group(load_object(s, Matrix{ZZRingElem})) end # elems -@register_serialization_type FinGenAbGroupElem uses_params +@register_serialization_type FinGenAbGroupElem function save_object(s::SerializerState, g::FinGenAbGroupElem) save_object(s, _coeff(g)) end function load_object(s::DeserializerState, ::Type{FinGenAbGroupElem}, G::FinGenAbGroup) - return G(vec(load_object(s, Matrix, ZZRingElem))) + return G(vec(load_object(s, Matrix{ZZRingElem}))) end diff --git a/src/Serialization/LieTheory.jl b/src/Serialization/LieTheory.jl index 2d06aa6769fa..ae8b4edd58da 100644 --- a/src/Serialization/LieTheory.jl +++ b/src/Serialization/LieTheory.jl @@ -20,12 +20,12 @@ function save_object(s::SerializerState, R::RootSystem) end function load_object(s::DeserializerState, ::Type{RootSystem}) - cm = load_object(s, Matrix, Int, :cartan_matrix) + cm = load_object(s, Matrix{Int}, :cartan_matrix) R = root_system(cm; check=false, detect_type=false) if haskey(s, :type) - type = Vector{Tuple{Symbol,Int}}(load_object(s, Vector, (Tuple, [Symbol, Int]), :type)) # coercion needed due to https://github.com/oscar-system/Oscar.jl/issues/3983 + type = load_object(s, Vector{Tuple{Symbol, Int}}, :type) if haskey(s, :type_ordering) - type_ordering = load_object(s, Vector, Int, :type_ordering) + type_ordering = load_object(s, Vector{Int}, :type_ordering) set_root_system_type!(R, type, type_ordering) else set_root_system_type!(R, type) @@ -34,8 +34,10 @@ function load_object(s::DeserializerState, ::Type{RootSystem}) return R end -@register_serialization_type RootSpaceElem uses_params -@register_serialization_type DualRootSpaceElem uses_params +@register_serialization_type RootSpaceElem +@register_serialization_type DualRootSpaceElem + +type_params(e::T) where T <: Union{RootSpaceElem, DualRootSpaceElem} = root_system(e) function save_object(s::SerializerState, r::Union{RootSpaceElem,DualRootSpaceElem}) save_object(s, _vec(coefficients(r))) @@ -44,23 +46,9 @@ end function load_object( s::DeserializerState, T::Type{<:Union{RootSpaceElem,DualRootSpaceElem}}, R::RootSystem ) - return T(R, load_object(s, Vector, QQ)) -end - -function save_type_params(s::SerializerState, r::Union{RootSpaceElem,DualRootSpaceElem}) - save_data_dict(s) do - save_object(s, encode_type(typeof(r)), :name) - rs_x = root_system(r) - rs_ref = save_as_ref(s, rs_x) - save_object(s, rs_ref, :params) - end + return T(R, load_object(s, Vector{QQFieldElem})) end -function load_type_params( - s::DeserializerState, ::Type{<:Union{RootSpaceElem,DualRootSpaceElem}} -) - return load_typed_object(s) -end ############################################################################### # @@ -70,38 +58,29 @@ end @register_serialization_type WeightLattice uses_id +type_params(P::WeightLattice) = root_system(P) + function save_object(s::SerializerState, P::WeightLattice) save_data_dict(s) do - save_typed_object(s, root_system(P), :root_system) + # no data required but we leave this function here to generate a valid json + # and leave root for possible future attrs end end -function load_object(s::DeserializerState, ::Type{WeightLattice}) - R = load_typed_object(s, :root_system) +function load_object(s::DeserializerState, ::Type{WeightLattice}, R::RootSystem) return weight_lattice(R) end -@register_serialization_type WeightLatticeElem uses_params +@register_serialization_type WeightLatticeElem + +type_params(w::WeightLatticeElem) = parent(w) function save_object(s::SerializerState, w::WeightLatticeElem) save_object(s, _vec(coefficients(w))) end function load_object(s::DeserializerState, ::Type{WeightLatticeElem}, P::WeightLattice) - return WeightLatticeElem(P, load_object(s, Vector, ZZ)) -end - -function save_type_params(s::SerializerState, w::WeightLatticeElem) - save_data_dict(s) do - save_object(s, encode_type(typeof(w)), :name) - parent_w = parent(w) - parent_ref = save_as_ref(s, parent_w) - save_object(s, parent_ref, :params) - end -end - -function load_type_params(s::DeserializerState, ::Type{WeightLatticeElem}) - return load_typed_object(s) + return WeightLatticeElem(P, load_object(s, Vector{ZZRingElem})) end ############################################################################### @@ -112,36 +91,27 @@ end @register_serialization_type WeylGroup uses_id +type_params(W::WeylGroup) = root_system(W) + function save_object(s::SerializerState, W::WeylGroup) - save_data_dict(s) do - save_typed_object(s, root_system(W), :root_system) + save_data_dict(s) do + # no data required but we leave this function here to generate a valid json + # and leave root for possible future attrs end end -function load_object(s::DeserializerState, ::Type{WeylGroup}) - R = load_typed_object(s, :root_system) +function load_object(s::DeserializerState, ::Type{WeylGroup}, R::RootSystem) return weyl_group(R) end -@register_serialization_type WeylGroupElem uses_params +@register_serialization_type WeylGroupElem + +type_params(w::WeylGroupElem) = parent(w) function save_object(s::SerializerState, x::WeylGroupElem) save_object(s, word(x)) end function load_object(s::DeserializerState, ::Type{WeylGroupElem}, W::WeylGroup) - return W(load_object(s, Vector, UInt8); normalize=false) -end - -function save_type_params(s::SerializerState, x::WeylGroupElem) - save_data_dict(s) do - save_object(s, encode_type(typeof(x)), :name) - parent_x = parent(x) - parent_ref = save_as_ref(s, parent_x) - save_object(s, parent_ref, :params) - end -end - -function load_type_params(s::DeserializerState, ::Type{WeylGroupElem}) - return load_typed_object(s) + return W(load_object(s, Vector{UInt8}); normalize=false) end diff --git a/src/Serialization/MPolyMap.jl b/src/Serialization/MPolyMap.jl index a1b3e599645f..107cabd17e8d 100644 --- a/src/Serialization/MPolyMap.jl +++ b/src/Serialization/MPolyMap.jl @@ -1,20 +1,10 @@ -@register_serialization_type MPolyAnyMap uses_params +@register_serialization_type MPolyAnyMap -function save_type_params(s::SerializerState, phi::T) where T <: MPolyAnyMap - save_data_dict(s) do - save_object(s, encode_type(T), :name) - - save_data_dict(s, :params) do - save_typed_object(s, domain(phi), :domain) - save_typed_object(s, codomain(phi), :codomain) - end - end -end - -function load_type_params(s::DeserializerState, ::Type{<:MPolyAnyMap}) - d = load_typed_object(s, :domain) - c = load_typed_object(s, :codomain) - return (d, c) +function type_params(phi::MPolyAnyMap{S, T, U, V}) where {S, T, U, V} + return Dict( + :domain => domain(phi), + :codomain => codomain(phi) + ) end function save_object(s::SerializerState, phi::MPolyAnyMap) @@ -28,11 +18,13 @@ function save_object(s::SerializerState, phi::MPolyAnyMap) end end -function load_object(s::DeserializerState, ::Type{<:MPolyAnyMap}, - params::Tuple{MPolyRing, MPolyRing}) - d = params[1] - c = params[2] - imgs = load_object(s, Vector, c, :images) +function load_object(s::DeserializerState, + ::Type{<:MPolyAnyMap}, + params::Dict) + d = params[:domain] + c = params[:codomain] + T = elem_type(c) + imgs = load_object(s, Vector{T}, c, :images) if haskey(s, :coeff_map) throw("MPolyAnyMap with coefficient map serialization unimplemented") diff --git a/src/Serialization/PolyhedralGeometry.jl b/src/Serialization/PolyhedralGeometry.jl index c41aca1ad3d4..39b85f7ed201 100644 --- a/src/Serialization/PolyhedralGeometry.jl +++ b/src/Serialization/PolyhedralGeometry.jl @@ -32,10 +32,46 @@ end ############################################################################## # Abstract Polyhedral Object -function save_type_params(s::SerializerState, obj::T) where T <: PolyhedralObject +type_params(obj::T) where {S <: Union{QQFieldElem, Float64}, T <: PolyhedralObject{S}} = coefficient_field(obj) + +function type_params(obj::T) where {S, T <: PolyhedralObject{S}} + return type_params(_polyhedral_object_as_dict(obj)) +end + +function save_type_params(s::SerializerState, obj::PolyhedralObject{S}) where S <: Union{QQFieldElem, Float64} + save_type_params(s, typeof(obj), type_params(obj)) +end + +function save_type_params(s::SerializerState, obj::T) where {S, T <: PolyhedralObject{S}} + dict = _polyhedral_object_as_dict(obj) + params = type_params(obj) save_data_dict(s) do save_object(s, encode_type(T), :name) - save_typed_object(s, coefficient_field(obj), :params) + save_data_dict(s, :params) do + for key in keys(dict) + save_type_params(s, typeof(dict[key]), params[key], key) + end + end + end +end + +function load_type_params(s::DeserializerState, T::Type{<:PolyhedralObject}) + load_node(s, :params) do obj + if !haskey(s, :_coeff) + U = decode_type(s) + params = load_type_params(s, U)[2] + return T{elem_type(U)}, params + else # handle cases where type_params is a dict of params + params = Dict{Symbol, Any}() + for (k, _) in obj + params[k] = load_node(s, k) do _ + U = decode_type(s) + # needs tuple (type, params) + load_type_params(s, U) + end + end + return T, params + end end end @@ -48,13 +84,7 @@ function save_object(s::SerializerState, obj::PolyhedralObject{<:FieldElem}) T = typeof(obj) error("Unsupported type $T for serialization") end - save_data_dict(s) do - save_typed_object(s, _polyhedral_object_as_dict(obj)) - end -end - -function load_type_params(s::DeserializerState, ::Type{<:PolyhedralObject}) - return load_typed_object(s) + save_object(s, _polyhedral_object_as_dict(obj)) end function load_object(s::DeserializerState, T::Type{<:PolyhedralObject}, @@ -67,16 +97,36 @@ function load_object(s::DeserializerState, T::Type{<:PolyhedralObject{S}}, return load_from_polymake(T, Dict{Symbol, Any}(s.obj)) end -function load_object(s::DeserializerState, T::Type{<:PolyhedralObject}, field::Field) - polymake_dict = load_typed_object(s) +function load_object(s::DeserializerState, T::Type{<:PolyhedralObject}, dict::Dict) + polymake_dict = Dict{String, Any}() + + for (k, v) in dict + if Base.issingletontype(v[1]) + polymake_dict[String(k)] = v[1]() + else + polymake_dict[String(k)] = load_object(s, v..., k) + end + end bigobject = _dict_to_bigobject(polymake_dict) + field = polymake_dict["_coeff"] + return T{elem_type(field)}(bigobject, field) end function load_object(s::DeserializerState, T::Type{<:PolyhedralObject{S}}, - field::Field) where S <: FieldElem - polymake_dict = load_typed_object(s) + dict::Dict) where S <: FieldElem + polymake_dict = Dict{String, Any}() + + for (k, v) in dict + if Base.issingletontype(v[1]) + polymake_dict[String(k)] = v[1]() + else + polymake_dict[String(k)] = load_object(s, v..., k) + end + end bigobject = _dict_to_bigobject(polymake_dict) + field = polymake_dict["_coeff"] + return T(bigobject, field) end @@ -183,8 +233,8 @@ function load_object(s::DeserializerState, ::Type{<: MixedIntegerLinearProgram}, end # use generic serialization for the other types: -@register_serialization_type Cone uses_params -@register_serialization_type PolyhedralComplex uses_params -@register_serialization_type Polyhedron uses_params -@register_serialization_type PolyhedralFan uses_params -@register_serialization_type SubdivisionOfPoints uses_params +@register_serialization_type Cone +@register_serialization_type PolyhedralComplex +@register_serialization_type Polyhedron +@register_serialization_type PolyhedralFan +@register_serialization_type SubdivisionOfPoints diff --git a/src/Serialization/QuadForm.jl b/src/Serialization/QuadForm.jl index 000e99b61078..c05a441d0713 100644 --- a/src/Serialization/QuadForm.jl +++ b/src/Serialization/QuadForm.jl @@ -1,17 +1,8 @@ ############################################################ # QuadSpace -@register_serialization_type Hecke.QuadSpace uses_params +@register_serialization_type Hecke.QuadSpace -function save_type_params(s::SerializerState, V::Hecke.QuadSpace) - save_data_dict(s) do - save_object(s, encode_type(Hecke.QuadSpace), :name) - save_type_params(s, gram_matrix(V), :params) - end -end - -function load_type_params(s::DeserializerState, ::Type{<: Hecke.QuadSpace}) - return load_params_node(s) -end +type_params(V::Hecke.QuadSpace) = type_params(gram_matrix(V)) function save_object(s::SerializerState, V::Hecke.QuadSpace) save_object(s, gram_matrix(V)) @@ -19,7 +10,7 @@ end function load_object(s::DeserializerState, ::Type{<:Hecke.QuadSpace}, params::MatSpace) gram = load_object(s, MatElem, params) - F = base_ring(params) + F = base_ring(params) return quadratic_space(F, gram) end diff --git a/src/Serialization/Rings.jl b/src/Serialization/Rings.jl index dfc1415f5b98..80ee70aac31a 100644 --- a/src/Serialization/Rings.jl +++ b/src/Serialization/Rings.jl @@ -1,54 +1,50 @@ ################################################################################ # Common union types -# this will need a better name at some point -const RingMatElemUnion = Union{RingElem, MatElem, FreeAssociativeAlgebraElem, SMat} +const RingMatElemUnion = Union{RingElem, MatElem, FreeAssociativeAlgebraElem, + SMat, TropicalSemiringElem} +const RingMatSpaceUnion = Union{Ring, MatSpace, SMatSpace, + FreeAssociativeAlgebra, TropicalSemiring} +const ModRingUnion = Union{zzModRing, ZZModRing} +const ModRingElemUnion = Union{zzModRingElem, ZZModRingElem} -# this union will also need a better name at some point -const RingMatSpaceUnion = Union{Ring, MatSpace, SMatSpace, FreeAssociativeAlgebra} +const PolyRingUnionType = Union{UniversalPolyRing, + MPolyRing, + PolyRing, + AbstractAlgebra.Generic.LaurentMPolyWrapRing} -################################################################################ -# Utility functions for ring parent tree - -# builds parent tree -function get_parents(parent_ring::T) where T <: RingMatSpaceUnion - # we have reached the end of the parent references and the current ring - # can be found as the base_ring of the previous parent without ambiguity - if !serialize_with_id(parent_ring) - return RingMatSpaceUnion[] - end - base = base_ring(parent_ring) +const IdealOrdUnionType = Union{MPolyIdeal, + LaurentMPolyIdeal, + FreeAssociativeAlgebraIdeal, + IdealGens, + MonomialOrdering} - parents = get_parents(base) - push!(parents, parent_ring) - return parents -end +const RelPowerSeriesUnionType = Union{Generic.RelPowerSeriesRing, + QQRelPowerSeriesRing, + ZZRelPowerSeriesRing, + fqPolyRepRelPowerSeriesRing, + FqRelPowerSeriesRing, + zzModRelPowerSeriesRing} +const AbsPowerSeriesUnionType = Union{Generic.AbsPowerSeriesRing, + QQAbsPowerSeriesRing, + ZZAbsPowerSeriesRing, + FqAbsPowerSeriesRing, + fqPolyRepAbsPowerSeriesRing, + zzModAbsPowerSeriesRing} + +const LaurentUnionType = Union{Generic.LaurentSeriesRing, + Generic.LaurentSeriesField, + ZZLaurentSeriesRing} ################################################################################ -# Handling RingElem MatElem, FieldElem ... Params +# type_params functions -function save_type_params(s::SerializerState, x::T) where T <: RingMatElemUnion - save_data_dict(s) do - save_object(s, encode_type(T), :name) - parent_x = parent(x) - if serialize_with_id(parent_x) - parent_ref = save_as_ref(s, parent_x) - save_object(s, parent_ref, :params) - else - save_typed_object(s, parent_x, :params) - end - end -end - -function load_type_params(s::DeserializerState, ::Type{<:RingMatElemUnion}) - return load_typed_object(s) -end - -# fix for polynomial cases -function load_object(s::DeserializerState, T::Type{<:RingMatElemUnion}, parent_ring::RingMatSpaceUnion) - parents = get_parents(parent_ring) - return load_object(s, T, parents) -end +type_params(x::T) where T <: RingMatElemUnion = parent(x) +type_params(R::T) where T <: RingMatSpaceUnion = base_ring(R) +type_params(x::T) where T <: IdealOrdUnionType = base_ring(x) +# exclude from ring union +type_params(::ZZRing) = nothing +type_params(::T) where T <: ModRingUnion = nothing ################################################################################ # ring of integers (singleton type) @@ -58,7 +54,6 @@ end # Mod Rings @register_serialization_type Nemo.zzModRing @register_serialization_type Nemo.ZZModRing -const ModRingUnion = Union{zzModRing, ZZModRing} function save_object(s::SerializerState, R::T) where T <: ModRingUnion save_object(s, modulus(R)) @@ -75,9 +70,8 @@ function load_object(s::DeserializerState, ::Type{ZZModRing}) end #elements -@register_serialization_type zzModRingElem uses_params -@register_serialization_type ZZModRingElem uses_params -const ModRingElemUnion = Union{zzModRingElem, ZZModRingElem} +@register_serialization_type zzModRingElem +@register_serialization_type ZZModRingElem function save_object(s::SerializerState, x::ModRingElemUnion) save_data_basic(s, string(x)) @@ -85,64 +79,65 @@ end function load_object(s::DeserializerState, ::Type{<:ModRingElemUnion}, parent_ring::T) where T <: ModRingUnion - return parent_ring(load_object(s, ZZRingElem)) + return parent_ring(load_object(s, ZZRingElem, ZZRing())) end ################################################################################ # Polynomial Rings -@register_serialization_type PolyRing uses_id +@register_serialization_type PolyRing uses_id @register_serialization_type MPolyRing uses_id @register_serialization_type UniversalPolyRing uses_id @register_serialization_type MPolyDecRing uses_id @register_serialization_type AbstractAlgebra.Generic.LaurentMPolyWrapRing uses_id -function save_object(s::SerializerState, R::Union{UniversalPolyRing, MPolyRing, PolyRing, AbstractAlgebra.Generic.LaurentMPolyWrapRing}) +function save_object(s::SerializerState, R::PolyRingUnionType) + base = base_ring(R) save_data_dict(s) do - save_typed_object(s, base_ring(R), :base_ring) save_object(s, symbols(R), :symbols) end end function load_object(s::DeserializerState, - T::Type{<: Union{UniversalPolyRing, MPolyRing, PolyRing, AbstractAlgebra.Generic.LaurentMPolyWrapRing}}) - base_ring = load_typed_object(s, :base_ring) - symbols = load_object(s, Vector, Symbol, :symbols) - + T::Type{<: PolyRingUnionType}, + params::Ring) + symbols = load_object(s, Vector{Symbol}, :symbols) if T <: PolyRing - return polynomial_ring(base_ring, symbols..., cached=false)[1] + return polynomial_ring(params, symbols..., cached=false)[1] elseif T <: UniversalPolyRing - poly_ring = universal_polynomial_ring(base_ring, cached=false) + poly_ring = universal_polynomial_ring(params, cached=false) gens(poly_ring, symbols) return poly_ring elseif T <: AbstractAlgebra.Generic.LaurentMPolyWrapRing - return laurent_polynomial_ring(base_ring, symbols, cached=false)[1] + return laurent_polynomial_ring(params, symbols, cached=false)[1] end - return polynomial_ring(base_ring, symbols, cached=false)[1] + return polynomial_ring(params, symbols, cached=false)[1] end # with grading +type_params(R::MPolyDecRing) = Dict( + :grading_group => type_params(_grading(R)), + :ring => forget_grading(R)) function save_object(s::SerializerState, R::MPolyDecRing) save_data_dict(s) do - save_typed_object(s, _grading(R), :grading) - save_typed_object(s, forget_grading(R), :ring) + save_object(s, _grading(R), :grading) + save_object(s, forget_grading(R), :ring) end end -function load_object(s::DeserializerState, ::Type{<:MPolyDecRing}) - ring = load_typed_object(s, :ring) - grading = load_typed_object(s, :grading) +function load_object(s::DeserializerState, ::Type{<:MPolyDecRing}, d::Dict) + ring = d[:ring] + grading = load_object(s, Vector{elem_type(d[:grading_group])}, d[:grading_group], :grading) return grade(ring, grading)[1] end ################################################################################ # Polynomial Ring Elem Types -@register_serialization_type MPolyRingElem uses_params -@register_serialization_type MPolyDecRingElem uses_params -@register_serialization_type UniversalPolyRingElem uses_params -@register_serialization_type AbstractAlgebra.Generic.LaurentMPolyWrap uses_params -const PolyElemUniontype = Union{MPolyRingElem, UniversalPolyRingElem, AbstractAlgebra.Generic.LaurentMPolyWrap} +@register_serialization_type MPolyRingElem +@register_serialization_type MPolyDecRingElem +@register_serialization_type UniversalPolyRingElem +@register_serialization_type AbstractAlgebra.Generic.LaurentMPolyWrap # elements function save_object(s::SerializerState, p::Union{UniversalPolyRingElem, MPolyRingElem}) @@ -175,7 +170,7 @@ end ################################################################################ # Univariate Polynomials -@register_serialization_type PolyRingElem uses_params +@register_serialization_type PolyRingElem function save_object(s::SerializerState, p::PolyRingElem) coeffs = coefficients(p) @@ -196,8 +191,8 @@ function save_object(s::SerializerState, p::PolyRingElem) end end -function load_object(s::DeserializerState, ::Type{<: PolyRingElem}, parents::Vector) - parent_ring = parents[end] +function load_object(s::DeserializerState, ::Type{<: PolyRingElem}, + parent_ring::PolyRing) load_node(s) do terms if isempty(terms) return parent_ring(0) @@ -214,23 +209,10 @@ function load_object(s::DeserializerState, ::Type{<: PolyRingElem}, parents::Vec base = base_ring(parent_ring) loaded_terms = zeros(base, degree) coeff_type = elem_type(base) - for (i, exponent) in enumerate(exponents) - load_node(s, i) do term - if serialize_with_params(coeff_type) - if length(parents) == 1 - params = coefficient_ring(parent_ring) - else - params = parents[1:end - 1] - end - # place coefficient at s.obj - load_node(s, 2) do _ - loaded_terms[exponent] = load_object(s, coeff_type, params) - end - else - load_node(s, 2) do _ - loaded_terms[exponent] = load_object(s, coeff_type) - end + load_node(s, i) do _ + load_node(s, 2) do _ + loaded_terms[exponent] = load_object(s, coeff_type, base) end end end @@ -241,32 +223,18 @@ end function load_object(s::DeserializerState, ::Type{<:Union{MPolyRingElem, UniversalPolyRingElem, AbstractAlgebra.Generic.LaurentMPolyWrap}}, - parents::Vector) + parent_ring::PolyRingUnionType) load_node(s) do terms exponents = [term[1] for term in terms] - parent_ring = parents[end] base = base_ring(parent_ring) polynomial = MPolyBuildCtx(parent_ring) coeff_type = elem_type(base) - for (i, e) in enumerate(exponents) load_node(s, i) do _ - c = nothing - if serialize_with_params(coeff_type) - if length(parents) == 1 - params = coefficient_ring(parent_ring) - else - params = parents[1:end - 1] - end - load_node(s, 2) do _ - c = load_object(s, coeff_type, params) - end - else - load_node(s, 2) do _ - c = load_object(s, coeff_type) - end + c = load_object(s, coeff_type, base, 2) + e_int = load_array_node(s, 1) do _ + load_object(s, Int) end - e_int = [parse(Int, x) for x in e] push_term!(polynomial, c, e_int) end end @@ -274,39 +242,16 @@ function load_object(s::DeserializerState, end end -function load_object(s::DeserializerState, ::Type{<:MPolyDecRingElem}, parents::Vector) - parent_ring = parents[end] - new_parents = push!(parents[1:end - 1], forget_grading(parent_ring)) - poly = load_object(s, MPolyRingElem, new_parents) +function load_object(s::DeserializerState, ::Type{<:MPolyDecRingElem}, parent_ring::MPolyDecRingElem) + poly = load_object(s, MPolyRingElem, forget_grading(parent_ring)) return parent_ring(poly) end - ################################################################################ # Polynomial Ideals -@register_serialization_type MPolyIdeal uses_params -@register_serialization_type LaurentMPolyIdeal uses_params - -# we should avoid this list getting too long and find a -# way to abstract saving params soon -const IdealOrdUnionType = Union{MPolyIdeal, - LaurentMPolyIdeal, - FreeAssociativeAlgebraIdeal, - IdealGens, - MonomialOrdering} - -function save_type_params(s::SerializerState, x::T) where T <: IdealOrdUnionType - save_data_dict(s) do - save_object(s, encode_type(T), :name) - ref = save_as_ref(s, base_ring(x)) - save_object(s, ref, :params) - end -end - -function load_type_params(s::DeserializerState, ::Type{<: IdealOrdUnionType}) - return load_type_params(s, RingElem) -end +@register_serialization_type MPolyIdeal +@register_serialization_type LaurentMPolyIdeal function save_object(s::SerializerState, I::T) where T <: IdealOrdUnionType save_object(s, gens(I)) @@ -314,13 +259,8 @@ end function load_object(s::DeserializerState, ::Type{<: IdealOrdUnionType}, parent_ring::RingMatSpaceUnion) gens = elem_type(parent_ring)[] - load_node(s) do gens_data - for i in 1:length(gens_data) - gen = load_node(s, i) do _ - load_object(s, elem_type(parent_ring), parent_ring) - end - push!(gens, gen) - end + load_array_node(s) do _ + push!(gens, load_object(s, elem_type(parent_ring), parent_ring)) end return ideal(parent_ring, gens) end @@ -330,7 +270,7 @@ end # this will need adjustments to cover the NCRing case -@register_serialization_type IdealGens uses_params +@register_serialization_type IdealGens function save_object(s::SerializerState, obj::IdealGens) save_data_dict(s) do @@ -357,13 +297,12 @@ end ################################################################################ # Matrices @register_serialization_type MatSpace uses_id -@register_serialization_type MatElem uses_params +@register_serialization_type MatElem @register_serialization_type SMatSpace uses_id -@register_serialization_type SMat uses_params +@register_serialization_type SMat function save_object(s::SerializerState, obj::MatSpace) save_data_dict(s) do - save_typed_object(s, base_ring(obj), :base_ring) save_object(s, ncols(obj), :ncols) save_object(s, nrows(obj), :nrows) end @@ -371,20 +310,24 @@ end function save_object(s::SerializerState, obj::SMatSpace) save_data_dict(s) do - save_typed_object(s, base_ring(obj), :base_ring) # getters currently do not seem to exist save_object(s, obj.cols, :ncols) save_object(s, obj.rows, :nrows) end end -function load_object(s::DeserializerState, ::Type{<:Union{MatSpace, SMatSpace}}) - base_ring = load_typed_object(s, :base_ring) +function load_object(s::DeserializerState, ::Type{MatSpace}, base_ring::Ring) ncols = load_object(s, Int, :ncols) nrows = load_object(s, Int, :nrows) return matrix_space(base_ring, nrows, ncols) end +function load_object(s::DeserializerState, ::Type{SMatSpace}, base_ring::Ring) + ncols = load_object(s, Int, :ncols) + nrows = load_object(s, Int, :nrows) + return SMatSpace(base_ring, nrows, ncols) +end + # elems function save_object(s::SerializerState, obj::MatElem) save_object(s, Array(obj)) @@ -398,53 +341,24 @@ function save_object(s::SerializerState, obj::SMat) end end -function load_object(s::DeserializerState, ::Type{<:MatElem}, parents::Vector) - parent = parents[end] - T = elem_type(base_ring(parent)) - if serialize_with_params(T) - if length(parents) == 1 - params = base_ring(parent) - else - params = parents[1:end - 1] - end - m = load_object(s, Matrix, (T, params)) - else - m = load_object(s, Matrix, T) - end +function load_object(s::DeserializerState, ::Type{<:MatElem}, parent::MatSpace{T}) where T + m = load_object(s, Matrix{T}, base_ring(parent)) if isempty(m) return parent() end return parent(m) end -function load_object(s::DeserializerState, ::Type{<:SMat}, parents::Vector) - parent = parents[end] +function load_object(s::DeserializerState, ::Type{<:SMat}, parent::SMatSpace{T}) where T base = base_ring(parent) - T = elem_type(base) M = sparse_matrix(base) - if serialize_with_params(T) - if length(parents) == 1 - params = base_ring(parent) - else - params = parents[1:end - 1] - end - + load_array_node(s) do _ + row_entries = Tuple{Int, T}[] load_array_node(s) do _ - row_entries = Tuple{Int, T}[] - load_array_node(s) do _ - push!(row_entries, load_object(s, Tuple, [Int, (T, params)])) - end - push!(M, sparse_row(base, row_entries)) - end - else - load_array_node(s) do _ - row_entries = Tuple{Int, T}[] - load_array_node(s) do _ - push!(row_entries, load_object(s, Tuple, [Int, T])) - end - push!(M, sparse_row(base, row_entries)) + push!(row_entries, load_object(s, Tuple{Int, T}, (nothing, base))) end + push!(M, sparse_row(base, row_entries)) end return M end @@ -453,39 +367,24 @@ end # Power Series @register_serialization_type SeriesRing uses_id -function save_object(s::SerializerState, R::Union{ - Generic.RelPowerSeriesRing, - QQRelPowerSeriesRing, - ZZRelPowerSeriesRing, - fqPolyRepRelPowerSeriesRing, - FqRelPowerSeriesRing, - zzModRelPowerSeriesRing}) + +function save_object(s::SerializerState, R::RelPowerSeriesUnionType) save_data_dict(s) do - save_typed_object(s, base_ring(R), :base_ring) save_object(s, var(R), :var) save_object(s, max_precision(R), :max_precision) save_object(s, :capped_relative, :model) end end -function save_object(s::SerializerState, R::Union{ - Generic.AbsPowerSeriesRing, - QQAbsPowerSeriesRing, - ZZAbsPowerSeriesRing, - FqAbsPowerSeriesRing, - fqPolyRepAbsPowerSeriesRing, - zzModAbsPowerSeriesRing}) - +function save_object(s::SerializerState, R::AbsPowerSeriesUnionType) save_data_dict(s) do - save_typed_object(s, base_ring(R), :base_ring) save_object(s, var(R), :var) save_object(s, max_precision(R), :max_precision) save_object(s, :capped_absolute, :model) end end -function load_object(s::DeserializerState, ::Type{<: SeriesRing}) - base_ring = load_typed_object(s, :base_ring) +function load_object(s::DeserializerState, ::Type{<: SeriesRing}, base_ring::Ring) var = load_object(s, Symbol, :var) max_precision = load_object(s, Int, :max_precision) model = load_object(s, Symbol, :model) @@ -494,8 +393,8 @@ function load_object(s::DeserializerState, ::Type{<: SeriesRing}) end # elements -@register_serialization_type RelPowerSeriesRingElem uses_params -@register_serialization_type AbsPowerSeriesRingElem uses_params +@register_serialization_type RelPowerSeriesRingElem +@register_serialization_type AbsPowerSeriesRingElem function save_object(s::SerializerState, r::RelPowerSeriesRingElem) v = valuation(r) @@ -546,8 +445,8 @@ function save_object(s::SerializerState, r::AbsPowerSeriesRingElem) end end -function load_object(s::DeserializerState, ::Type{<:RelPowerSeriesRingElem}, parents::Vector) - parent_ring = parents[end] +function load_object(s::DeserializerState, ::Type{<:RelPowerSeriesRingElem}, + parent_ring::RelPowerSeriesUnionType) valuation = load_object(s, Int, :valuation) pol_length = load_object(s, Int, :pol_length) precision = load_object(s, Int, :precision) @@ -555,52 +454,27 @@ function load_object(s::DeserializerState, ::Type{<:RelPowerSeriesRingElem}, par loaded_terms = zeros(base, pol_length) coeff_type = elem_type(base) - load_node(s, :terms) do terms - for i in 1:length(terms) - load_node(s, i) do (exponent, _) - if serialize_with_params(coeff_type) - if length(parents) == 1 - params = base - else - params = parents[1:end - 1] - end - c = load_object(s, coeff_type, params, 2) - else - c = load_object(s, coeff_type, 2) - end - e = parse(Int, exponent) - loaded_terms[e] = c - end + load_node(s, :terms) do _ + load_array_node(s) do _ + e = load_object(s, Int, 1) + loaded_terms[e] = load_object(s, coeff_type, base, 2) end end return parent_ring(loaded_terms, pol_length, precision, valuation) end -function load_object(s::DeserializerState, ::Type{<:AbsPowerSeriesRingElem}, parents::Vector) - parent_ring = parents[end] +function load_object(s::DeserializerState, ::Type{<:AbsPowerSeriesRingElem}, + parent_ring::AbsPowerSeriesUnionType) pol_length = load_object(s, Int, :pol_length) precision = load_object(s, Int, :precision) base = base_ring(parent_ring) loaded_terms = zeros(base, pol_length) coeff_type = elem_type(base) - load_node(s, :terms) do terms - for i in 1:length(terms) - load_node(s, i) do (exponent, _) - if serialize_with_params(coeff_type) - if length(parents) == 1 - params = base - else - params = parents[1:end - 1] - end - c = load_object(s, coeff_type, params, 2) - else - c = load_object(s, coeff_type, 2) - end - e = parse(Int, exponent) - e += 1 - loaded_terms[e] = c - end + load_node(s, :terms) do _ + load_array_node(s) do _ + e = load_object(s, Int, 1) + loaded_terms[e + 1] = load_object(s, coeff_type, base, 2) end end return parent_ring(loaded_terms, pol_length, precision) @@ -612,23 +486,14 @@ end @register_serialization_type Generic.LaurentSeriesField "LaurentSeriesField" uses_id @register_serialization_type ZZLaurentSeriesRing uses_id -function save_object(s::SerializerState, R::Union{ - Generic.LaurentSeriesRing, - Generic.LaurentSeriesField, - ZZLaurentSeriesRing}) +function save_object(s::SerializerState, R::LaurentUnionType) save_data_dict(s) do - save_typed_object(s, base_ring(R), :base_ring) save_object(s, var(R), :var) save_object(s, max_precision(R), :max_precision) end end -function load_object(s::DeserializerState, - ::Type{<: Union{ - Generic.LaurentSeriesRing, - Generic.LaurentSeriesField, - ZZLaurentSeriesRing}}) - base_ring = load_typed_object(s, :base_ring) +function load_object(s::DeserializerState, ::Type{<: LaurentUnionType}, base_ring::Ring) var = load_object(s, Symbol, :var) max_precision = load_object(s, Int, :max_precision) @@ -636,9 +501,9 @@ function load_object(s::DeserializerState, end # elements -@register_serialization_type Generic.LaurentSeriesFieldElem "LaurentSeriesFieldElem" uses_params -@register_serialization_type Generic.LaurentSeriesRingElem "LaurentSeriesRingElem" uses_params -@register_serialization_type ZZLaurentSeriesRingElem uses_params +@register_serialization_type Generic.LaurentSeriesFieldElem "LaurentSeriesFieldElem" +@register_serialization_type Generic.LaurentSeriesRingElem "LaurentSeriesRingElem" +@register_serialization_type ZZLaurentSeriesRingElem function save_object(s::SerializerState, r:: Union{Generic.LaurentSeriesElem, ZZLaurentSeriesRingElem}) v = valuation(r) @@ -668,11 +533,11 @@ end function load_object(s::DeserializerState, ::Type{<: Union{Generic.LaurentSeriesElem, ZZLaurentSeriesRingElem}}, - parents::Vector) - parent_ring = parents[end] - + parent_ring::LaurentUnionType) terms = load_node(s, :terms) do terms_data - exponents = [] + # reading all exponents before ... + # might be more efficient way ... + exponents = Int[] for i in 1:length(terms_data) load_node(s, i) do _ push!(exponents, load_object(s, Int, 1)) @@ -686,19 +551,9 @@ function load_object(s::DeserializerState, # account for index shift loaded_terms = zeros(base, highest_degree - lowest_degree + 1) for (i, e) in enumerate(exponents) + e -= lowest_degree - 1 load_node(s, i) do _ - e -= lowest_degree - 1 - if serialize_with_params(coeff_type) - if length(parents) == 1 - params = base - else - params = parents[1:end - 1] - end - c = load_object(s, coeff_type, params, 2) - else - c = load_object(s, coeff_type, 2) - end - loaded_terms[e] = c + loaded_terms[e] = load_object(s, coeff_type, base, 2) end end return loaded_terms @@ -713,28 +568,44 @@ end ### Affine algebras @register_serialization_type MPolyQuoRing uses_id +type_params(A::MPolyQuoRing) = Dict( + :base_ring => base_ring(A), + :ordering => ordering(A) +) + function save_object(s::SerializerState, A::MPolyQuoRing) save_data_dict(s) do # Saves stuff in a JSON dictionary. This opens a `{`, puts stuff # inside there for the various keys and then closes it with `}`. # It's not using Julia Dicts. - save_typed_object(s, modulus(A), :modulus) - save_typed_object(s, ordering(A), :ordering) # Does this already serialize??? + save_object(s, modulus(A), :modulus) end end -function load_object(s::DeserializerState, ::Type{MPolyQuoRing}) - I = load_typed_object(s, :modulus) - R = base_ring(I) - o = load_typed_object(s, :ordering) +function load_object(s::DeserializerState, ::Type{MPolyQuoRing}, params::Dict) + R = params[:base_ring] + o = params[:ordering] + I = load_object(s, ideal_type(R), R, :modulus) return MPolyQuoRing(R, I, o) end +@register_serialization_type MPolyQuoRingElem + +function save_object(s::SerializerState, a::MPolyQuoRingElem) + save_object(s, lift(a)) +end + +function load_object(s::DeserializerState, ::Type{<:MPolyQuoRingElem}, Q::MPolyQuoRing) + R = base_ring(Q) + rep = load_object(s, elem_type(R), R) + return Q(rep) +end + ### Serialization of Monomial orderings -@register_serialization_type MonomialOrdering uses_params +@register_serialization_type MonomialOrdering function save_object(s::SerializerState, o::MonomialOrdering) save_data_dict(s) do - save_typed_object(s, o.o, :internal_ordering) # TODO: Is there a getter for this? + save_object(s, o.o, :internal_ordering) # TODO: Is there a getter for this? if isdefined(o, :is_total) save_object(s, o.is_total, :is_total) end @@ -742,7 +613,8 @@ function save_object(s::SerializerState, o::MonomialOrdering) end function load_object(s::DeserializerState, ::Type{MonomialOrdering}, ring::MPolyRing) - ord = load_typed_object(s, :internal_ordering) + # this will need to be changed to include other orderings, see below + ord = load_object(s, Orderings.SymbOrdering, :internal_ordering) result = MonomialOrdering(ring, ord) if haskey(s, :is_total) @@ -756,14 +628,13 @@ end function save_object(s::SerializerState, o::Orderings.SymbOrdering{S}) where {S} save_data_dict(s) do - save_typed_object(s, S, :ordering_symbol_as_type) - save_typed_object(s, o.vars, :vars) # TODO: Is there a getter? + save_object(s, S, :ordering_symbol_as_type) + save_object(s, o.vars, :vars) # TODO: Is there a getter? end end function load_object(s::DeserializerState, ::Type{Orderings.SymbOrdering}) - S = load_typed_object(s, :ordering_symbol_as_type) - vars = load_typed_object(s, :vars) + S = load_object(s, Symbol, :ordering_symbol_as_type) + vars = load_object(s, Vector{Int}, :vars) # are these always Vector{Int} ? return Orderings.SymbOrdering(S, vars) end - diff --git a/src/Serialization/ToricGeometry.jl b/src/Serialization/ToricGeometry.jl index 7c8fad660f2e..8876119045f1 100644 --- a/src/Serialization/ToricGeometry.jl +++ b/src/Serialization/ToricGeometry.jl @@ -4,27 +4,28 @@ @register_serialization_type NormalToricVariety uses_id [:cox_ring, :class_group, :cohomology_ring] +# function type_params(ntv::T) where T <: NormalToricVarietyType +# attrs = attrs_list(T) +# if !isempty(attrs) && any([has_attribute(ntv, attr) for attr in attrs]) +# dict = Dict{Symbol, Any}() +# +# for attr in filter(x -> has_attribute(ntv, x), attrs) +# params = type_params(get_attribute(ntv, attr)) +# if !isnothing(params) +# dict[attr] = params +# end +# end +# return Dict(:attrs => dict) +# else +# return nothing +# end +# end function save_object(s::SerializerState, ntv::T) where T <: NormalToricVarietyType - attrs = attrs_list(s, T) - - if !isempty(attrs) && any([has_attribute(ntv, attr) for attr in attrs]) - save_data_dict(s) do - save_attrs(s, ntv) - save_object(s, ntv.polymakeNTV, :pm_data) - end - else - save_object(s, ntv.polymakeNTV) - end + save_object(s, ntv.polymakeNTV) end function load_object(s::DeserializerState, ::Type{T}) where {T <: Union{NormalToricVariety, AffineNormalToricVariety}} - if haskey(s, :pm_data) - ntv = T(load_object(s, Polymake.BigObject, :pm_data)) - load_attrs(s, ntv) - - return ntv - end return T(load_object(s, Polymake.BigObject)) end @@ -32,23 +33,14 @@ end # Torus invariant divisors on toric varieties @register_serialization_type ToricDivisor uses_params -function save_type_params(s::SerializerState, obj::ToricDivisor) - save_data_dict(s) do - save_object(s, encode_type(ToricDivisor), :name) - save_typed_object(s, obj.toric_variety, :params) - end -end - -function load_type_params(s::DeserializerState, ::Type{<:ToricDivisor}) - return load_typed_object(s) -end +type_params(obj::ToricDivisor) = toric_variety(obj) function save_object(s::SerializerState, td::ToricDivisor) save_object(s, td.coeffs) end function load_object(s::DeserializerState, ::Type{ToricDivisor}, tv::NormalToricVarietyType) - coeffs = load_object(s, Vector, ZZRingElem) + coeffs = load_object(s, Vector{ZZRingElem}) all = Polymake._lookup_multi(pm_object(tv), "DIVISOR") index = 0 for i in 1:length(all) @@ -65,23 +57,14 @@ end # Torus invariant divisor classes on toric varieties @register_serialization_type ToricDivisorClass uses_params -function save_type_params(s::SerializerState, obj::ToricDivisorClass) - save_data_dict(s) do - save_object(s, encode_type(ToricDivisorClass), :name) - save_typed_object(s, obj.toric_variety, :params) - end -end - -function load_type_params(s::DeserializerState, ::Type{<:ToricDivisorClass}) - return load_typed_object(s) -end +type_params(obj::ToricDivisorClass) = toric_variety(obj) function save_object(s::SerializerState, tdc::ToricDivisorClass) save_object(s, toric_divisor(tdc).coeffs) end function load_object(s::DeserializerState, ::Type{ToricDivisorClass}, tv::NormalToricVarietyType) - coeffs = load_object(s, Vector, ZZRingElem) + coeffs = load_object(s, Vector{ZZRingElem}) all = Polymake._lookup_multi(pm_object(tv), "DIVISOR") index = 0 for i in 1:length(all) @@ -96,18 +79,9 @@ end ################################################################################ # Cohomology classes on toric varieties -@register_serialization_type CohomologyClass uses_params +@register_serialization_type CohomologyClass -function save_type_params(s::SerializerState, obj::CohomologyClass) - save_data_dict(s) do - save_object(s, encode_type(CohomologyClass), :name) - save_typed_object(s, obj.v, :params) - end -end - -function load_type_params(s::DeserializerState, ::Type{<:CohomologyClass}) - return load_typed_object(s) -end +type_params(obj::CohomologyClass) = toric_variety(obj) function save_object(s::SerializerState, cc::CohomologyClass) save_data_dict(s) do diff --git a/src/Serialization/TropicalGeometry.jl b/src/Serialization/TropicalGeometry.jl index 825a80f6211e..365eb2909fb1 100644 --- a/src/Serialization/TropicalGeometry.jl +++ b/src/Serialization/TropicalGeometry.jl @@ -3,32 +3,22 @@ @register_serialization_type TropicalSemiring{typeof(max)} ## elements -@register_serialization_type TropicalSemiringElem uses_params +@register_serialization_type TropicalSemiringElem -function save_type_params(s::SerializerState, x::T) where {T <: TropicalSemiringElem} - save_data_dict(s) do - save_object(s, encode_type(T), :name) - save_typed_object(s, parent(x), :params) - end -end - - -function load_type_params(s::DeserializerState, ::Type{<:TropicalSemiringElem}) - return load_typed_object(s) -end +type_params(obj::TropicalSemiringElem) = parent(obj) function save_object(s::SerializerState, x::TropicalSemiringElem) str = string(x) save_data_basic(s, String(strip(str, ['(', ')']))) end -function load_object(s::DeserializerState, ::Type{<:TropicalSemiringElem}, params::TropicalSemiring) +function load_object(s::DeserializerState, ::Type{<:TropicalSemiringElem}, + R::TropicalSemiring) load_node(s) do str if str == "∞" || str == "-∞" || str == "infty" || str == "-infty" - return inf(params) + return inf(R) else - # looks like (q) - return params(load_object(s, QQFieldElem)) + return R(load_object(s, QQFieldElem)) end end end @@ -36,41 +26,45 @@ end # Tropical Hypersurfaces @register_serialization_type TropicalHypersurface uses_id -function save_object(s::SerializerState, t_surf::T) where T <: TropicalHypersurface +type_params(t::T) where T <: TropicalHypersurface = type_params(tropical_polynomial(t)) + +function save_object(s::SerializerState, t::T) where T <: TropicalHypersurface save_data_dict(s) do - save_typed_object(s, tropical_polynomial(t_surf), :tropical_polynomial) + save_object(s, tropical_polynomial(t), :tropical_polynomial) end end -function load_object(s::DeserializerState, ::Type{<: TropicalHypersurface}) - polynomial = load_typed_object(s, :tropical_polynomial) +function load_object(s::DeserializerState, ::Type{<: TropicalHypersurface}, + params::MPolyRing) + polynomial = load_object(s, MPolyRingElem, params, :tropical_polynomial) return tropical_hypersurface(polynomial) end # Tropical Curves -@register_serialization_type TropicalCurve uses_id +@register_serialization_type TropicalCurve uses_id uses_params + +type_params(t::TropicalCurve{M, true}) where M = type_params(polyhedral_complex(t)) +# here to handle annnoying edge case +type_params(t::TropicalCurve{M, false}) where M = "graph" -function save_object(s::SerializerState, t_curve::TropicalCurve{M, EMB}) where {M, EMB} +function save_object(s::SerializerState, t::TropicalCurve{M, EMB}) where {M, EMB} save_data_dict(s) do if EMB - save_typed_object(s, polyhedral_complex(t_curve), :polyhedral_complex) - save_object(s, true, :is_embedded) + save_object(s, polyhedral_complex(t), :polyhedral_complex) else - save_typed_object(s, graph(t_curve), :graph) - save_object(s, false, :is_embedded) + save_object(s, graph(t), :graph) end end end -function load_object(s::DeserializerState, ::Type{<: TropicalCurve}) - EMB = load_object(s, Bool, :is_embedded) - if EMB - return tropical_curve( - load_typed_object(s, :polyhedral_complex) - ) - else - return tropical_curve( - load_typed_object(s, :graph) - ) - end +function load_object(s::DeserializerState, ::Type{<: TropicalCurve}, params::Field) + return tropical_curve( + load_object(s, PolyhedralComplex, params, :polyhedral_complex) + ) +end + +function load_object(s::DeserializerState, ::Type{<: TropicalCurve}, ::String) + return tropical_curve( + load_object(s, Graph{Undirected}, :graph) + ) end diff --git a/src/Serialization/basic_types.jl b/src/Serialization/basic_types.jl index 9e69b40d8db2..d1cbfc8a65fe 100644 --- a/src/Serialization/basic_types.jl +++ b/src/Serialization/basic_types.jl @@ -2,6 +2,8 @@ function save_object(s::SerializerState, x::T) where T <: Union{BasicTypeUnion, save_data_basic(s, x) end +load_object(s::DeserializerState, T::Type, ::Nothing) = load_object(s, T) + ################################################################################ # Bool @register_serialization_type Bool @@ -22,6 +24,8 @@ end # ZZRingElem @register_serialization_type ZZRingElem +load_object(s::DeserializerState, T::Type{ZZRingElem}, ::ZZRing) = load_object(s, T) + function load_object(s::DeserializerState, ::Type{ZZRingElem}) load_node(s) do str return ZZRingElem(str) @@ -32,6 +36,8 @@ end # QQFieldElem @register_serialization_type QQFieldElem +load_object(s::DeserializerState, T::Type{QQFieldElem}, ::QQField) = load_object(s, T) + function load_object(s::DeserializerState, ::Type{QQFieldElem}) # TODO: simplify the code below once https://github.com/Nemocas/Nemo.jl/pull/1375 # is merged and in a Nemo release diff --git a/src/Serialization/containers.jl b/src/Serialization/containers.jl index 57c9c774d6d6..3b6b6c970287 100644 --- a/src/Serialization/containers.jl +++ b/src/Serialization/containers.jl @@ -1,37 +1,73 @@ +const MatVecType{T} = Union{Matrix{T}, Vector{T}, SRow{T}} +const ContainerTypes = Union{MatVecType, Set, Dict, Tuple, NamedTuple} + +function type_params(obj::S) where {T, S <:MatVecType{T}} + if isempty(obj) + return nothing + end + + params = type_params.(obj) + params_all_equal = all(map(x -> isequal(first(params), x), params)) + @req params_all_equal "Not all params of Vector or Matrix entries are the same, consider using a Tuple for serialization" + return params[1] +end + +function has_empty_entries(obj::T) where T + return false +end + +function has_empty_entries(obj::T) where T <: ContainerTypes + isempty(obj) && return true + any(has_empty_entries, obj) && return true + return false +end + +function type_params(obj::S) where {T <: ContainerTypes, S <:MatVecType{T}} + isempty(obj) && return nothing + + # empty entries can inherit params from the rest of the collection + params = type_params.(filter(!has_empty_entries, obj)) + params_all_equal = all(map(x -> isequal(first(params), x), params)) + @req params_all_equal "Not all params of Vector or Matrix entries are the same, consider using a Tuple for serialization" + return params[1] +end + ################################################################################ -# Saving and loading vectors +# loads to handle -@register_serialization_type Vector uses_params +function load_object(s::DeserializerState, T::Type{Vector{S}}, + key::Union{Symbol, Int}) where S + load_node(s, key) do _ + load_object(s, T) + end +end -const MatVecType{T} = Union{Matrix{T}, Vector{T}, SRow{T}} +################################################################################ +# Saving and loading vectors +@register_serialization_type Vector -function save_type_params(s::SerializerState, obj::S) where {T, S <:MatVecType{T}} +function save_type_params(s::SerializerState, T::Type{Vector{S}}, ::Nothing) where S save_data_dict(s) do - save_object(s, encode_type(S), :name) - if serialize_with_params(T) && !isempty(obj) - if !(T <: MatVecType) && hasmethod(parent, (T,)) - parents = parent.(obj) - parents_all_equal = all(map(isequal(first(parents)), parents)) - @req parents_all_equal "Not all parents of Vector or Matrix entries are the same, consider using a Tuple" - end - save_type_params(s, obj[1], :params) - else - save_object(s, encode_type(T), :params) - end + save_object(s, encode_type(T), :name) + save_object(s, encode_type(S), :params) end end -function load_type_params(s::DeserializerState, ::Type{<:MatVecType}) - T = decode_type(s) - if serialize_with_params(T) && haskey(s, :params) - params = load_params_node(s) - return (T, params) +function save_type_params(s::SerializerState, T::Type{Vector{U}}, obj::Any) where U + save_data_dict(s) do + save_object(s, encode_type(T), :name) + save_type_params(s, U, obj, :params) end - return T end -function load_type_params(s::DeserializerState, ::Type{<:MatVecType}, override_params::Any) - return (elem_type(override_params), override_params) +function load_type_params(s::DeserializerState, T::Type{<: MatVecType}) + !haskey(s, :params) && return T, nothing + + subtype, params = load_node(s, :params) do _ + U = decode_type(s) + subtype, params = load_type_params(s, U) + end + return T{subtype}, params end function save_object(s::SerializerState, x::Vector) @@ -47,8 +83,7 @@ function save_object(s::SerializerState, x::Vector) end end -# this should eventually become deprecated -function load_object(s::DeserializerState, ::Type{<: Vector}, params::Type) +function load_object(s::DeserializerState, ::Type{<: Vector{params}}) where params load_node(s) do v if serialize_with_id(params) loaded_v::Vector{params} = load_array_node(s) do _ @@ -64,85 +99,92 @@ function load_object(s::DeserializerState, ::Type{<: Vector}, params::Type) end end -function load_object(s::DeserializerState, ::Type{<: Vector{params}}) where params - load_node(s) do v - if serialize_with_id(params) - loaded_v::Vector{params} = load_array_node(s) do _ - load_ref(s) - end + +function load_object(s::DeserializerState, ::Type{Vector{T}}, params::S) where {T, S} + load_array_node(s) do _ + if serialize_with_id(T) + load_ref(s) else - loaded_v = params[] - for (i, entry) in enumerate(v) - push!(loaded_v, load_object(s, params, i)) - end + load_object(s, T, params) end - return loaded_v end end -# handles nested Vectors -function load_object(s::DeserializerState, ::Type{<: Vector}, params::Tuple) - T = params[1] - load_node(s) do v - if isempty(v) - return T[] - else - loaded_v = [] - len = length(v) - for i in 1:len - load_node(s, i) do _ - push!(loaded_v, load_object(s, T, params[2])) - end - end - return Vector{typeof(loaded_v[1])}(loaded_v) +function load_object(s::DeserializerState, T::Type{Vector{U}}, ::Nothing) where U + entries = load_array_node(s) do _ + load_object(s, U) + end + return T(entries) +end + +################################################################################ +# Saving and loading matrices +@register_serialization_type Matrix + +function save_object(s::SerializerState, mat::Matrix) + m, n = size(mat) + save_data_array(s) do + for i in 1:m + save_object(s, [mat[i, j] for j in 1:n]) end end end -function load_object(s::DeserializerState, ::Type{<: Vector}, params::Ring) - T = elem_type(params) - loaded_entries = load_array_node(s) do _ - if serialize_with_params(T) - return load_object(s, T, params) - else - return load_object(s, T) +function load_object(s::DeserializerState, T::Type{<:Matrix{S}}) where S + load_node(s) do entries + if isempty(entries) + return T(undef, 0, 0) + end + len = length(entries) + m = reduce(vcat, [ + permutedims(load_object(s, Vector{S}, i)) for i in 1:len + ]) + return T(m) + end +end + +function load_object(s::DeserializerState, T::Type{<:Matrix{S}}, params::Ring) where S + load_node(s) do entries + if isempty(entries) + return T(undef, 0, 0) end + + len = length(entries) + m = reduce(vcat, [ + permutedims(load_object(s, Vector{S}, params, i)) for i in 1:len + ]) + return Matrix{elem_type(params)}(m) end - return Vector{T}(loaded_entries) end ################################################################################ # Saving and loading Tuple -@register_serialization_type Tuple uses_params +@register_serialization_type Tuple + +function type_params(obj::T) where T <: Tuple + return type_params.(obj) +end -function save_type_params(s::SerializerState, tup::T) where T <: Tuple +function save_type_params(s::SerializerState, ::Type{T}, params::Tuple) where T <: Tuple save_data_dict(s) do - save_object(s, encode_type(Tuple), :name) - n = fieldcount(T) + save_object(s, encode_type(T), :name) save_data_array(s, :params) do - for i in 1:n - U = fieldtype(T, i) - if serialize_with_params(U) - save_type_params(s, tup[i]) - else - save_object(s, encode_type(U)) - end + for (i, param) in enumerate(params) + save_type_params(s, fieldtype(T, i), param) end end end end -function load_type_params(s::DeserializerState, ::Type{Tuple}) - loaded_params = Any[] - load_array_node(s) do (_, param) - T = decode_type(s) - if serialize_with_params(T) - push!(loaded_params, (T, load_params_node(s))) - else - push!(loaded_params, T) +function load_type_params(s::DeserializerState, T::Type{Tuple}) + subtype, params = load_node(s, :params) do _ + tuple_params = load_array_node(s) do _ + U = decode_type(s) + load_type_params(s, U) end + return collect(zip(tuple_params...)) end - return loaded_params + return T{subtype...}, params end function save_object(s::SerializerState, obj::Tuple) @@ -158,221 +200,125 @@ function save_object(s::SerializerState, obj::Tuple) end end -function load_object(s::DeserializerState, ::Type{<:Tuple}, params::Vector) +function load_object(s::DeserializerState, T::Type{<:Tuple}, params::Tuple) entries = load_array_node(s) do (i, entry) - if params[i] isa Type - if serialize_with_id(params[i]) - return load_ref(s) - else - return load_object(s, params[i]) - end + S = fieldtype(T, i) + if serialize_with_id(S) + return load_ref(s) else - return load_object(s, params[i][1], params[i][2]) + return load_object(s, S, params[i]) end end return Tuple(entries) end +function load_object(s::DeserializerState, T::Type{<:Tuple}) + entries = load_array_node(s) do (i, entry) + S = fieldtype(T, i) + load_object(s, S) + end + return T(entries) +end + ################################################################################ # Saving and loading NamedTuple -@register_serialization_type NamedTuple uses_params +@register_serialization_type NamedTuple + +function type_params(obj::T) where T <: NamedTuple + return NamedTuple(map(x -> x.first => type_params(x.second), collect(pairs(obj)))) +end -function save_type_params(s::SerializerState, obj::T) where T <: NamedTuple +# Named Tuples need to preserve order so they are handled seperate from Dict +function save_type_params(s::SerializerState, T::Type{<:NamedTuple}, params::NamedTuple) save_data_dict(s) do - save_object(s, encode_type(NamedTuple), :name) + save_object(s, encode_type(T), :name) save_data_dict(s, :params) do + save_data_array(s, :names) do + for name in keys(params) + save_object(s, name) + end + end save_data_array(s, :tuple_params) do - for (i, value) in enumerate(values(obj)) - U = fieldtype(T, i) - if serialize_with_params(U) - save_type_params(s, value) - else - save_object(s, encode_type(U)) - end + for (i, param) in enumerate(values(params)) + save_type_params(s, fieldtype(T, i), param) end end - save_object(s, keys(obj), :names) - end - end -end - -function save_object(s::SerializerState, obj::NamedTuple) - save_object(s, values(obj)) -end - -function load_type_params(s::DeserializerState, ::Type{<:NamedTuple}) - loaded_params = Any[] - load_array_node(s, :tuple_params) do (_, param) - if param isa String - push!(loaded_params, decode_type(s)) - else - T = decode_type(s) - params = load_params_node(s) - push!(loaded_params, (T, params)) end end - load_node(s, :names) do names - return (names, loaded_params) - end end -function load_object(s::DeserializerState, ::Type{<: NamedTuple}, params::Tuple) - keys, tuple_params = params - tuple = load_object(s, Tuple, tuple_params) - keys = Symbol.(keys) - return NamedTuple{Tuple(keys), typeof(tuple)}(tuple) -end - -################################################################################ -# Saving and loading matrices -@register_serialization_type Matrix uses_params - -function save_object(s::SerializerState, mat::Matrix) - m, n = size(mat) - save_data_array(s) do - for i in 1:m - save_object(s, [mat[i, j] for j in 1:n]) +function load_type_params(s::DeserializerState, T::Type{NamedTuple}) + subtype, params = load_node(s, :params) do obj + tuple_params = load_array_node(s, :tuple_params) do _ + U = decode_type(s) + load_type_params(s, U) end + tuple_types, named_tuple_params = collect(zip(tuple_params...)) + names = load_object(s, Vector{Symbol}, :names) + return (tuple(names...), Tuple{tuple_types...}), named_tuple_params end + return T{subtype...}, params end -function load_object(s::DeserializerState, ::Type{<:Matrix}, params::Type) - load_node(s) do entries - if isempty(entries) - return Matrix{params}(undef, 0, 0) - end - len = length(entries) - m = reduce(vcat, [ - permutedims(load_object(s, Vector, params, i)) for i in 1:len - ]) - return Matrix{params}(m) - end +function save_object(s::SerializerState, obj::NamedTuple) + save_object(s, values(obj)) end -function load_object(s::DeserializerState, ::Type{<:Matrix}, params::Tuple) - load_node(s) do entries - if isempty(entries) - return Matrix{params[1]}(undef, 0, 0) - end - - len = length(entries) - m = reduce(vcat, [ - permutedims(load_object(s, Vector, params, i)) for i in 1:len - ]) - return Matrix{params[1]}(m) - end +function load_object(s::DeserializerState, T::Type{<: NamedTuple}, params::Tuple) + return T(load_object(s, Tuple{fieldtypes(T)...}, params)) end ################################################################################ # Saving and loading dicts -@register_serialization_type Dict uses_params - -function save_type_params(s::SerializerState, obj::Dict{S, Any}) where S <: Union{Symbol, String, Int} - save_data_dict(s) do - save_object(s, encode_type(Dict), :name) - save_data_dict(s, :params) do - save_object(s, encode_type(S), :key_type) - for (k, v) in obj - U = typeof(v) - if serialize_with_params(U) - save_type_params(s, v, Symbol(k)) - else - save_object(s, encode_type(U), Symbol(k)) - end - end - end - end +@register_serialization_type Dict +function type_params(obj::T) where T <: Dict + return Dict(map(x -> x.first => type_params(x.second), collect(pairs(obj)))) end -function save_type_params(s::SerializerState, obj::Dict{S, T}) where {S <: Union{Symbol, String, Int}, T} +function save_type_params(s::SerializerState, obj::Dict{S, T}) where {T, S <: Union{Symbol, Int, String}} + params = type_params(obj) save_data_dict(s) do save_object(s, encode_type(Dict), :name) save_data_dict(s, :params) do save_object(s, encode_type(S), :key_type) - - if serialize_with_params(T) - if isempty(obj) - save_object(s, encode_type(T), :value_type) - else - v = first(values(obj)) - save_object(s, encode_type(T), :value_type) - save_type_params(s, v, :value_params) - end - else - save_object(s, encode_type(T), :value_type) + isempty(params) && save_object(s, encode_type(T), :value_type) + for (k, v) in params + save_type_params(s, typeof(obj[k]), params[k], Symbol(k)) end end end end -function save_type_params(s::SerializerState, obj::Dict{T, S}) where {T, S} - save_data_dict(s) do - save_object(s, encode_type(Dict), :name) - save_data_dict(s, :params) do - if serialize_with_params(S) - if isempty(obj) - save_object(s, encode_type(S), :value_type) - else - v = first(values(obj)) - save_object(s, encode_type(S), :value_type) - save_type_params(s, v, :value_params) +function load_type_params(s::DeserializerState, T::Type{Dict}) + subtype, params = load_node(s, :params) do obj + S = load_node(s, :key_type) do _ + decode_type(s) + end + params_dict = Dict{S, Any}() + if S <: Union{String, Symbol, Int} + value_types = Type[] + for (k, _) in obj + k == :key_type && continue + if k == :value_type + load_node(s, k) do _ + push!(value_types, decode_type(s)) + end + continue end - else - save_object(s, encode_type(S), :value_type) - end - - if serialize_with_params(T) - if isempty(obj) - save_object(s, encode_type(T), :key_type) - else - k = first(keys(obj)) - save_object(s, encode_type(T), :key_type) - save_type_params(s, k, :key_params) + key = S == Int ? parse(Int, string(k)) : S(k) + params_dict[key] = load_node(s, k) do _ + value_type = decode_type(s) + return load_type_params(s, value_type) end - else - save_object(s, encode_type(T), :key_type) - end - end - end -end - -function load_type_params(s::DeserializerState, ::Type{<:Dict}) - if haskey(s, :value_type) - key_type = load_node(_ -> decode_type(s), s, :key_type) - value_type = load_node(_ -> decode_type(s), s, :value_type) - d = Dict{Symbol, Any}(:key_type => key_type, :value_type => value_type) - - if serialize_with_params(value_type) - d[:value_params] = load_node(s, :value_params) do _ - load_params_node(s) - end - end - - if serialize_with_params(key_type) - d[:key_params] = load_node(s, :key_params) do _ - load_params_node(s) - end - end - - return d - end - - params_dict = Dict{Symbol, Any}() - for (k, _) in s.obj - load_node(s, k) do _ - value_type = decode_type(s) - - if serialize_with_params(value_type) - params_dict[k] = Dict{Symbol, Any}( - type_key => value_type, - :params => load_params_node(s) - ) - else - params_dict[k] = value_type + push!(value_types, params_dict[key][1]) end + return (S, Union{value_types...}), params_dict + else + error{"not implemented yet"} end end - return params_dict + + return Dict{subtype...}, params end function save_object(s::SerializerState, obj::Dict{S, T}) where {S <: Union{Symbol, String, Int}, T} @@ -385,95 +331,65 @@ function save_object(s::SerializerState, obj::Dict{S, T}) where {S <: Union{Symb end end -function save_object(s::SerializerState, obj::Dict) - save_data_array(s) do - for (k, v) in obj - save_object(s, (k, v)) +function load_object(s::DeserializerState, + T::Type{<:Dict{S, U}}, + params::Dict{S, V}) where {S <: Union{Symbol, String, Int}, U, V} + dict = T() + for k in keys(params) + # has no data, hence no key was generated on the data side + if Base.issingletontype(params[k][1]) + dict[k] = params[k][1]() + else + dict[k] = load_object(s, params[k]..., Symbol(k)) end end + return dict end -function load_object(s::DeserializerState, ::Type{Dict{String, Int}}) - return Dict{String, Int}(string(k) => parse(Int, v) for (k,v) in s.obj) -end - -function load_object(s::DeserializerState, ::Type{Dict{Int, Int}}) - return Dict{Int, Int}(parse(Int,string(k)) => parse(Int, v) for (k,v) in s.obj) +function load_object(s::DeserializerState, + T::Type{<:Dict{S, U}}) where {S <: Union{Int, Symbol, String}, U <: Union{Symbol, String, Int}} + dict = T() + for k in keys(s.obj) + dict[S(k)] = load_object(s, U, Symbol(k)) + end + return dict end -function load_object(s::DeserializerState, ::Type{<:Dict}, params::Dict{Symbol, Any}) - key_type = params[:key_type] - value_type = haskey(params, :value_type) ? params[:value_type] : Any - dict = Dict{key_type, value_type}() - - for (i, (k, _)) in enumerate(s.obj) - if k == :key_type - continue - end - - if key_type == Int - key = parse(Int, string(k)) - elseif haskey(params, :key_params) # type is not Int, String or Symbol - load_node(s, i) do _ - # 1 is for first entry of tuple which is the key in this case - key = load_object(s, key_type, params[:key_params], 1) - end - else - key = key_type(k) - end - - if value_type != Any - if serialize_with_params(value_type) - if haskey(params, :key_params) # key type is not Int, String or Symbol - load_node(s, i) do _ - # 2 is for second entry of tuple which is the value in this case - dict[key] = load_object(s, value_type, params[:value_params], 2) - end - else - dict[key] = load_object(s, value_type, params[:value_params], k) - end - else - if haskey(params, :key_params) # key type is not Int, String or Symbol - load_node(s, i) do _ - # 2 is for second entry of tuple which is the value in this case - dict[key] = load_object(s, value_type, 2) - end - else - dict[key] = load_object(s, value_type, k) - end - end - elseif params[k] isa Type - dict[key] = load_object(s, params[k], k) - else - dict[key] = load_object(s, params[k][type_key], params[k][:params], k) - end +function load_object(s::DeserializerState, + T::Type{<:Dict{Int, S}}) where {S <: Union{Symbol, String, Int}} + dict = T() + for k in keys(s.obj) + dict[parse(Int, string(k))] = load_object(s, S, k) end - return dict end ################################################################################ # Saving and loading sets -@register_serialization_type Set uses_params +@register_serialization_type Set -function save_type_params(s::SerializerState, obj::Set{T}) where T +function type_params(obj::T) where T <: Set + if isempty(obj) + return nothing + end + return type_params(first(obj)) +end + +function save_type_params(s::SerializerState, T::Type{Set{S}}, ::Nothing) where S save_data_dict(s) do - save_object(s, encode_type(Set), :name) - if serialize_with_params(T) && !isempty(obj) - save_type_params(s, first(obj), :params) - else - save_object(s, encode_type(T), :params) - end + save_object(s, encode_type(T), :name) + save_object(s, encode_type(S), :params) end end -function load_type_params(s::DeserializerState, ::Type{<:Set}) - T = decode_type(s) - if serialize_with_params(T) && haskey(s, :params) - params = load_params_node(s) - return (T, params) +function load_type_params(s::DeserializerState, T::Type{<: Set}) + !haskey(s, :params) && return T, nothing + + subtype, params = load_node(s, :params) do _ + U = decode_type(s) + subtype, params = load_type_params(s, U) end - return T + return T{subtype}, params end function save_object(s::SerializerState, x::Set) @@ -489,55 +405,24 @@ function save_object(s::SerializerState, x::Set) end end -function load_object(s::DeserializerState, ::Type{<: Set}, params::Any) - load_node(s) do v - if serialize_with_id(params) - loaded_v = params[load_ref(s, x) for x in v] - else - loaded_v = params[] - for (i, entry) in enumerate(v) - push!(loaded_v, load_object(s, params, i)) - end - end - return Set(loaded_v) - end -end - -# handles nested -function load_object(s::DeserializerState, ::Type{<: Set}, params::Tuple) - T = params[1] - load_node(s) do v - if isempty(v) - return Set{T}() - else - loaded_v = Set{T}() - len = length(v) - for i in 1:len - load_node(s, i) do _ - push!(loaded_v, load_object(s, T, params[2])) - end - end - return Set{typeof(first(loaded_v))}(loaded_v) - end +function load_object(s::DeserializerState, S::Type{<:Set{T}}, params::Any) where T + elems = load_array_node(s) do _ + load_object(s, T, params) end + return S(elems) end -function load_object(s::DeserializerState, ::Type{<: Set}, params::Ring) - T = elem_type(params) - loaded_entries = load_array_node(s) do _ - if serialize_with_params(T) - return load_object(s, T, params) - else - return load_object(s, T) - end +function load_object(s::DeserializerState, S::Type{<:Set{T}}, ::Nothing) where T + elems = load_array_node(s) do _ + load_object(s, T) end - return Set{T}(loaded_entries) + return S(elems) end ################################################################################ # Sparse rows -@register_serialization_type SRow uses_params +@register_serialization_type SRow function save_object(s::SerializerState, obj::SRow) save_data_array(s) do @@ -553,11 +438,7 @@ function load_object(s::DeserializerState, ::Type{<:SRow}, params::Ring) values = entry_type[] load_array_node(s) do _ push!(pos, load_object(s, Int, 1)) - if serialize_with_params(entry_type) - push!(values, load_object(s, entry_type, params, 2)) - else - push!(values, load_object(s, entry_type, 2)) - end + push!(values, load_object(s, entry_type, params, 2)) end return sparse_row(params, pos, values) end diff --git a/src/Serialization/main.jl b/src/Serialization/main.jl index fff1110bf739..37f5f8ce19d1 100644 --- a/src/Serialization/main.jl +++ b/src/Serialization/main.jl @@ -71,6 +71,10 @@ end # Type attribute map const type_attr_map = Dict{String, Vector{Symbol}}() +attrs_list(T::Type) = get(type_attr_map, encode_type(T), Symbol[]) + +with_attrs(s::T) where T <: Union{DeserializerState, SerializerState} = s.with_attrs + ################################################################################ # (De|En)coding types @@ -126,21 +130,6 @@ end ################################################################################ # High level -function save_as_ref(s::SerializerState, obj::T) where T - # find ref or create one - ref = get(global_serializer_state.obj_to_id, obj, nothing) - if ref !== nothing - if !(ref in s.refs) - push!(s.refs, ref) - end - return string(ref) - end - ref = global_serializer_state.obj_to_id[obj] = uuid4() - global_serializer_state.id_to_obj[ref] = obj - push!(s.refs, ref) - return string(ref) -end - function save_object(s::SerializerState, x::Any, key::Symbol) set_key(s, key) save_object(s, x) @@ -164,15 +153,17 @@ function save_header(s::SerializerState, h::Dict{Symbol, Any}, key::Symbol) end function save_typed_object(s::SerializerState, x::T) where T - if serialize_with_params(T) - save_type_params(s, x, type_key) - save_object(s, x, :data) - elseif Base.issingletontype(T) + if Base.issingletontype(T) save_object(s, encode_type(T), type_key) else - save_object(s, encode_type(T), type_key) + save_type_params(s, x, type_key) save_object(s, x, :data) end + + if with_attrs(s) + attrs = attrs_list(T) + !isempty(attrs) && save_attrs(s, x) + end end function save_typed_object(s::SerializerState, x::T, key::Symbol) where T @@ -188,58 +179,115 @@ function save_typed_object(s::SerializerState, x::T, key::Symbol) where T end end +type_params(obj::T) where T = nothing + function save_type_params(s::SerializerState, obj::Any, key::Symbol) set_key(s, key) save_type_params(s, obj) end -function save_attrs(s::SerializerState, obj::T) where T - !with_attrs(s) && return - if any(attr -> has_attribute(obj, attr), attrs_list(s, T)) - save_data_dict(s, :attrs) do - for attr in attrs_list(s, T) - has_attribute(obj, attr) && save_typed_object(s, get_attribute(obj, attr), attr) +function save_type_params(s::SerializerState, T::Type, params::Any, key::Symbol) + set_key(s, key) + save_type_params(s, T, params) +end + +save_type_params(s::SerializerState, obj::Any) = save_type_params(s, typeof(obj), type_params(obj)) +save_type_params(s::SerializerState, T::Type, ::Nothing) = save_object(s, encode_type(T)) + +function save_type_params(s::SerializerState, T::Type, params::Any) + save_data_dict(s) do + save_object(s, encode_type(T), :name) + save_typed_object(s, params, :params) + end +end + +# splits all params that are dictionaries into vector of pair +# to be able to handle varying types in the values +function save_type_params(s::SerializerState, T::Type, params::Dict) + save_type_params(s, T, collect(pairs(params))) +end + +# This is used for types that have multiple parameters +function save_type_params(s::SerializerState, T::Type, + params::Vector{<:Pair{S, U}}) where {S <: Union{Symbol, String}, U} + save_data_dict(s) do + save_object(s, encode_type(T), :name) + save_data_dict(s, :params) do + for param in params + isnothing(param.second) && continue + save_type_params(s, typeof(param.second), param.second, Symbol(param.first)) end end end end -# The load mechanism first checks if the type needs to load necessary -# parameters before loading it's data, if so a type tree is traversed -function load_typed_object(s::DeserializerState, key::Symbol; override_params::Any = nothing) - load_node(s, key) do node - if node isa String && !isnothing(tryparse(UUID, node)) - return load_ref(s) +function load_type_params(s::DeserializerState, T::Type, key::Symbol) + load_node(s, key) do _ + load_type_params(s, T) + end +end + +function load_type_params(s::DeserializerState, T::Type) + if s.obj isa String + if !isnothing(tryparse(UUID, s.obj)) + return T, load_ref(s) + end + return T, nothing + end + if haskey(s, :params) + load_node(s, :params) do obj + if obj isa String || haskey(s, :params) + U = decode_type(s) + params = load_type_params(s, U)[2] + + # handle cases where type_params is a dict of params + elseif !haskey(obj, type_key) + params = Dict{Symbol, Any}() + for (k, _) in obj + params[k] = load_node(s, k) do _ + U = decode_type(s) + return load_type_params(s, U)[2] + end + end + else + params = load_typed_object(s) + end + # all types where the type T should be updated with a subtype i.e. T -> T{U} + # need to implement their own method, see for example containers + return T, params end - return load_typed_object(s; override_params=override_params) + else + return T, load_typed_object(s) + end +end + +function load_typed_object(s::DeserializerState, key::Symbol; override_params::Any = nothing) + load_node(s, key) do _ + load_typed_object(s; override_params=override_params) end end +# The load mechanism first checks if the type needs to load necessary +# parameters before loading it's data, if so a type tree is traversed function load_typed_object(s::DeserializerState; override_params::Any = nothing) T = decode_type(s) - if Base.issingletontype(T) && return T() - elseif serialize_with_params(T) - if !isnothing(override_params) - if override_params isa Dict - error("Unsupported override type") - else - params = override_params - end + Base.issingletontype(T) && return T() + if !isnothing(override_params) + if override_params isa Dict + error("Unsupported override type") else - # depending on the type, :params is either an object to be loaded or a - # dict with keys and object values to be loaded - params = load_node(s, type_key) do _ - load_params_node(s) - end - end - load_node(s, :data) do _ - return load_object(s, T, params) + T, _ = load_type_params(s, T, type_key) + params = override_params end else - load_node(s, :data) do _ - return load_object(s, T) - end + s.obj isa String && !isnothing(tryparse(UUID, s.obj)) && return load_ref(s) + T, params = load_type_params(s, T, type_key) end + obj = load_node(s, :data) do _ + return load_object(s, T, params) + end + load_attrs(s, obj) + return obj end function load_object(s::DeserializerState, T::Type, key::Union{Symbol, Int}) @@ -248,12 +296,25 @@ function load_object(s::DeserializerState, T::Type, key::Union{Symbol, Int}) end end -function load_object(s::DeserializerState, T::Type, params::Any, key::Union{Symbol, Int}) +function load_object(s::DeserializerState, T::Type, params::S, + key::Union{Symbol, Int}) where S load_node(s, key) do _ load_object(s, T, params) end end +################################################################################ +# serializing attributes +function save_attrs(s::SerializerState, obj::T) where T + if any(attr -> has_attribute(obj, attr), attrs_list(T)) + save_data_dict(s, :attrs) do + for attr in attrs_list(T) + has_attribute(obj, attr) && save_typed_object(s, get_attribute(obj, attr), attr) + end + end + end +end + function load_attrs(s::DeserializerState, obj::T) where T !with_attrs(s) && return @@ -286,19 +347,6 @@ function load_object_generic(s::DeserializerState, ::Type{T}, dict::Dict) where return T(fields...) end -################################################################################ -# Utility functions for parent tree - -# loads parent tree -function load_parents(s::DeserializerState, parent_ids::Vector) - loaded_parents = [] - for id in parent_ids - loaded_parent = load_ref(s, id) - push!(loaded_parents, loaded_parent) - end - return loaded_parents -end - ################################################################################ # Type Registration function register_serialization_type(@nospecialize(T::Type), str::String) @@ -357,10 +405,14 @@ function register_serialization_type(ex::Any, str::String, uses_id::Bool, if !($ex <: Union{Number, String, Bool, Symbol, Vector, Tuple, Matrix, NamedTuple, Dict, Set}) function Oscar.serialize(s::Oscar.AbstractSerializer, obj::T) where T <: $ex Oscar.serialize_type(s, T) - Oscar.save(s.io, obj; serializer=Oscar.IPCSerializer()) + Oscar.save(s.io, obj; serializer=Oscar.IPCSerializer( + worker_id_from_socket(s.io) + )) end - function Oscar.deserialize(s::Oscar.AbstractSerializer, ::Type{<:$ex}) - Oscar.load(s.io; serializer=Oscar.IPCSerializer()) + function Oscar.deserialize(s::Oscar.AbstractSerializer, T::Type{<:$ex}) + Oscar.load(s.io; serializer=Oscar.IPCSerializer( + worker_id_from_socket(s.io) + )) end end end) @@ -439,7 +491,6 @@ macro import_all_serialization_functions() load_array_node, load_attrs, load_node, - load_params_node, load_ref, load_typed_object, save_as_ref, @@ -656,7 +707,7 @@ function load(io::IO; params::Any = nothing, type::Any = nothing, jsondict_str = JSON3.write(jsondict) s = deserializer_open(IOBuffer(jsondict_str), serializer, - with_attrs) + with_attrs) end try @@ -670,24 +721,18 @@ function load(io::IO; params::Any = nothing, type::Any = nothing, U = load_node(s, type_key) do _ decode_type(s) end + U <: type || U >: type || error("Type in file doesn't match target type: $(dict[type_key]) not a subtype of $T") - if serialize_with_params(type) - if isnothing(params) - params = load_node(s, type_key) do _ - load_params_node(s) - end - end - - load_node(s, :data) do _ - loaded = load_object(s, type, params) - end - else - Base.issingletontype(type) && return type() - load_node(s, :data) do _ - loaded = load_object(s, type) + Base.issingletontype(type) && return type() + if isnothing(params) + _, params = load_node(s, type_key) do _ + load_type_params(s, U) end end + load_node(s, :data) do _ + loaded = load_object(s, type, params) + end else loaded = load_typed_object(s; override_params=params) end diff --git a/src/Serialization/polymake.jl b/src/Serialization/polymake.jl index 432b58706a77..0631dfa9acd4 100644 --- a/src/Serialization/polymake.jl +++ b/src/Serialization/polymake.jl @@ -115,7 +115,7 @@ _pmdata_for_oscar(s::Polymake.Set, coeff::Field) = Set(_pmdata_for_oscar(e, coef function _bigobject_to_dict(bo::Polymake.BigObject, coeff::Field) - data = Dict{String,Any}() + data = Dict{Symbol,Any}() for pname in Polymake.list_properties(bo) p = Polymake.give(bo, pname) if p isa Polymake.PropertyValue @@ -123,7 +123,11 @@ function _bigobject_to_dict(bo::Polymake.BigObject, coeff::Field) else try obj = _pmdata_for_oscar(p, coeff) - data[pname] = obj + if haskey(obj, :_coeff) + data[Symbol(pname)] = obj + else + data[Symbol(pname)] = p + end catch e if e isa MethodError @debug "failed to convert $pname of type $(typeof(p)) to Oscar, skipping" @@ -136,7 +140,7 @@ function _bigobject_to_dict(bo::Polymake.BigObject, coeff::Field) description = Polymake.getdescription(bo) if !isempty(description) - data["_description"] = String(description) + data[:_description] = String(description) end data end @@ -144,8 +148,8 @@ end function _polyhedral_object_as_dict(x::Oscar.PolyhedralObjectUnion) bo = Oscar.pm_object(x) data = _bigobject_to_dict(bo, coefficient_field(x)) - data["_type"] = Polymake.bigobject_qualifiedname(bo) - data["_coeff"] = coefficient_field(x) + data[:_polymake_type] = Polymake.bigobject_qualifiedname(bo) + data[:_coeff] = coefficient_field(x) return data end @@ -181,7 +185,7 @@ function _load_bigobject_from_dict!(obj::Polymake.BigObject, dict::Dict, parent_ end function _dict_to_bigobject(dict::Dict{String, Any}) - obj = Polymake.BigObject(Polymake.BigObjectType(dict["_type"])) + obj = Polymake.BigObject(Polymake.BigObjectType(dict["_polymake_type"])) _load_bigobject_from_dict!(obj, dict) return obj end diff --git a/src/Serialization/serializers.jl b/src/Serialization/serializers.jl index 8b8708e313e9..abd98038f378 100644 --- a/src/Serialization/serializers.jl +++ b/src/Serialization/serializers.jl @@ -7,7 +7,9 @@ abstract type OscarSerializer end struct JSONSerializer <: OscarSerializer end -struct IPCSerializer <: OscarSerializer end +struct IPCSerializer <: OscarSerializer + worker_pid::Int +end abstract type MultiFileSerializer <: OscarSerializer end @@ -145,6 +147,57 @@ function save_data_json(s::SerializerState, jsonstr::Any, write(s.io, jsonstr) end + +function save_as_ref(s::SerializerState, obj::T) where T + # find ref or create one + ref = get(global_serializer_state.obj_to_id, obj, nothing) + if !isnothing(ref) + if !(ref in s.refs) + push!(s.refs, ref) + end + return string(ref) + end + ref = global_serializer_state.obj_to_id[obj] = uuid4() + global_serializer_state.id_to_obj[ref] = obj + push!(s.refs, ref) + return string(ref) +end + +function save_as_ref(s::SerializerState{IPCSerializer}, obj::T) where T + ref = get(global_serializer_state.obj_to_id, obj, nothing) + w = s.serializer.worker_pid + if !isnothing(ref) + # check if ref already exists on worker + f = remotecall_fetch( + (ref) -> haskey(Oscar.global_serializer_state.id_to_obj, Oscar.UUID(ref)), + w, + string(ref)) #&& return string(ref) + return string(ref) + else + ref = uuid4() + global_serializer_state.id_to_obj[ref] = obj + end + + rrid = Distributed.RRID(myid(), w) + put!(channel_from_id(rrid), obj) + + return string(ref) +end + +function handle_refs(s::SerializerState) + if !isempty(s.refs) + save_data_dict(s, refs_key) do + for id in s.refs + ref_obj = global_serializer_state.id_to_obj[id] + s.key = Symbol(id) + save_data_dict(s) do + save_typed_object(s, ref_obj) + end + end + end + end +end + function serializer_close(s::SerializerState) finish_writing(s) end @@ -164,6 +217,7 @@ mutable struct DeserializerState{T <: OscarSerializer} end # general loading of a reference + function load_ref(s::DeserializerState) id = s.obj if haskey(global_serializer_state.id_to_obj, UUID(id)) @@ -178,6 +232,7 @@ function load_ref(s::DeserializerState) end function haskey(s::DeserializerState, key::Symbol) + s.obj isa String && return false load_node(s) do obj key in keys(obj) end @@ -210,14 +265,11 @@ function load_array_node(f::Function, s::DeserializerState, end end -function load_params_node(s::DeserializerState) - T = decode_type(s) - load_node(s, :params) do _ - return load_type_params(s, T) - end -end - -function serializer_open(io::IO, serializer::OscarSerializer, with_attrs::Bool) +function serializer_open( + io::IO, + serializer::OscarSerializer, + with_attrs::Bool) + # some level of handling should be done here at a later date return SerializerState(serializer, true, UUID[], io, nothing, with_attrs) end @@ -228,7 +280,7 @@ function deserializer_open(io::IO, serializer::OscarSerializer, with_attrs::Bool if haskey(obj, refs_key) refs = obj[refs_key] end - + return DeserializerState(serializer, obj, nothing, refs, with_attrs) end @@ -241,22 +293,40 @@ function deserializer_open(io::IO, serializer::IPCSerializer, with_attrs::Bool) return DeserializerState(serializer, obj, nothing, nothing, with_attrs) end -function handle_refs(s::SerializerState) - if !isempty(s.refs) - save_data_dict(s, refs_key) do - for id in s.refs - ref_obj = global_serializer_state.id_to_obj[id] - s.key = Symbol(id) - save_data_dict(s) do - save_typed_object(s, ref_obj) - end - end - end - end +################################################################################ +# Refs Channel +import Base: put!, wait, isready, take!, fetch + +mutable struct RefChannel{T} <: AbstractChannel{T} + stack::Vector + cond_take::Condition # waiting for data to become available + RefChannel{T}() where T = new([], Condition()) end -function attrs_list(s::U, T::Type) where U <: Union{DeserializerState, SerializerState} - return get(type_attr_map, encode_type(T), Symbol[]) +function put!(D::RefChannel, v) + push!(D.stack, v) + notify(D.cond_take) + D end -with_attrs(s::T) where T <: Union{DeserializerState, SerializerState} = s.with_attrs +function take!(D::RefChannel) + v = fetch(D) + delete!(D.d, k) + v +end + +isready(D::RefChannel) = length(D.d) > 1 + +function fetch(D::RefChannel) + wait(D,k) + D.d[k] +end + +function wait(D::RefChannel) + while !isready(D, k) + wait(D.cond_take) + end +end + + + diff --git a/test/Serialization/IPC.jl b/test/Serialization/IPC.jl index 8abeffc716d2..e96a6eccc38c 100644 --- a/test/Serialization/IPC.jl +++ b/test/Serialization/IPC.jl @@ -4,19 +4,14 @@ process_ids = addprocs(1) @everywhere using Oscar -@testset "Interprocess Serialization" begin - channels = Oscar.params_channels(Union{Ring, MatSpace}) - +wp = WorkerPool(RemoteChannel(()->Oscar.RefChannel{Any}())) +foreach(w->push!(wp, w), process_ids) Qx, x = QQ[:x] F, a = number_field(x^2 + x + 1) MR = matrix_space(F, 2, 2) - - Oscar.put_params(channels, Qx) - Oscar.put_params(channels, F) - Oscar.put_params(channels, MR) c = [MR([a^i F(1); a a + 1]) for i in 1:5] - dets = pmap(det, c) + dets = pmap(det, wp, c) total = reduce(*, dets) @test total == F(4) diff --git a/test/Serialization/PolyhedralGeometry.jl b/test/Serialization/PolyhedralGeometry.jl index 9d04515719d3..b36c31c0a578 100644 --- a/test/Serialization/PolyhedralGeometry.jl +++ b/test/Serialization/PolyhedralGeometry.jl @@ -74,7 +74,7 @@ using Oscar: _integer_variables f_vector(d_hedron) lattice_points(d_hedron) - dict_ps = Dict{String, Any}( + dict_ps = Dict{String, Polyhedron}( "unprecise" => polyhedron( Polymake.common.convert_to{Float64}(Oscar.pm_object(d_hedron)) ), diff --git a/test/Serialization/PolynomialsSeries.jl b/test/Serialization/PolynomialsSeries.jl index 833fc8a7068d..7b5c13f47df5 100644 --- a/test/Serialization/PolynomialsSeries.jl +++ b/test/Serialization/PolynomialsSeries.jl @@ -36,7 +36,6 @@ cases = [ (P7, 7 + 3*7^2, 7^5, "Padic Field"), ] - @testset "Serialization.Polynomials.and.Series" begin mktempdir() do path @testset "Empty Ideal" begin @@ -70,7 +69,7 @@ cases = [ test_save_load_roundtrip(path, p) do loaded @test loaded == p end - + @testset "Load with params" begin test_save_load_roundtrip(path, p; params=R) do loaded @test loaded == z^2 + case[2] * z + case[3] diff --git a/test/Serialization/containers.jl b/test/Serialization/containers.jl index 119ba29490cd..b84c5ae2e3c7 100644 --- a/test/Serialization/containers.jl +++ b/test/Serialization/containers.jl @@ -42,7 +42,7 @@ end @testset "Vector{FpFieldElem}" begin - F = GF(ZZRingElem(77777732222322222232222222223)) + F = FpField(ZZRingElem(77777732222322222232222222223)) one = F(1) minusone = F(-1) v = [one, minusone] @@ -52,10 +52,17 @@ test_save_load_roundtrip(path, v; params=F) do loaded @test v == loaded end + + # tests loading into other types + filename = joinpath(path, "original.json") + save(filename, v;) + loaded = load(filename; params=GF(ZZRingElem(77777732222322222232222222223)), + type=Vector{FqFieldElem}) + @test loaded isa Vector{FqFieldElem} end @testset "Vector{fpFieldElem}" begin - F = GF(7) + F = fpField(UInt(7)) one = F(1) minusone = F(-1) v = [one, minusone] @@ -65,6 +72,12 @@ test_save_load_roundtrip(path, v; params=F) do loaded @test v == loaded end + # tests loading into other types + filename = joinpath(path, "original.json") + save(filename, v;) + loaded = load(filename; params=GF(ZZRingElem(77777732222322222232222222223)), + type=Vector{FqFieldElem}) + @test loaded isa Vector{FqFieldElem} end @testset "Tuple" begin @@ -79,20 +92,6 @@ end end - # Does it make sense to save such types? - # I feel that these types should be discouraged? - # @testset "Vector{Union{Polyhedron, LinearProgram}}" begin - # c = cube(3) - # LP0 = linear_program(c, [2,2,-3]) - # v = Vector{Union{Polyhedron, LinearProgram}}([c, LP0]) - # test_save_load_roundtrip(path, v) do loaded - # @test length(v) == length(loaded) - # @test loaded[1] isa Polyhedron - # @test loaded[2] isa LinearProgram - # @test loaded isa Vector - # end - # end - @testset "Testing (de)serialization of Vector{$(T)}" for T in ( UInt, UInt128, UInt16, UInt32, UInt64, UInt8, @@ -125,7 +124,7 @@ ) Qx, x = QQ[:x] p = x^2 + 1 - original = Dict(keys[1] => cube(:2), keys[2] => p) + original = Dict{S, Union{PolyRingElem, Polyhedron}}(keys[1] => cube(2), keys[2] => p) test_save_load_roundtrip(path, original) do loaded @test original == loaded end