diff --git a/docs/src/Groups/group_characters.md b/docs/src/Groups/group_characters.md index 1f7f0349ef55..9e94e965fc11 100644 --- a/docs/src/Groups/group_characters.md +++ b/docs/src/Groups/group_characters.md @@ -206,6 +206,7 @@ class_multiplication_coefficient known_class_fusion order(tbl::GAPGroupCharacterTable) possible_class_fusions +approximate_class_fusion ``` ## Character tables and normal subgroups diff --git a/docs/src/NumberTheory/abelian_closure.md b/docs/src/NumberTheory/abelian_closure.md index 93b135d8792c..4b0ce54c8c4f 100644 --- a/docs/src/NumberTheory/abelian_closure.md +++ b/docs/src/NumberTheory/abelian_closure.md @@ -21,6 +21,8 @@ Given the abelian closure, the generator can be recovered as follows: ```@docs gen(::QQAbField) +atlas_irrationality +atlas_description ``` ## Printing diff --git a/src/GAP/wrappers.jl b/src/GAP/wrappers.jl index f0b05e1b8ffe..cb618551c3b7 100644 --- a/src/GAP/wrappers.jl +++ b/src/GAP/wrappers.jl @@ -92,6 +92,7 @@ GAP.@wrap Image(x::Any, y::Any)::GapObj GAP.@wrap Image(x::Any)::GapObj GAP.@wrap ImmutableMatrix(x::GapObj, y::GapObj, z::Bool)::GapObj GAP.@wrap IndependentGeneratorExponents(x::Any, y::Any)::GapObj +GAP.@wrap InitFusion(x::GapObj, y::GapObj)::GapObj GAP.@wrap Indeterminate(x::GapObj)::GapObj GAP.@wrap Indeterminate(x::GapObj, y::GAP.Obj)::GapObj GAP.@wrap IndeterminateNumberOfUnivariateRationalFunction(x::GapObj)::Int diff --git a/src/Groups/group_characters.jl b/src/Groups/group_characters.jl index 2f3f77695c62..d3d323268b89 100644 --- a/src/Groups/group_characters.jl +++ b/src/Groups/group_characters.jl @@ -19,7 +19,7 @@ ## ## Atlas irrationalities ## -""" +@doc raw""" atlas_irrationality([F::AnticNumberField, ]description::String) Return the value encoded by `description`. @@ -30,6 +30,7 @@ if `F` is not given then the result has type `QQAbElem`. `description` is assumed to have the format defined in [CCNPW85](@cite), Chapter 6, Section 10. +# Examples ```jldoctest julia> Oscar.with_unicode() do show(atlas_irrationality("r5")) @@ -65,6 +66,34 @@ function atlas_irrationality(description::String) end +@doc raw""" + atlas_description(val::QQAbElem) + +Return a string in the format defined in +[CCNPW85](@cite), Chapter 6, Section 10, +describing `val`. +Applying [`atlas_irrationality`](@ref) to the result yields `val`. + +# Examples +```jldoctest +julia> K, z = abelian_closure(QQ); + +julia> val = z(5) + z(5)^4; + +julia> str = Oscar.atlas_description(val) +"b5" + +julia> val == atlas_irrationality(str) +true +``` +""" +function atlas_description(val::QQAbElem) + iso = Oscar.iso_oscar_gap(parent(val)) + val = iso(val) + return string(GAP.Globals.CTblLib.StringOfAtlasIrrationality(val)) +end + + ############################################################################# ## ## character tables @@ -1264,10 +1293,50 @@ end class_multiplication_coefficient(tbl::GAPGroupCharacterTable, i::Int, j::Int, k::Int) = class_multiplication_coefficient(ZZRingElem, tbl, i, j, k) +@doc raw""" + approximate_class_fusion(subtbl::GAPGroupCharacterTable, + tbl::GAPGroupCharacterTable) + +Compute for each class of `subtbl` all those classes in `tbl` to which it can +fuse under an embedding of the group of `subtbl` into the group of `tbl`, +according to element orders and centralizer orders in the two tables. + +If no embedding is possible then return an empty vector. +Otherwise return a vector of length equal to the number of classes of +`subtbl`, such that the entry at position $i$ either an integer (if there is +a unique possible image class) or the vector of the positions of possible +image classes. + +# Examples +```jldoctest +julia> subtbl = character_table("A5"); tbl = character_table("A6"); + +julia> println(approximate_class_fusion(subtbl, tbl)) +Union{Int64, Vector{Int64}}[1, 2, [3, 4], [6, 7], [6, 7]] + +``` +""" +function approximate_class_fusion(subtbl::GAPGroupCharacterTable, + tbl::GAPGroupCharacterTable) + fus = GAPWrap.InitFusion(GAPTable(subtbl), GAPTable(tbl)) + res = Union{Int, Vector{Int}}[] + fus == GAP.Globals.fail && return res + for i in 1:length(fus) + if fus[i] isa Int + push!(res, fus[i]) + else + push!(res, Vector{Int}(fus[i])) + end + end + return res +end + + @doc raw""" possible_class_fusions(subtbl::GAPGroupCharacterTable, tbl::GAPGroupCharacterTable; - decompose::Bool = true) + decompose::Bool = true, + fusionmap::Vector = []) Return the array of possible class fusions from `subtbl` to `tbl`. Each entry is an array of positive integers, where the value at position `i` @@ -1281,6 +1350,9 @@ is not checked; this does not change the result, but in certain situations it is faster to omit this step. +If `fusionmap` is set to a vector of integers and integer vectors then +only those maps are returned that are compatible with the prescribed value. + # Examples ```jldoctest julia> possible_class_fusions(character_table("A5"), character_table("A6")) @@ -1293,9 +1365,14 @@ julia> possible_class_fusions(character_table("A5"), character_table("A6")) """ function possible_class_fusions(subtbl::GAPGroupCharacterTable, tbl::GAPGroupCharacterTable; - decompose::Bool = true) + decompose::Bool = true, + fusionmap::Vector = []) + cond = Dict{Symbol, Any}(:decompose => decompose) + if length(fusionmap) != 0 + cond[:fusionmap] = GapObj(fusionmap, recursive = true) + end fus = GAPWrap.PossibleClassFusions(GAPTable(subtbl), GAPTable(tbl), - GapObj(Dict(:decompose => decompose))) + GapObj(cond)) return [Vector{Int}(x::GapObj) for x in fus] end diff --git a/src/Groups/libraries/libraries.jl b/src/Groups/libraries/libraries.jl index ae66201c4f64..98be4046d2ca 100644 --- a/src/Groups/libraries/libraries.jl +++ b/src/Groups/libraries/libraries.jl @@ -26,15 +26,15 @@ end function __init_group_libraries() props = [ is_abelian => GAP.Globals.IsAbelian, - is_almostsimple => GAP.Globals.IsAlmostSimpleGroup, + is_almostsimple => GAP.Globals.IsAlmostSimple, is_cyclic => GAP.Globals.IsCyclic, - is_nilpotent => GAP.Globals.IsNilpotentGroup, - is_perfect => GAP.Globals.IsPerfectGroup, + is_nilpotent => GAP.Globals.IsNilpotent, + is_perfect => GAP.Globals.IsPerfect, is_quasisimple => GAP.Globals.IsQuasisimple, - is_simple => GAP.Globals.IsSimpleGroup, + is_simple => GAP.Globals.IsSimple, is_sporadic_simple => GAP.Globals.IsSporadicSimple, - is_solvable => GAP.Globals.IsSolvableGroup, - is_supersolvable => GAP.Globals.IsSupersolvableGroup, + is_solvable => GAP.Globals.IsSolvable, + is_supersolvable => GAP.Globals.IsSupersolvable, ] empty!(_group_filter_attrs) diff --git a/src/exports.jl b/src/exports.jl index 5f0af46c1b87..129bb9e6a22a 100644 --- a/src/exports.jl +++ b/src/exports.jl @@ -241,12 +241,14 @@ export anti_symmetric_parts export anticanonical_bundle export anticanonical_divisor export anticanonical_divisor_class +export approximate_class_fusion export archimedean_solid export as_dictionary export as_gset export as_perm_group export as_polycyclic_group export associahedron +export atlas_description export atlas_group export atlas_irrationality export atlas_program diff --git a/test/Groups/group_characters.jl b/test/Groups/group_characters.jl index 9a63e2e62c2c..4973ed130851 100644 --- a/test/Groups/group_characters.jl +++ b/test/Groups/group_characters.jl @@ -956,6 +956,10 @@ end fus1 = possible_class_fusions(subtbl, tbl) fus2 = possible_class_fusions(subtbl, tbl, decompose = false) @test fus1 == fus2 + fus3 = possible_class_fusions(subtbl, tbl, fusionmap = fus1[1]) + @test length(fus3) == 1 && fus3[1] == fus1[1] + @test approximate_class_fusion(subtbl, tbl) == [1, 2, [3, 4], [6, 7], [6, 7]] + @test approximate_class_fusion(tbl, subtbl) == [] end @testset "normal subgroups" begin diff --git a/test/Groups/libraries.jl b/test/Groups/libraries.jl index 804c8bd42f3d..a8040184b337 100644 --- a/test/Groups/libraries.jl +++ b/test/Groups/libraries.jl @@ -29,7 +29,9 @@ is_cyclic, is_nilpotent, is_perfect, + is_quasisimple, is_simple, + is_sporadic_simple, is_solvable, is_supersolvable, is_transitive, diff --git a/test/Rings/AbelianClosure.jl b/test/Rings/AbelianClosure.jl index 8fce53d72386..d40d5ec3a562 100644 --- a/test/Rings/AbelianClosure.jl +++ b/test/Rings/AbelianClosure.jl @@ -366,4 +366,15 @@ end @test F isa AnticNumberField @test dim(F) == 8 end + + @testset "Atlas irrationalities" begin + K, z = abelian_closure(QQ) + vals = [z(7) + z(7)^2 + z(7)^4, + z(8) + z(8)^3, + z(9) + z(9)^-1, + z(3) - z(3)^2] + for val in vals + @test atlas_irrationality(atlas_description(val)) == val + end + end end