Skip to content

Commit

Permalink
Serialization of Lie algebras (#3980)
Browse files Browse the repository at this point in the history
* some progress on lie algebra serialization

* more progress

* serialization for SRow

* more

* fix for general lie algebras

* direct sum lie algebras working

* remove whitespace

* Fix for empty direct sum

* Make LieAlgebraElem work

* Some optimizations

* Add serialization tests

* Add serialization of attributes

* Add macro for importing serialization functions

* Add reference to issue

* foo

* Add serialization of root systems

* Add serialization of weyl groups

* Connect Lie algebra and root system serialization

* Make attribute serialization work for Lie algebras

* Add artifact serialization test

---------

Co-authored-by: Lars Göttgens <[email protected]>
  • Loading branch information
antonydellavecchia and lgoettgens authored Aug 22, 2024
1 parent 61bf7dc commit f7601d3
Show file tree
Hide file tree
Showing 5 changed files with 368 additions and 0 deletions.
4 changes: 4 additions & 0 deletions experimental/LieAlgebras/src/LieAlgebras.jl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ import ..Oscar:
,

Oscar.@import_all_serialization_functions

import Base: getindex, deepcopy_internal, hash, issubset, iszero, parent, zero

export AbstractLieAlgebra, AbstractLieAlgebraElem
Expand Down Expand Up @@ -228,6 +230,8 @@ include("iso_oscar_gap.jl")
include("iso_gap_oscar.jl")
include("GapWrapper.jl")

include("serialization.jl")

end # module LieAlgebras

using .LieAlgebras
Expand Down
237 changes: 237 additions & 0 deletions experimental/LieAlgebras/src/serialization.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
###############################################################################
#
# Lie algebras
#
###############################################################################

const lie_algebra_serialization_attributes = [
:is_abelian, :is_nilpotent, :is_perfect, :is_simple, :is_solvable
]

@register_serialization_type AbstractLieAlgebra uses_id lie_algebra_serialization_attributes

function save_object(s::SerializerState, L::AbstractLieAlgebra)
save_data_dict(s) do
save_typed_object(s, coefficient_ring(L), :base_ring)
save_object(s, _struct_consts(L), :struct_consts)
save_object(s, symbols(L), :symbols)
save_root_system_data(s, L)
save_attrs(s, L)
end
end

function load_object(s::DeserializerState, ::Type{<:AbstractLieAlgebra})
R = load_typed_object(s, :base_ring)
struct_consts = load_object(s, Matrix, (sparse_row_type(R), R), :struct_consts)
symbs = load_object(s, Vector, Symbol, :symbols)
L = lie_algebra(R, struct_consts, symbs; check=false)
load_root_system_data(s, L)
load_attrs(s, L)
return L
end

@register_serialization_type LinearLieAlgebra uses_id [
lie_algebra_serialization_attributes;
[:type, :form]
]

function save_object(s::SerializerState, L::LinearLieAlgebra)
save_data_dict(s) do
save_typed_object(s, coefficient_ring(L), :base_ring)
save_object(s, L.n, :n)
save_object(s, matrix_repr_basis(L), :basis)
save_object(s, symbols(L), :symbols)
save_root_system_data(s, L)
save_attrs(s, L)
end
end

function load_object(s::DeserializerState, ::Type{<:LinearLieAlgebra})
R = load_typed_object(s, :base_ring)
n = load_object(s, Int, :n)
basis = load_object(s, Vector, (dense_matrix_type(R), matrix_space(R, n, n)), :basis)
symbs = load_object(s, Vector, Symbol, :symbols)
L = lie_algebra(R, n, basis, symbs; check=false)
load_root_system_data(s, L)
load_attrs(s, L)
return L
end

@register_serialization_type DirectSumLieAlgebra uses_id lie_algebra_serialization_attributes

function save_object(s::SerializerState, L::DirectSumLieAlgebra)
save_data_dict(s) do
save_typed_object(s, coefficient_ring(L), :base_ring)
save_data_array(s, :summands) do
for summand in L.summands
ref = save_as_ref(s, summand)
save_object(s, ref)
end
end
save_root_system_data(s, L)
save_attrs(s, L)
end
end

function load_object(s::DeserializerState, ::Type{<:DirectSumLieAlgebra})
R = load_typed_object(s, :base_ring)
summands = Vector{LieAlgebra{elem_type(R)}}(
load_array_node(s, :summands) do _
load_ref(s)
end,
)

L = direct_sum(R, summands)
load_root_system_data(s, L)
load_attrs(s, L)
return L
end

function save_root_system_data(s::SerializerState, L::LieAlgebra)
if has_root_system(L)
save_typed_object(s, root_system(L), :root_system)
save_object(s, chevalley_basis(L), :chevalley_basis)
end
end

function load_root_system_data(s::DeserializerState, L::LieAlgebra)
if haskey(s, :root_system)
@assert L isa AbstractLieAlgebra # TODO: adapt once we have a proper interface for this
L.root_system = load_typed_object(s, :root_system)
chevalley_basis = load_object(
s, Tuple, [(Vector, (AbstractLieAlgebraElem, L)) for _ in 1:3], :chevalley_basis
)
# chevalley basis will become an attribute in the near future
end
end

@register_serialization_type AbstractLieAlgebraElem uses_params
@register_serialization_type LinearLieAlgebraElem uses_params
@register_serialization_type DirectSumLieAlgebraElem uses_params

function save_object(s::SerializerState, x::LieAlgebraElem)
save_object(s, coefficients(x))
end

function load_object(s::DeserializerState, ::Type{<:LieAlgebraElem}, L::LieAlgebra)
return L(load_object(s, Vector, coefficient_ring(L)))
end

function save_type_params(s::SerializerState, x::LieAlgebraElem)
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{<:LieAlgebraElem})
return load_typed_object(s)
end

###############################################################################
#
# Root systems
#
###############################################################################

@register_serialization_type RootSystem uses_id

function save_object(s::SerializerState, R::RootSystem)
save_data_dict(s) do
save_object(s, cartan_matrix(R), :cartan_matrix)
if has_root_system_type(R)
type, type_ordering = root_system_type_with_ordering(R)
save_object(s, type, :type)
if type_ordering != 1:length(type_ordering) # don't save if it's the default
save_object(s, type_ordering, :type_ordering)
end
end
end
end

function load_object(s::DeserializerState, ::Type{RootSystem})
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
if haskey(s, :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)
end
end
return R
end

@register_serialization_type RootSpaceElem uses_params
@register_serialization_type DualRootSpaceElem uses_params

function save_object(s::SerializerState, r::Union{RootSpaceElem,DualRootSpaceElem})
save_object(s, _vec(coefficients(r)))
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
end

function load_type_params(
s::DeserializerState, ::Type{<:Union{RootSpaceElem,DualRootSpaceElem}}
)
return load_typed_object(s)
end

###############################################################################
#
# Weyl groups
#
###############################################################################

@register_serialization_type WeylGroup uses_id

function save_object(s::SerializerState, W::WeylGroup)
save_data_dict(s) do
save_typed_object(s, root_system(W), :root_system)
end
end

function load_object(s::DeserializerState, ::Type{WeylGroup})
R = load_typed_object(s, :root_system)
return weyl_group(R)
end

@register_serialization_type WeylGroupElem uses_params

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)
end
24 changes: 24 additions & 0 deletions experimental/LieAlgebras/test/RootSystem-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,30 @@
1:n_roots(R),
)
end

@testset "Serialization" begin
mktempdir() do path
test_save_load_roundtrip(path, R) do loaded
# nothing, cause `R === loaded` anyway
end

if n_roots(R) >= 1
r = positive_root(R, n_positive_roots(R))
test_save_load_roundtrip(path, r) do loaded
@test root_system(loaded) === R
@test coefficients(loaded) == coefficients(r)
end
end

test_save_load_roundtrip(path, positive_roots(R)) do loaded
@test length(loaded) == n_positive_roots(R)
@test all(
coefficients(loaded[i]) == coefficients(root(R, i)) for
i in 1:n_positive_roots(R)
)
end
end
end
end

@testset "rk 0" begin
Expand Down
58 changes: 58 additions & 0 deletions experimental/LieAlgebras/test/WeylGroup-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -431,4 +431,62 @@ include(
@test allunique(orb)
end
end

@testset "Serialization" begin
mktempdir() do path
@testset "simple saving and loading" begin
W = weyl_group((:A, 2), (:B, 4))

test_save_load_roundtrip(path, W) do loaded
# nothing, cause `W === loaded` anyway
end

x = rand(W)
test_save_load_roundtrip(path, x) do loaded
@test parent(loaded) === W
@test word(loaded) == word(x)
end

test_save_load_roundtrip(path, gens(W)) do loaded
@test length(loaded) == ngens(W)
@test all(
word(loaded[i]) == word(gen(W, i)) for i in 1:ngens(W)
)
end
end

@testset "cyclic reference between R and W survives" begin
Oscar.reset_global_serializer_state()

R_filename = joinpath(path, "R.mrdi")
W_filename = joinpath(path, "W.mrdi")

R = root_system(:D, 5)
W = weyl_group(R)

save(R_filename, R)
save(W_filename, W)

Oscar.reset_global_serializer_state()

loaded_R = load(R_filename)
loaded_W = load(W_filename)

@test loaded_R === root_system(loaded_W)
@test loaded_W === weyl_group(loaded_R)

loaded_R = loaded_W = nothing # unset all references

Oscar.reset_global_serializer_state()

loaded_W = load(W_filename)
loaded_R = load(R_filename)

@test loaded_R === root_system(loaded_W)
@test loaded_W === weyl_group(loaded_R)

loaded_R = loaded_W = nothing # unset all references
end
end
end
end
Loading

0 comments on commit f7601d3

Please sign in to comment.