Skip to content

Commit

Permalink
Merge pull request #27 from kalmarek/enh/simplify_wlmetric
Browse files Browse the repository at this point in the history
simplify wlmetric_ball
  • Loading branch information
Marek Kaluba authored Mar 22, 2023
2 parents 8bd3f7e + 7518505 commit 7230106
Show file tree
Hide file tree
Showing 9 changed files with 66 additions and 95 deletions.
5 changes: 2 additions & 3 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,20 +1,19 @@
name = "Groups"
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
authors = ["Marek Kaluba <[email protected]>"]
version = "0.7.5"
version = "0.7.6"

[deps]
Folds = "41a02a25-b8f0-4f67-bc48-60067656b558"
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"
KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"

[compat]
Folds = "0.2.7"
GroupsCore = "0.4"
KnuthBendix = "0.4"
OrderedCollections = "1"
Expand Down
5 changes: 1 addition & 4 deletions src/Groups.jl
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
module Groups

import Folds
import Logging

using GroupsCore
import GroupsCore.Random

import OrderedCollections: OrderedSet
import Random

import KnuthBendix
import KnuthBendix: AbstractWord, Alphabet, Word
Expand Down
2 changes: 1 addition & 1 deletion src/autgroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ function KnuthBendix.Alphabet(S::AbstractVector{<:GSymbol})
return Alphabet(S, inversions)
end

struct AutomorphismGroup{G<:Group,T,RW,S} <: AbstractFPGroup
mutable struct AutomorphismGroup{G<:Group,T,RW,S} <: AbstractFPGroup
group::G
gens::Vector{T}
rw::RW
Expand Down
2 changes: 1 addition & 1 deletion src/hashing.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
## Hashing

equality_data(g::AbstractFPGroupElement) = (normalform!(g); word(g))
equality_data(g::AbstractFPGroupElement) = word(g)

bitget(h::UInt, n::Int) = Bool((h & (1 << n)) >> n)
bitclear(h::UInt, n::Int) = h & ~(1 << n)
Expand Down
2 changes: 2 additions & 0 deletions src/iteration.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import OrderedCollections: OrderedSet

mutable struct FPGroupIter{S,T,GEl}
seen::S
seen_iter_state::T
Expand Down
13 changes: 12 additions & 1 deletion src/types.jl
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,18 @@ end

function Base.:(*)(g::GEl, h::GEl) where {GEl<:AbstractFPGroupElement}
@boundscheck @assert parent(g) === parent(h)
return GEl(word(g) * word(h), parent(g))
A = alphabet(parent(g))
k = 0
while k + 1 min(length(word(g)), length(word(h)))
if inv(word(g)[end-k], A) == word(h)[k+1]
k += 1
else
break
end
end
w = @view(word(g)[1:end-k]) * @view(word(h)[k+1:end])
res = GEl(w, parent(g))
return res
end

function GroupsCore.isfiniteorder(g::AbstractFPGroupElement)
Expand Down
72 changes: 21 additions & 51 deletions src/wl_ball.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
wlmetric_ball(S::AbstractVector{<:GroupElem}
[, center=one(first(S)); radius=2, op=*, threading=true])
[, center=one(first(S)); radius=2, op=*])
Compute metric ball as a list of elements of non-decreasing length, given the
word-length metric on the group generated by `S`. The ball is centered at `center`
(by default: the identity element). `radius` and `op` keywords specify the
Expand All @@ -11,57 +11,27 @@ function wlmetric_ball(
center::T = one(first(S));
radius = 2,
op = *,
threading = true,
) where {T}
threading && return wlmetric_ball_thr(S, center; radius = radius, op = op)
return wlmetric_ball_serial(S, center; radius = radius, op = op)
end

function wlmetric_ball_serial(
S::AbstractVector{T},
center::T = one(first(S));
radius = 2,
op = *,
) where {T}
@assert radius >= 1
old = union!(OrderedSet([center]), [center * s for s in S])
sizes = [1, length(old)]
for _ in 2:radius
new = collect(
op(o, s) for o in @view(old.dict.keys[sizes[end-1]:end]) for s in S
)
union!(old, new)
push!(sizes, length(old))
end
return old.dict.keys, sizes[2:end]
end

function wlmetric_ball_thr(
S::AbstractVector{T},
center::T = one(first(S));
radius = 2,
op = *,
) where {T}
@assert radius >= 1
old = union!([center], [center * s for s in S])
return _wlmetric_ball(S, old, radius, op, Folds.collect, Folds.unique)
end

function _wlmetric_ball(S, old, radius, op, collect, unique)
sizes = [1, length(old)]
for _ in 2:radius
old = let old = old, S = S
new = collect(
(g = op(o, s);
normalform!(g);
hash(g);
g) for o in @view(old[sizes[end-1]:end]) for s in S
)

append!(old, new)
unique(old)
ball = [center]
sizes = [1]
if radius 0
return ball, sizes[2:end]
else
ball = union!(ball, [center * s for s in S])
push!(sizes, length(ball))
if radius == 1
return ball, sizes[2:end]
else
for _ in 2:radius
new = collect(
op(o, s) for o in @view(ball[sizes[end-1]:end]) for s in S
)
append!(ball, new)
unique!(ball)
push!(sizes, length(ball))
end
end
push!(sizes, length(old))
return ball, sizes[2:end]
end
return old, sizes[2:end]
# return wlmetric_ball_serial(S, center; radius = radius, op = op)
end
40 changes: 17 additions & 23 deletions test/benchmarks.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,18 @@ using Groups

function wl_ball(F; radius::Integer)
g, state = iterate(F)
while length(word(g)) <= radius
sizes = Int[]
while length(sizes) radius
res = iterate(F, state)
isnothing(res) && break
g, state = res
if length(word(g)) > length(sizes)
push!(sizes, length(state.seen) - 1)
end
end
elts = collect(state.seen)
elts = resize!(elts, length(elts)-1)
return elts
resize!(elts, sizes[end] - 1)
return elts, sizes[2:end]
end

@testset "Benchmarks" begin
Expand All @@ -25,21 +29,16 @@ end
let G = FN
S = unique([gens(G); inv.(gens(G))])

sizes1 = last(Groups.wlmetric_ball(S, radius=R, threading=false))
sizes2 = last(Groups.wlmetric_ball(S, radius=R, threading=true))

l = length(wl_ball(G, radius=R))
sizes1 = last(Groups.wlmetric_ball(S; radius = R))
sizes2 = last(wl_ball(G; radius = R))

@test sizes1 == sizes2
@test last(sizes1) == l

@info "Ball of radius $R in $(parent(first(S)))" sizes=sizes1
@info "Ball of radius $R in $(parent(first(S)))" sizes = sizes1
@info "serial"
@time Groups.wlmetric_ball(S, radius=R, threading=false)
@info "threaded"
@time Groups.wlmetric_ball(S, radius=R, threading=true)
@time Groups.wlmetric_ball(S, radius = R)
@info "iteration"
@time wl_ball(G, radius=R)
@time wl_ball(G, radius = R)
end
end

Expand All @@ -51,21 +50,16 @@ end
let G = SAutFN
S = unique([gens(G); inv.(gens(G))])

sizes1 = last(Groups.wlmetric_ball(S, radius=R, threading=false))
sizes2 = last(Groups.wlmetric_ball(S, radius=R, threading=true))

l = length(wl_ball(G, radius=R))
sizes1 = last(Groups.wlmetric_ball(S; radius = R))
sizes2 = last(wl_ball(G; radius = R))

@test sizes1 == sizes2
@test last(sizes1) == l

@info "Ball of radius $R in $(parent(first(S)))" sizes=sizes1
@info "Ball of radius $R in $(parent(first(S)))" sizes = sizes1
@info "serial"
@time Groups.wlmetric_ball(S, radius=R, threading=false)
@info "threaded"
@time Groups.wlmetric_ball(S, radius=R, threading=true)
@time Groups.wlmetric_ball(S, radius = R)
@info "iteration"
@time wl_ball(G, radius=R)
@time wl_ball(G, radius = R)
end
end
end
20 changes: 9 additions & 11 deletions test/matrix_groups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ using Groups.MatrixGroups
S = unique([S; inv.(S)])
_, sizes = Groups.wlmetric_ball(S; radius = 4)
@test sizes == [7, 33, 141, 561]
_, sizes = Groups.wlmetric_ball_serial(S; radius = 4)
@test sizes == [7, 33, 141, 561]

Logging.with_logger(Logging.NullLogger()) do
@testset "GroupsCore conformance" begin
Expand All @@ -35,9 +33,9 @@ using Groups.MatrixGroups
end
end

x = w * inv(w) * r
x = w * inv(SL3Z(word(w)[end:end])) * r

@test length(word(x)) == 5
@test length(word(x)) == length(word(r))
@test size(x) == (3, 3)
@test eltype(x) == Int8

Expand Down Expand Up @@ -65,10 +63,10 @@ using Groups.MatrixGroups
end
end

x = gens(Sp6, 1)
x *= inv(x) * gens(Sp6, 2)
x = gens(Sp6, 1) * gens(Sp6, 2)^2
x *= inv(gens(Sp6, 2)^2) * gens(Sp6, 3)

@test length(word(x)) == 3
@test length(word(x)) == 2
@test size(x) == (6, 6)
@test eltype(x) == Int8

Expand All @@ -80,7 +78,7 @@ using Groups.MatrixGroups
@test contains(sprint(show, MIME"text/plain"(), x), "∈ Sp{6,Int8}")
@test sprint(print, x) isa String

@test length(word(x)) == 1
@test length(word(x)) == 2

for g in gens(Sp6)
@test MatrixGroups.issymplectic(MatrixGroups.matrix(g))
Expand All @@ -101,10 +99,10 @@ using Groups.MatrixGroups
end
end

x = gens(G, 1)
x *= inv(x) * gens(G, 2)
x = gens(G, 1) * gens(G, 2)^3
x *= gens(G, 2)^-3

@test length(word(x)) == 3
@test length(word(x)) == 1
@test size(x) == (6, 6)
@test eltype(x) == Int16

Expand Down

2 comments on commit 7230106

@kalmarek
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Registration pull request created: JuliaRegistries/General/80141

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.7.6 -m "<description of version>" 7230106bfc7a53555ee7a5a46625cf03b927468c
git push origin v0.7.6

Please sign in to comment.