add construction and tests

This commit is contained in:
thinh-le 2022-09-15 15:46:57 +02:00
parent 2e544d623f
commit 765f9841cf
3 changed files with 190 additions and 0 deletions

View File

@ -0,0 +1,172 @@
# Code by Marek Kaluba
# using OrderedCollections
mutable struct Subgroup{Gr, GrE} <: GroupsCore.Group
supergroup::Gr
gens::Vector{GrE}
order::Int
elts::OrderedSet{GrE}
function Subgroup(G::Group, gens::AbstractVector{<:GroupElement};
full_set=false)
Gr = typeof(G)
GrE = eltype(gens)
H = new{Gr, GrE}(G, gens)
if full_set
@assert one(G) gens throw(ArgumentError("Full set of elements defining subgroup does not contain the identity."))
H.order = length(gens)
H.elts = OrderedSet(gens)
end
return H
end
end
Base.parent(sg::Subgroup) = sg.supergroup
struct SubgroupElement{GrE, Gr<:Subgroup} <: GroupsCore.GroupElement
elt::GrE
parent::Gr
end
Base.one(sg::Subgroup) = SubgroupElement(one(parent(sg)), sg)
Base.eltype(::Type{<:Subgroup{Gr, GrE}}) where {Gr, GrE} =
SubgroupElement{GrE, Subgroup{Gr, GrE}}
# may be false for finite subgroups of infinite groups
function Base.IteratorSize(::Type{<:Subgroup{Gr}}) where Gr
PIS = Base.IteratorSize(Gr)
if PIS in (Base.IsInfinite(), Base.SizeUnknown())
return PIS
else
return Base.HasLength()
end
end
###### iteration and order
mutable struct GroupIter{S, T, GEl}
seen::S
seen_iter_state::T
current::GEl
gen_idx::Int
tmp::GEl
end
function _iterate(G::Subgroup)
seen = OrderedSet([one(G)])
current, seen_state = iterate(seen)
gr_iter = GroupIter(seen, seen_state, current, 1, one(G))
return one(G), gr_iter
end
function _next_elt!(itr::GroupIter)
# iterate over itr.seen, updating itr.seen_iter_state
# we're advancing to the next element, so
# * set itr.current and
# * reset gen_idx to 1
res = iterate(itr.seen, itr.seen_iter_state)
res == nothing && return true
# returned true terminates the outer iteration
itr.current = first(res)
itr.seen_iter_state = last(res)
itr.gen_idx = 1
return false
end
function _iterate(G::Subgroup, state)
# we generate new elements of G as elt*s^±1, where s is a generator;
# gen_idx ranges from 1 to 2ngens(G), even idx denote inverses
if state.gen_idx > 2ngens(G) # we've finished the generators, so advance current to the next element
finished = _next_elt!(state)
finished && return nothing
end
elt = let s = gens(G, (state.gen_idx + 1) ÷ 2)
# multiply current by s (or its inverse) on the right
state.tmp = GroupsCore.mul!(state.tmp, state.current, (isodd(state.gen_idx) ? s : inv(s)))
end
state.gen_idx += 1
if elt in state.seen
iterate(G, state)
else
push!(state.seen, deepcopy(elt))
return deepcopy(elt), state
end
end
function GroupsCore.order(::Type{T}, sg::Subgroup) where T
if !isdefined(sg, :order)
#morally: T(length(collect(sg)))
g, gr_iter = _iterate(sg)
while true
k = _iterate(sg, gr_iter)
isnothing(k) && break
end
sg.order = length(gr_iter.seen)
sg.elts = gt_iter.seen
end
return convert(T, sg.order)
end
function Base.iterate(G::Subgroup)
if !isdefined(G, :order)
k = order(Int, G)
end
@assert isdefined(G, :elts)
g, state = iterate(G.elts)
return SubgroupElement(g, G), state
end
function Base.iterate(G::Subgroup, state)
k = iterate(G.elts, state)
isnothing(k) && return nothing
g, state = k
return SubgroupElement(g, G), state
end
###### iteration and order
GroupsCore.gens(sg::Subgroup) = [SubgroupElement(g, sg) for g in sg.gens]
GroupsCore.gens(sg::Subgroup, i::Integer) = SubgroupElement(sg.gens[i], sg)
function Base.rand(
rng::Random.AbstractRNG,
rs::Random.SamplerTrivial{<:Subgroup},
)
sg = rs[]
S = sg.gens
@warn "FIXME: distribution of random element is not guaranteed to be uniform"
return SubgroupElement(prod(rand(S, 10*length(S))), sg)
end
Base.parent(g::SubgroupElement) = g.parent
Base.:(==)(g::SubgroupElement, h::SubgroupElement) =
(parent(g) === parent(h)) && g.elt == h.elt
Base.hash(g::SubgroupElement, h::UInt) = hash(g.elt, hash(parent(g), h))
function Base.deepcopy_internal(g::SubgroupElement, stackdict::IdDict)
haskey(stackdict, g) && return stackdict[g]
return SubgroupElement(Base.deepcopy_internal(g.elt, stackdict), parent(g))
end
# TODO:
Base.copy(g::SubgroupElement) = deepcopy(g)
Base.inv(g::SubgroupElement) = SubgroupElement(inv(g.elt), parent(g))
function Base.:(*)(g::SubgroupElement, h::SubgroupElement)
@assert parent(g) === parent(h)
return SubgroupElement(g.elt * h.elt, parent(g))
end
Base.show(io::IO, sg::Subgroup) =
print(io, "subgroup of $(parent(sg)) defined by $(ngens(sg)) generators.")
Base.show(io::IO, g::SubgroupElement) = show(io, g.elt)

View File

@ -2,9 +2,11 @@ module Constructions
using GroupsCore
import GroupsCore.Random
import OrderedCollections: OrderedSet
include("direct_product.jl")
include("direct_power.jl")
include("wreath_product.jl")
include("abstract_subgroup.jl")
end # of module Constructions

View File

@ -40,4 +40,20 @@
@test contains(sprint(print, W), "Wreath product")
@test sprint(print, rand(W)) isa String
end
@testset "AbstractSubgroup" begin
G = PermutationGroups.SymmetricGroup(3)
@test parent(G(perm"(1,2)")) == G # wrong coerce
@test parent(G([2,1,3])) == G
H = Groups.Constructions.Subgroup(G, [G([2,1,3])])
test_Group_interface(H)
test_GroupElement_interface(rand(H, 2)...)
@test order(H) == 2
K = Groups.Constructions.Subgroup(G, rand(G,1))
@test order(K) <= order(G)
# collect(K)
# ERROR: AssertionError: isdefined(G, :elts)
L = Groups.Constructions.Subgroup(G, [one(G), G([2,1,3])]; full_set=true)
@test L == H
end
end