From 1f1e51917a392feba759efb1b7227da148f332e4 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 22 Mar 2023 21:41:37 +0100 Subject: [PATCH 1/5] remove threaded wlmetric_ball * the threaded version was hardly faster * there was a memory leak (?) that was gone with -t 1 * simplifies the whole thing --- Project.toml | 5 ++-- src/Groups.jl | 5 +--- src/iteration.jl | 2 ++ src/wl_ball.jl | 72 ++++++++++++++---------------------------------- 4 files changed, 26 insertions(+), 58 deletions(-) diff --git a/Project.toml b/Project.toml index 6b177eb..6e57613 100644 --- a/Project.toml +++ b/Project.toml @@ -1,20 +1,19 @@ name = "Groups" uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557" authors = ["Marek Kaluba "] -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" diff --git a/src/Groups.jl b/src/Groups.jl index 96c7d09..9237429 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -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 diff --git a/src/iteration.jl b/src/iteration.jl index 210289b..e3028fb 100644 --- a/src/iteration.jl +++ b/src/iteration.jl @@ -1,3 +1,5 @@ +import OrderedCollections: OrderedSet + mutable struct FPGroupIter{S,T,GEl} seen::S seen_iter_state::T diff --git a/src/wl_ball.jl b/src/wl_ball.jl index ae8db7d..96b5984 100644 --- a/src/wl_ball.jl +++ b/src/wl_ball.jl @@ -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 @@ -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 From a1bc334fb216c652372dd5c32044c41d6a040a3a Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 22 Mar 2023 21:43:00 +0100 Subject: [PATCH 2/5] make AutomorphismGroup mutable parent field of an automorphism is now a pointer (i.e. 8 bytes) --- src/autgroups.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autgroups.jl b/src/autgroups.jl index 2d4ff18..75b8eba 100644 --- a/src/autgroups.jl +++ b/src/autgroups.jl @@ -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 From c69eff15404d4ee94d3c4caa92f55de78ba14b16 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 22 Mar 2023 21:44:09 +0100 Subject: [PATCH 3/5] freely reduce words upon * --- src/types.jl | 13 ++++++++++++- test/matrix_groups.jl | 20 +++++++++----------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/src/types.jl b/src/types.jl index 336b570..b2482d9 100644 --- a/src/types.jl +++ b/src/types.jl @@ -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) diff --git a/test/matrix_groups.jl b/test/matrix_groups.jl index e93c70e..ec6dfef 100644 --- a/test/matrix_groups.jl +++ b/test/matrix_groups.jl @@ -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 @@ -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 @@ -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 @@ -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)) @@ -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 From 038fc29b81f03421cef08ad1b39dc2bd7895d131 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 22 Mar 2023 21:44:33 +0100 Subject: [PATCH 4/5] update benchmark on wl_ball --- test/benchmarks.jl | 40 +++++++++++++++++----------------------- 1 file changed, 17 insertions(+), 23 deletions(-) diff --git a/test/benchmarks.jl b/test/benchmarks.jl index 44db248..74e9c98 100644 --- a/test/benchmarks.jl +++ b/test/benchmarks.jl @@ -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 @@ -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 @@ -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 From 751850568ccc0fc688f04dff41f7735733e702b9 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 22 Mar 2023 21:45:04 +0100 Subject: [PATCH 5/5] make equality_data immutable operation --- src/hashing.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hashing.jl b/src/hashing.jl index 5c33019..48593e9 100644 --- a/src/hashing.jl +++ b/src/hashing.jl @@ -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)