mirror of
https://github.com/kalmarek/Groups.jl.git
synced 2024-11-28 00:40:28 +01:00
Merge pull request #31 from kalmarek/mk/update_to_PG_0.6
Update to PermutationGroups-0.6
This commit is contained in:
commit
1fbd7b875b
@ -1,7 +1,7 @@
|
|||||||
name = "Groups"
|
name = "Groups"
|
||||||
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
||||||
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
||||||
version = "0.7.8"
|
version = "0.8"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"
|
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"
|
||||||
@ -14,10 +14,10 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
|||||||
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
|
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
|
||||||
|
|
||||||
[compat]
|
[compat]
|
||||||
GroupsCore = "0.4"
|
GroupsCore = "0.5"
|
||||||
KnuthBendix = "0.4"
|
KnuthBendix = "0.4"
|
||||||
OrderedCollections = "1"
|
OrderedCollections = "1"
|
||||||
PermutationGroups = "0.4"
|
PermutationGroups = "0.6"
|
||||||
StaticArrays = "1"
|
StaticArrays = "1"
|
||||||
julia = "1.6"
|
julia = "1.6"
|
||||||
|
|
||||||
|
@ -27,6 +27,7 @@ include(joinpath("constructions", "constructions.jl"))
|
|||||||
import .Constructions
|
import .Constructions
|
||||||
|
|
||||||
include("types.jl")
|
include("types.jl")
|
||||||
|
include("rand.jl")
|
||||||
include("hashing.jl")
|
include("hashing.jl")
|
||||||
include("normalform.jl")
|
include("normalform.jl")
|
||||||
include("autgroups.jl")
|
include("autgroups.jl")
|
||||||
|
@ -36,8 +36,8 @@ function Base.:(==)(
|
|||||||
hash(g) != hash(h) && return false
|
hash(g) != hash(h) && return false
|
||||||
end
|
end
|
||||||
|
|
||||||
length(word(g)) > 8 && normalform!(g)
|
normalform!(g)
|
||||||
length(word(h)) > 8 && normalform!(h)
|
normalform!(h)
|
||||||
|
|
||||||
word(g) == word(h) && return true
|
word(g) == word(h) && return true
|
||||||
|
|
||||||
@ -138,14 +138,14 @@ end
|
|||||||
|
|
||||||
# forward evaluate by substitution
|
# forward evaluate by substitution
|
||||||
|
|
||||||
struct LettersMap{T,A}
|
struct LettersMap{W<:AbstractWord,A}
|
||||||
indices_map::Dict{Int,T}
|
indices_map::Dict{Int,W}
|
||||||
A::A
|
A::A
|
||||||
end
|
end
|
||||||
|
|
||||||
function LettersMap(a::FPGroupElement{<:AutomorphismGroup})
|
function LettersMap(a::FPGroupElement{<:AutomorphismGroup})
|
||||||
dom = domain(a)
|
dom = domain(a)
|
||||||
@assert all(isone ∘ length ∘ word, dom)
|
if all(isone ∘ length ∘ word, dom)
|
||||||
A = alphabet(first(dom))
|
A = alphabet(first(dom))
|
||||||
first_letters = first.(word.(dom))
|
first_letters = first.(word.(dom))
|
||||||
img = evaluate!(dom, a)
|
img = evaluate!(dom, a)
|
||||||
@ -157,24 +157,28 @@ function LettersMap(a::FPGroupElement{<:AutomorphismGroup})
|
|||||||
@assert length(dom) == length(img)
|
@assert length(dom) == length(img)
|
||||||
|
|
||||||
indices_map =
|
indices_map =
|
||||||
Dict(A[A[fl]] => word(im) for (fl, im) in zip(first_letters, img))
|
Dict(Int(fl) => word(im) for (fl, im) in zip(first_letters, img))
|
||||||
# inverses of generators are dealt lazily in getindex
|
# inverses of generators are dealt lazily in getindex
|
||||||
|
else
|
||||||
|
throw("LettersMap is not implemented for non-generators in domain")
|
||||||
|
end
|
||||||
|
|
||||||
return LettersMap(indices_map, A)
|
return LettersMap(indices_map, A)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.getindex(lm::LettersMap, i::Integer)
|
function Base.getindex(lm::LettersMap{W}, i::Integer) where {W}
|
||||||
# here i is an index of an alphabet
|
# here i is an index of an alphabet
|
||||||
@boundscheck 1 ≤ i ≤ length(lm.A)
|
@boundscheck 1 ≤ i ≤ length(lm.A)
|
||||||
|
|
||||||
if !haskey(lm.indices_map, i)
|
if !haskey(lm.indices_map, i)
|
||||||
img = if haskey(lm.indices_map, inv(i, lm.A))
|
I = inv(i, lm.A)
|
||||||
inv(lm.indices_map[inv(i, lm.A)], lm.A)
|
if haskey(lm.indices_map, I)
|
||||||
else
|
img = inv(lm.indices_map[I], lm.A)
|
||||||
@warn "LetterMap: neither $i nor its inverse has assigned value"
|
|
||||||
one(valtype(lm.indices_map))
|
|
||||||
end
|
|
||||||
lm.indices_map[i] = img
|
lm.indices_map[i] = img
|
||||||
|
else
|
||||||
|
lm.indices_map[i] = W([i])
|
||||||
|
lm.indices_map[I] = W([I])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
return lm.indices_map[i]
|
return lm.indices_map[i]
|
||||||
end
|
end
|
||||||
@ -185,9 +189,10 @@ function (a::FPGroupElement{<:AutomorphismGroup})(g::FPGroupElement)
|
|||||||
return parent(g)(img_w)
|
return parent(g)(img_w)
|
||||||
end
|
end
|
||||||
|
|
||||||
evaluate(w::AbstractWord, lm::LettersMap) = evaluate!(one(w), w, lm)
|
evaluate(w::AbstractWord, lm::LettersMap) = evaluate!(similar(w), w, lm)
|
||||||
|
|
||||||
function evaluate!(res::AbstractWord, w::AbstractWord, lm::LettersMap)
|
function evaluate!(res::AbstractWord, w::AbstractWord, lm::LettersMap)
|
||||||
|
resize!(res, 0)
|
||||||
for i in w
|
for i in w
|
||||||
append!(res, lm[i])
|
append!(res, lm[i])
|
||||||
end
|
end
|
||||||
|
@ -59,15 +59,6 @@ end
|
|||||||
|
|
||||||
GroupsCore.ngens(G::DirectPower) = _nfold(G) * ngens(G.group)
|
GroupsCore.ngens(G::DirectPower) = _nfold(G) * ngens(G.group)
|
||||||
|
|
||||||
function GroupsCore.gens(G::DirectPower, i::Integer)
|
|
||||||
k = ngens(G.group)
|
|
||||||
ci = CartesianIndices((k, _nfold(G)))
|
|
||||||
@boundscheck checkbounds(ci, i)
|
|
||||||
r, c = Tuple(ci[i])
|
|
||||||
tup = ntuple(j -> j == c ? gens(G.group, r) : one(G.group), _nfold(G))
|
|
||||||
return DirectPowerElement(tup, G)
|
|
||||||
end
|
|
||||||
|
|
||||||
function GroupsCore.gens(G::DirectPower)
|
function GroupsCore.gens(G::DirectPower)
|
||||||
N = _nfold(G)
|
N = _nfold(G)
|
||||||
S = gens(G.group)
|
S = gens(G.group)
|
||||||
@ -78,14 +69,6 @@ end
|
|||||||
|
|
||||||
Base.isfinite(G::DirectPower) = isfinite(G.group)
|
Base.isfinite(G::DirectPower) = isfinite(G.group)
|
||||||
|
|
||||||
function Base.rand(
|
|
||||||
rng::Random.AbstractRNG,
|
|
||||||
rs::Random.SamplerTrivial{<:DirectPower},
|
|
||||||
)
|
|
||||||
G = rs[]
|
|
||||||
return DirectPowerElement(rand(rng, G.group, _nfold(G)), G)
|
|
||||||
end
|
|
||||||
|
|
||||||
GroupsCore.parent(g::DirectPowerElement) = g.parent
|
GroupsCore.parent(g::DirectPowerElement) = g.parent
|
||||||
|
|
||||||
function Base.:(==)(g::DirectPowerElement, h::DirectPowerElement)
|
function Base.:(==)(g::DirectPowerElement, h::DirectPowerElement)
|
||||||
@ -94,13 +77,6 @@ end
|
|||||||
|
|
||||||
Base.hash(g::DirectPowerElement, h::UInt) = hash(g.elts, hash(parent(g), h))
|
Base.hash(g::DirectPowerElement, h::UInt) = hash(g.elts, hash(parent(g), h))
|
||||||
|
|
||||||
function Base.deepcopy_internal(g::DirectPowerElement, stackdict::IdDict)
|
|
||||||
return DirectPowerElement(
|
|
||||||
Base.deepcopy_internal(g.elts, stackdict),
|
|
||||||
parent(g),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
Base.inv(g::DirectPowerElement) = DirectPowerElement(inv.(g.elts), parent(g))
|
Base.inv(g::DirectPowerElement) = DirectPowerElement(inv.(g.elts), parent(g))
|
||||||
|
|
||||||
function Base.:(*)(g::DirectPowerElement, h::DirectPowerElement)
|
function Base.:(*)(g::DirectPowerElement, h::DirectPowerElement)
|
||||||
@ -108,6 +84,39 @@ function Base.:(*)(g::DirectPowerElement, h::DirectPowerElement)
|
|||||||
return DirectPowerElement(g.elts .* h.elts, parent(g))
|
return DirectPowerElement(g.elts .* h.elts, parent(g))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# to make sure that parents are never copied i.e.
|
||||||
|
# g and deepcopy(g) share their parent
|
||||||
|
Base.deepcopy_internal(G::DirectPower, ::IdDict) = G
|
||||||
|
|
||||||
|
################## Implementing Group Interface Done!
|
||||||
|
|
||||||
|
function GroupsCore.gens(G::DirectPower, i::Integer)
|
||||||
|
k = ngens(G.group)
|
||||||
|
ci = CartesianIndices((k, _nfold(G)))
|
||||||
|
@boundscheck checkbounds(ci, i)
|
||||||
|
r, c = Tuple(ci[i])
|
||||||
|
tup = ntuple(j -> j == c ? gens(G.group, r) : one(G.group), _nfold(G))
|
||||||
|
return DirectPowerElement(tup, G)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Overloading rand: the PRA of GroupsCore is known for not performing
|
||||||
|
# well on direct sums
|
||||||
|
function Random.Sampler(
|
||||||
|
RNG::Type{<:Random.AbstractRNG},
|
||||||
|
G::DirectPower,
|
||||||
|
repetition::Random.Repetition = Val(Inf),
|
||||||
|
)
|
||||||
|
return Random.SamplerTrivial(G)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Base.rand(
|
||||||
|
rng::Random.AbstractRNG,
|
||||||
|
rs::Random.SamplerTrivial{<:DirectPower},
|
||||||
|
)
|
||||||
|
G = rs[]
|
||||||
|
return DirectPowerElement(rand(rng, G.group, _nfold(G)), G)
|
||||||
|
end
|
||||||
|
|
||||||
function GroupsCore.order(::Type{I}, g::DirectPowerElement) where {I<:Integer}
|
function GroupsCore.order(::Type{I}, g::DirectPowerElement) where {I<:Integer}
|
||||||
return convert(I, reduce(lcm, (order(I, h) for h in g.elts); init = one(I)))
|
return convert(I, reduce(lcm, (order(I, h) for h in g.elts); init = one(I)))
|
||||||
end
|
end
|
||||||
@ -117,10 +126,13 @@ Base.isone(g::DirectPowerElement) = all(isone, g.elts)
|
|||||||
function Base.show(io::IO, G::DirectPower)
|
function Base.show(io::IO, G::DirectPower)
|
||||||
n = _nfold(G)
|
n = _nfold(G)
|
||||||
nn = n == 1 ? "1-st" : n == 2 ? "2-nd" : n == 3 ? "3-rd" : "$n-th"
|
nn = n == 1 ? "1-st" : n == 2 ? "2-nd" : n == 3 ? "3-rd" : "$n-th"
|
||||||
return print(io, "Direct $(nn) power of $(G.group)")
|
return print(io, "Direct $(nn) power of ", G.group)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.show(io::IO, g::DirectPowerElement)
|
function Base.show(io::IO, g::DirectPowerElement)
|
||||||
return print(io, "( ", join(g.elts, ", "), " )")
|
print(io, "( ")
|
||||||
|
join(io, g.elts, ", ")
|
||||||
|
return print(" )")
|
||||||
end
|
end
|
||||||
|
|
||||||
# convienience:
|
# convienience:
|
||||||
|
@ -72,14 +72,6 @@ end
|
|||||||
|
|
||||||
Base.isfinite(G::DirectProduct) = isfinite(G.first) && isfinite(G.last)
|
Base.isfinite(G::DirectProduct) = isfinite(G.first) && isfinite(G.last)
|
||||||
|
|
||||||
function Base.rand(
|
|
||||||
rng::Random.AbstractRNG,
|
|
||||||
rs::Random.SamplerTrivial{<:DirectProduct},
|
|
||||||
)
|
|
||||||
G = rs[]
|
|
||||||
return DirectProductElement((rand(rng, G.first), rand(rng, G.last)), G)
|
|
||||||
end
|
|
||||||
|
|
||||||
GroupsCore.parent(g::DirectProductElement) = g.parent
|
GroupsCore.parent(g::DirectProductElement) = g.parent
|
||||||
|
|
||||||
function Base.:(==)(g::DirectProductElement, h::DirectProductElement)
|
function Base.:(==)(g::DirectProductElement, h::DirectProductElement)
|
||||||
@ -88,13 +80,6 @@ end
|
|||||||
|
|
||||||
Base.hash(g::DirectProductElement, h::UInt) = hash(g.elts, hash(parent(g), h))
|
Base.hash(g::DirectProductElement, h::UInt) = hash(g.elts, hash(parent(g), h))
|
||||||
|
|
||||||
function Base.deepcopy_internal(g::DirectProductElement, stackdict::IdDict)
|
|
||||||
return DirectProductElement(
|
|
||||||
Base.deepcopy_internal(g.elts, stackdict),
|
|
||||||
parent(g),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Base.inv(g::DirectProductElement)
|
function Base.inv(g::DirectProductElement)
|
||||||
return DirectProductElement(inv.(g.elts), parent(g))
|
return DirectProductElement(inv.(g.elts), parent(g))
|
||||||
end
|
end
|
||||||
@ -104,6 +89,30 @@ function Base.:(*)(g::DirectProductElement, h::DirectProductElement)
|
|||||||
return DirectProductElement(g.elts .* h.elts, parent(g))
|
return DirectProductElement(g.elts .* h.elts, parent(g))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# to make sure that parents are never copied i.e.
|
||||||
|
# g and deepcopy(g) share their parent
|
||||||
|
Base.deepcopy_internal(G::DirectProduct, ::IdDict) = G
|
||||||
|
|
||||||
|
################## Implementing Group Interface Done!
|
||||||
|
|
||||||
|
# Overloading rand: the PRA of GroupsCore is known for not performing
|
||||||
|
# well on direct sums
|
||||||
|
function Random.Sampler(
|
||||||
|
RNG::Type{<:Random.AbstractRNG},
|
||||||
|
G::DirectProduct,
|
||||||
|
repetition::Random.Repetition = Val(Inf),
|
||||||
|
)
|
||||||
|
return Random.SamplerTrivial(G)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Base.rand(
|
||||||
|
rng::Random.AbstractRNG,
|
||||||
|
rs::Random.SamplerTrivial{<:DirectProduct},
|
||||||
|
)
|
||||||
|
G = rs[]
|
||||||
|
return DirectProductElement((rand(rng, G.first), rand(rng, G.last)), G)
|
||||||
|
end
|
||||||
|
|
||||||
function GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer}
|
function GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer}
|
||||||
return convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts))))
|
return convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts))))
|
||||||
end
|
end
|
||||||
@ -111,10 +120,13 @@ end
|
|||||||
Base.isone(g::DirectProductElement) = all(isone, g.elts)
|
Base.isone(g::DirectProductElement) = all(isone, g.elts)
|
||||||
|
|
||||||
function Base.show(io::IO, G::DirectProduct)
|
function Base.show(io::IO, G::DirectProduct)
|
||||||
return print(io, "Direct product of $(G.first) and $(G.last)")
|
return print(io, "Direct product of ", G.first, " and ", G.last)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.show(io::IO, g::DirectProductElement)
|
function Base.show(io::IO, g::DirectProductElement)
|
||||||
return print(io, "( $(join(g.elts, ",")) )")
|
print(io, "( ")
|
||||||
|
join(io, g.elts, ", ")
|
||||||
|
return print(io, " )")
|
||||||
end
|
end
|
||||||
|
|
||||||
# convienience:
|
# convienience:
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
import PermutationGroups:
|
import PermutationGroups as PG
|
||||||
AbstractPermutationGroup,
|
|
||||||
AbstractPermutation,
|
|
||||||
degree
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
WreathProduct(G::Group, P::AbstractPermutationGroup) <: Group
|
WreathProduct(G::Group, P::AbstractPermutationGroup) <: Group
|
||||||
@ -16,20 +13,20 @@ product is defined as
|
|||||||
where `m^σ` denotes the action (from the right) of the permutation `σ` on
|
where `m^σ` denotes the action (from the right) of the permutation `σ` on
|
||||||
`d`-tuples of elements from `G`.
|
`d`-tuples of elements from `G`.
|
||||||
"""
|
"""
|
||||||
struct WreathProduct{DP<:DirectPower,PGr<:AbstractPermutationGroup} <:
|
struct WreathProduct{DP<:DirectPower,PGr<:PG.AbstractPermutationGroup} <:
|
||||||
GroupsCore.Group
|
GroupsCore.Group
|
||||||
N::DP
|
N::DP
|
||||||
P::PGr
|
P::PGr
|
||||||
|
|
||||||
function WreathProduct(G::Group, P::AbstractPermutationGroup)
|
function WreathProduct(G::Group, P::PG.AbstractPermutationGroup)
|
||||||
N = DirectPower{degree(P)}(G)
|
N = DirectPower{PG.AP.degree(P)}(G)
|
||||||
return new{typeof(N),typeof(P)}(N, P)
|
return new{typeof(N),typeof(P)}(N, P)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
struct WreathProductElement{
|
struct WreathProductElement{
|
||||||
DPEl<:DirectPowerElement,
|
DPEl<:DirectPowerElement,
|
||||||
PEl<:AbstractPermutation,
|
PEl<:PG.AP.AbstractPermutation,
|
||||||
Wr<:WreathProduct,
|
Wr<:WreathProduct,
|
||||||
} <: GroupsCore.GroupElement
|
} <: GroupsCore.GroupElement
|
||||||
n::DPEl
|
n::DPEl
|
||||||
@ -38,7 +35,7 @@ struct WreathProductElement{
|
|||||||
|
|
||||||
function WreathProductElement(
|
function WreathProductElement(
|
||||||
n::DirectPowerElement,
|
n::DirectPowerElement,
|
||||||
p::AbstractPermutation,
|
p::PG.AP.AbstractPermutation,
|
||||||
W::WreathProduct,
|
W::WreathProduct,
|
||||||
)
|
)
|
||||||
return new{typeof(n),typeof(p),typeof(W)}(n, p, W)
|
return new{typeof(n),typeof(p),typeof(W)}(n, p, W)
|
||||||
@ -97,14 +94,6 @@ end
|
|||||||
|
|
||||||
Base.isfinite(G::WreathProduct) = isfinite(G.N) && isfinite(G.P)
|
Base.isfinite(G::WreathProduct) = isfinite(G.N) && isfinite(G.P)
|
||||||
|
|
||||||
function Base.rand(
|
|
||||||
rng::Random.AbstractRNG,
|
|
||||||
rs::Random.SamplerTrivial{<:WreathProduct},
|
|
||||||
)
|
|
||||||
G = rs[]
|
|
||||||
return WreathProductElement(rand(rng, G.N), rand(rng, G.P), G)
|
|
||||||
end
|
|
||||||
|
|
||||||
GroupsCore.parent(g::WreathProductElement) = g.parent
|
GroupsCore.parent(g::WreathProductElement) = g.parent
|
||||||
|
|
||||||
function Base.:(==)(g::WreathProductElement, h::WreathProductElement)
|
function Base.:(==)(g::WreathProductElement, h::WreathProductElement)
|
||||||
@ -115,15 +104,7 @@ function Base.hash(g::WreathProductElement, h::UInt)
|
|||||||
return hash(g.n, hash(g.p, hash(g.parent, h)))
|
return hash(g.n, hash(g.p, hash(g.parent, h)))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict)
|
function _act(p::PG.AP.AbstractPermutation, n::DirectPowerElement)
|
||||||
return WreathProductElement(
|
|
||||||
Base.deepcopy_internal(g.n, stackdict),
|
|
||||||
Base.deepcopy_internal(g.p, stackdict),
|
|
||||||
parent(g),
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function _act(p::AbstractPermutation, n::DirectPowerElement)
|
|
||||||
return DirectPowerElement(
|
return DirectPowerElement(
|
||||||
ntuple(i -> n.elts[i^p], length(n.elts)),
|
ntuple(i -> n.elts[i^p], length(n.elts)),
|
||||||
parent(n),
|
parent(n),
|
||||||
@ -140,11 +121,36 @@ function Base.:(*)(g::WreathProductElement, h::WreathProductElement)
|
|||||||
return WreathProductElement(g.n * _act(g.p, h.n), g.p * h.p, parent(g))
|
return WreathProductElement(g.n * _act(g.p, h.n), g.p * h.p, parent(g))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# to make sure that parents are never copied i.e.
|
||||||
|
# g and deepcopy(g) share their parent
|
||||||
|
Base.deepcopy_internal(G::WreathProduct, ::IdDict) = G
|
||||||
|
|
||||||
|
################## Implementing Group Interface Done!
|
||||||
|
|
||||||
|
# Overloading rand: the PRA of GroupsCore is known for not performing
|
||||||
|
# well on direct sums
|
||||||
|
function Random.Sampler(
|
||||||
|
RNG::Type{<:Random.AbstractRNG},
|
||||||
|
G::WreathProduct,
|
||||||
|
repetition::Random.Repetition = Val(Inf),
|
||||||
|
)
|
||||||
|
return Random.SamplerTrivial(G)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Base.rand(
|
||||||
|
rng::Random.AbstractRNG,
|
||||||
|
rs::Random.SamplerTrivial{<:WreathProduct},
|
||||||
|
)
|
||||||
|
G = rs[]
|
||||||
|
return WreathProductElement(rand(rng, G.N), rand(rng, G.P), G)
|
||||||
|
end
|
||||||
|
|
||||||
Base.isone(g::WreathProductElement) = isone(g.n) && isone(g.p)
|
Base.isone(g::WreathProductElement) = isone(g.n) && isone(g.p)
|
||||||
|
|
||||||
function Base.show(io::IO, G::WreathProduct)
|
function Base.show(io::IO, G::WreathProduct)
|
||||||
return print(io, "Wreath product of $(G.N.group) by $(G.P)")
|
return print(io, "Wreath product of ", G.N.group, " by ", G.P)
|
||||||
end
|
end
|
||||||
Base.show(io::IO, g::WreathProductElement) = print(io, "( $(g.n)≀$(g.p) )")
|
|
||||||
|
|
||||||
Base.copy(g::WreathProductElement) = WreathProductElement(g.n, g.p, parent(g))
|
function Base.show(io::IO, g::WreathProductElement)
|
||||||
|
return print(io, "( ", g.n, "≀", g.p, " )")
|
||||||
|
end
|
||||||
|
@ -2,8 +2,10 @@
|
|||||||
normalform!(g::FPGroupElement)
|
normalform!(g::FPGroupElement)
|
||||||
Compute the normal form of `g`, possibly modifying `g` in-place.
|
Compute the normal form of `g`, possibly modifying `g` in-place.
|
||||||
"""
|
"""
|
||||||
@inline function normalform!(g::AbstractFPGroupElement)
|
@inline function normalform!(g::AbstractFPGroupElement; force = false)
|
||||||
|
if !force
|
||||||
isnormalform(g) && return g
|
isnormalform(g) && return g
|
||||||
|
end
|
||||||
|
|
||||||
let w = one(word(g))
|
let w = one(word(g))
|
||||||
w = normalform!(w, g)
|
w = normalform!(w, g)
|
||||||
@ -36,9 +38,9 @@ end
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
normalform!(res::AbstractWord, g::FPGroupElement)
|
normalform!(res::AbstractWord, g::FPGroupElement)
|
||||||
Append the normal form of `g` to word `res`, modifying `res` in place.
|
Write the normal form of `g` to word `res`, modifying `res` in place.
|
||||||
|
|
||||||
Defaults to the rewriting in the free group.
|
The particular implementation of the normal form depends on `parent(g)`.
|
||||||
"""
|
"""
|
||||||
@inline function normalform!(res::AbstractWord, g::AbstractFPGroupElement)
|
@inline function normalform!(res::AbstractWord, g::AbstractFPGroupElement)
|
||||||
isone(res) && isnormalform(g) && return append!(res, word(g))
|
isone(res) && isnormalform(g) && return append!(res, word(g))
|
||||||
|
62
src/rand.jl
Normal file
62
src/rand.jl
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
"""
|
||||||
|
PoissonSampler
|
||||||
|
For a finitely presented group PoissonSampler returns group elements represented
|
||||||
|
by words of length at most `R ~ Poisson(λ)` chosen uniformly at random.
|
||||||
|
|
||||||
|
For finitely presented groups the Product Replacement Algorithm
|
||||||
|
(see `PRASampler` from `GroupsCore.jl`) doesn't make much sense due to
|
||||||
|
overly long words it produces. We therefore resort to a pseudo-random method,
|
||||||
|
where a word `w` of length `R` is chosen uniformly at random among all
|
||||||
|
words of length `R` where `R` follows the Poisson distribution.
|
||||||
|
|
||||||
|
!!! note
|
||||||
|
Due to the choice of the parameters (`λ=8`) and the floating point
|
||||||
|
arithmetic the sampler will always return group elements represented by
|
||||||
|
words of length at most `42`.
|
||||||
|
"""
|
||||||
|
struct PoissonSampler{G,T} <: Random.Sampler{T}
|
||||||
|
group::G
|
||||||
|
λ::Int
|
||||||
|
end
|
||||||
|
|
||||||
|
function PoissonSampler(G::AbstractFPGroup; λ)
|
||||||
|
return PoissonSampler{typeof(G),eltype(G)}(G, λ)
|
||||||
|
end
|
||||||
|
|
||||||
|
function __poisson_invcdf(val; λ)
|
||||||
|
# __poisson_pdf(k, λ) = λ^k * ℯ^-λ / factorial(k)
|
||||||
|
# pdf = ntuple(k -> __poisson_pdf(k - 1, λ), 21)
|
||||||
|
# cdf = accumulate(+, pdf)
|
||||||
|
# radius = something(findfirst(>(val), cdf) - 1, 0)
|
||||||
|
# this is the iterative version:
|
||||||
|
pdf = ℯ^-λ
|
||||||
|
cdf = pdf
|
||||||
|
k = 0
|
||||||
|
while cdf < val
|
||||||
|
k += 1
|
||||||
|
pdf = pdf * λ / k
|
||||||
|
cdf += pdf
|
||||||
|
end
|
||||||
|
return k
|
||||||
|
end
|
||||||
|
|
||||||
|
function Random.rand(rng::Random.AbstractRNG, sampler::PoissonSampler)
|
||||||
|
R = __poisson_invcdf(rand(rng); λ = sampler.λ)
|
||||||
|
|
||||||
|
G = sampler.group
|
||||||
|
n = length(alphabet(G))
|
||||||
|
W = word_type(G)
|
||||||
|
T = eltype(W)
|
||||||
|
|
||||||
|
letters = rand(rng, T(1):T(n), R)
|
||||||
|
word = W(letters, false)
|
||||||
|
return G(word)
|
||||||
|
end
|
||||||
|
|
||||||
|
function Random.Sampler(
|
||||||
|
RNG::Type{<:Random.AbstractRNG},
|
||||||
|
G::AbstractFPGroup,
|
||||||
|
repetition::Random.Repetition = Val(Inf),
|
||||||
|
)
|
||||||
|
return PoissonSampler(G; λ = 8)
|
||||||
|
end
|
89
src/types.jl
89
src/types.jl
@ -67,17 +67,6 @@ function GroupsCore.gens(G::AbstractFPGroup)
|
|||||||
return [gens(G, i) for i in 1:GroupsCore.ngens(G)]
|
return [gens(G, i) for i in 1:GroupsCore.ngens(G)]
|
||||||
end
|
end
|
||||||
|
|
||||||
# TODO: ProductReplacementAlgorithm
|
|
||||||
function Base.rand(
|
|
||||||
rng::Random.AbstractRNG,
|
|
||||||
rs::Random.SamplerTrivial{<:AbstractFPGroup},
|
|
||||||
)
|
|
||||||
l = rand(10:100)
|
|
||||||
G = rs[]
|
|
||||||
nletters = length(alphabet(G))
|
|
||||||
return FPGroupElement(word_type(G)(rand(1:nletters, l)), G)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Base.isfinite(::AbstractFPGroup)
|
function Base.isfinite(::AbstractFPGroup)
|
||||||
return (
|
return (
|
||||||
@warn "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong"; false
|
@warn "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong"; false
|
||||||
@ -110,10 +99,6 @@ mutable struct FPGroupElement{Gr<:AbstractFPGroup,W<:AbstractWord} <:
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.show(io::IO, ::Type{<:FPGroupElement{Gr}}) where {Gr}
|
|
||||||
return print(io, FPGroupElement, "{$Gr, …}")
|
|
||||||
end
|
|
||||||
|
|
||||||
function Base.copy(f::FPGroupElement)
|
function Base.copy(f::FPGroupElement)
|
||||||
return FPGroupElement(copy(word(f)), parent(f), f.savedhash)
|
return FPGroupElement(copy(word(f)), parent(f), f.savedhash)
|
||||||
end
|
end
|
||||||
@ -134,18 +119,33 @@ function Base.:(==)(g::AbstractFPGroupElement, h::AbstractFPGroupElement)
|
|||||||
@boundscheck @assert parent(g) === parent(h)
|
@boundscheck @assert parent(g) === parent(h)
|
||||||
normalform!(g)
|
normalform!(g)
|
||||||
normalform!(h)
|
normalform!(h)
|
||||||
|
# I. compare hashes of the normalform
|
||||||
|
# II. compare some data associated to FPGroupElement,
|
||||||
|
# e.g. word, image of the domain etc.
|
||||||
hash(g) != hash(h) && return false
|
hash(g) != hash(h) && return false
|
||||||
return equality_data(g) == equality_data(h)
|
equality_data(g) == equality_data(h) && return true # compares
|
||||||
|
|
||||||
|
# if this failed it is still possible that the words together can be
|
||||||
|
# rewritten even further, so we
|
||||||
|
# 1. rewrite word(g⁻¹·h) w.r.t. rewriting(parent(g))
|
||||||
|
# 2. check if the result is empty
|
||||||
|
G = parent(g)
|
||||||
|
|
||||||
|
g⁻¹h = append!(inv(word(g), alphabet(G)), word(h))
|
||||||
|
# similar + empty preserve the storage size
|
||||||
|
# saves some re-allocations if res does not represent id
|
||||||
|
res = similar(word(g))
|
||||||
|
resize!(res, 0)
|
||||||
|
res = KnuthBendix.rewrite!(res, g⁻¹h, rewriting(G))
|
||||||
|
return isone(res)
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.deepcopy_internal(g::FPGroupElement, stackdict::IdDict)
|
function Base.deepcopy_internal(g::FPGroupElement, stackdict::IdDict)
|
||||||
haskey(stackdict, objectid(g)) && return stackdict[objectid(g)]
|
haskey(stackdict, g) && return stackdict[g]
|
||||||
cw = if haskey(stackdict, objectid(word(g)))
|
cw = Base.deepcopy_internal(word(g), stackdict)
|
||||||
stackdict[objectid(word(g))]
|
h = FPGroupElement(cw, parent(g), g.savedhash)
|
||||||
else
|
stackdict[g] = h
|
||||||
copy(word(g))
|
return h
|
||||||
end
|
|
||||||
return FPGroupElement(cw, parent(g), g.savedhash)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement}
|
function Base.inv(g::GEl) where {GEl<:AbstractFPGroupElement}
|
||||||
@ -192,9 +192,16 @@ struct FreeGroup{T,O} <: AbstractFPGroup
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
FreeGroup(gens, A::Alphabet) = FreeGroup(gens, KnuthBendix.LenLex(A))
|
function FreeGroup(n::Integer)
|
||||||
|
symbols =
|
||||||
|
collect(Iterators.flatten((Symbol(:f, i), Symbol(:F, i)) for i in 1:n))
|
||||||
|
inverses = collect(Iterators.flatten((2i, 2i - 1) for i in 1:n))
|
||||||
|
return FreeGroup(Alphabet(symbols, inverses))
|
||||||
|
end
|
||||||
|
|
||||||
function FreeGroup(A::Alphabet)
|
FreeGroup(A::Alphabet) = FreeGroup(KnuthBendix.LenLex(A))
|
||||||
|
|
||||||
|
function __group_gens(A::Alphabet)
|
||||||
@boundscheck @assert all(KnuthBendix.hasinverse(l, A) for l in A)
|
@boundscheck @assert all(KnuthBendix.hasinverse(l, A) for l in A)
|
||||||
gens = Vector{eltype(A)}()
|
gens = Vector{eltype(A)}()
|
||||||
invs = Vector{eltype(A)}()
|
invs = Vector{eltype(A)}()
|
||||||
@ -203,20 +210,12 @@ function FreeGroup(A::Alphabet)
|
|||||||
push!(gens, l)
|
push!(gens, l)
|
||||||
push!(invs, inv(l, A))
|
push!(invs, inv(l, A))
|
||||||
end
|
end
|
||||||
|
return gens
|
||||||
return FreeGroup(gens, A)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function FreeGroup(n::Integer)
|
function FreeGroup(O::KnuthBendix.WordOrdering)
|
||||||
symbols = Symbol[]
|
grp_gens = __group_gens(alphabet(O))
|
||||||
inverses = Int[]
|
return FreeGroup(grp_gens, O)
|
||||||
sizehint!(symbols, 2n)
|
|
||||||
sizehint!(inverses, 2n)
|
|
||||||
for i in 1:n
|
|
||||||
push!(symbols, Symbol(:f, i), Symbol(:F, i))
|
|
||||||
push!(inverses, 2i, 2i - 1)
|
|
||||||
end
|
|
||||||
return FreeGroup(symbols[1:2:2n], Alphabet(symbols, inverses))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
function Base.show(io::IO, F::FreeGroup)
|
function Base.show(io::IO, F::FreeGroup)
|
||||||
@ -265,10 +264,18 @@ function FPGroup(
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Base.show(io::IO, ::MIME"text/plain", G::FPGroup)
|
function Base.show(io::IO, ::MIME"text/plain", G::FPGroup)
|
||||||
print(io, "Finitely presented group generated by:\n\t{")
|
println(
|
||||||
Base.print_array(io, permutedims(gens(G)))
|
io,
|
||||||
println(io, " },")
|
"Finitely presented group generated by $(ngens(G)) element",
|
||||||
println(io, "subject to relations:")
|
ngens(G) > 1 ? 's' : "",
|
||||||
|
": ",
|
||||||
|
)
|
||||||
|
join(io, gens(G), ", ", ", and ")
|
||||||
|
println(
|
||||||
|
io,
|
||||||
|
"\n subject to relation",
|
||||||
|
length(relations(G)) > 1 ? 's' : "",
|
||||||
|
)
|
||||||
return Base.print_array(io, relations(G))
|
return Base.print_array(io, relations(G))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@
|
|||||||
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
|
[2, 1, 4, 3, 6, 5, 8, 7, 10, 9]
|
||||||
)
|
)
|
||||||
|
|
||||||
F4 = FreeGroup([:a, :b, :c, :d], A4)
|
F4 = FreeGroup(A4)
|
||||||
a, b, c, d = gens(F4)
|
a, b, c, d = gens(F4)
|
||||||
D = ntuple(i -> gens(F4, i), 4)
|
D = ntuple(i -> gens(F4, i), 4)
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
@test FreeGroup(A) isa FreeGroup
|
@test FreeGroup(A) isa FreeGroup
|
||||||
@test sprint(show, FreeGroup(A)) == "free group on 3 generators"
|
@test sprint(show, FreeGroup(A)) == "free group on 3 generators"
|
||||||
|
|
||||||
F = FreeGroup([:a, :b, :c], A)
|
F = FreeGroup([:a, :b, :c], Groups.KnuthBendix.LenLex(A))
|
||||||
@test sprint(show, F) == "free group on 3 generators"
|
@test sprint(show, F) == "free group on 3 generators"
|
||||||
|
|
||||||
a, b, c = gens(F)
|
a, b, c = gens(F)
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
@testset "FreeGroup" begin
|
@testset "FreeGroup" begin
|
||||||
|
|
||||||
A3 = Alphabet([:a, :b, :c, :A, :B, :C], [4,5,6,1,2,3])
|
A3 = Alphabet([:a, :b, :c, :A, :B, :C], [4,5,6,1,2,3])
|
||||||
F3 = FreeGroup([:a, :b, :c], A3)
|
F3 = FreeGroup(A3)
|
||||||
@test F3 isa FreeGroup
|
@test F3 isa FreeGroup
|
||||||
|
|
||||||
@test gens(F3) isa Vector
|
@test gens(F3) isa Vector
|
||||||
|
Loading…
Reference in New Issue
Block a user