diff --git a/Project.toml b/Project.toml index 9a7d77b..4b3a75e 100644 --- a/Project.toml +++ b/Project.toml @@ -6,6 +6,7 @@ version = "0.4.2" [deps] AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" +ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d" [compat] AbstractAlgebra = "^0.9.0" diff --git a/src/AutGroup.jl b/src/AutGroup.jl index 46198cf..1ce4b00 100644 --- a/src/AutGroup.jl +++ b/src/AutGroup.jl @@ -258,7 +258,8 @@ function (==)(g::Automorphism{N}, h::Automorphism{N}) where N # cheap # if hashes differ, images must have differed as well hash(g) != hash(h) && return false - # equal elements, or possibly a hash conflict + + # hashes equal, hence either equal elements, or a hash conflict begin if !img_computed img_task = Threads.@spawn img = compute_images(g) @@ -271,6 +272,8 @@ function (==)(g::Automorphism{N}, h::Automorphism{N}) where N !img_computed && fetch(img_task) !imh_computed && fetch(imh_task) end + + img != imh && @warn "hash collision in == :" g h return img == imh end diff --git a/src/Groups.jl b/src/Groups.jl index 3b79d37..dc4f5e6 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -64,24 +64,59 @@ Return vector of generators of `G`, as its elements. AbstractAlgebra.gens(G::AbstractFPGroup) = G.(G.gens) """ - metric_ball(S::AbstractVector{<:GroupElem} + wlmetric_ball(S::AbstractVector{<:GroupElem} [, 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 radius and multiplication operation to be used. """ -function generate_balls(S::AbstractVector{T}, center::T=one(first(S)); - radius=2, op=*) where T<:Union{GroupElem, NCRingElem} - sizes = Int[] - B = [one(first(S))] - for i in 1:radius - BB = [op(i,j) for (i,j) in Base.product(B,S)] - B = unique([B; vec(BB)]) - push!(sizes, length(B)) + +function wlmetric_ball_serial(S::AbstractVector{T}; radius=2, op=*) where T<:Union{GroupElem, NCRingElem} + old = unique!([one(first(S)), S...]) + sizes = [1, length(old)] + for i in 2:radius + new = collect(op(i,j) for (i,j) in Base.product(@view(old[sizes[end-1]:end]), S)) + append!(old, new) + resize!(new, 0) + old = unique!(old) + push!(sizes, length(old)) end - isone(center) && return B, sizes - return c.*B, sizes + return old, sizes[2:end] +end + +function wlmetric_ball_thr(S::AbstractVector{T}; radius=2, op=*) where T<:Union{GroupElem, NCRingElem} + old = unique!([one(first(S)), S...]) + sizes = [1, length(old)] + for r in 2:radius + begin + new = ThreadsX.collect(op(o, s) for o in @view(old[sizes[end-1]:end]) for s in S) + ThreadsX.foreach(hash, new) + end + append!(old, new) + resize!(new, 0) + old = ThreadsX.unique(old) + push!(sizes, length(old)) + end + return old, sizes[2:end] +end + +function wlmetric_ball_serial(S::AbstractVector{T}, center::T; radius=2, op=*) where T<:Union{GroupElem, NCRingElem} + E, sizes = metric_ball_serial(S, radius=radius, op=op) + isone(center) && return E, sizes + return c.*E, sizes +end + +function wlmetric_ball_thr(S::AbstractVector{T}, center::T, radius=2, op=*) where T<:Union{GroupElem, NCRingElem} + E, sizes = metric_ball_thr(S, radius=radius, op=op) + isone(center) && return E, sizes + return c.*E, sizes +end + +function wlmetric_ball(S::AbstractVector{T}, center::T=one(first(S); + radius=2, op=*, threading=true) + threading && return wlmetric_ball_thr(S, center, radius=radius, op=op) + return return wlmetric_ball_serial(S, center, radius=radius, op=op) end """ diff --git a/test/runtests.jl b/test/runtests.jl index 25241ff..7e29894 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -13,7 +13,7 @@ using LinearAlgebra s = one(M); s[1,3] = 2; s[3,2] = -1; S = [w,r,s]; S = unique([S; inv.(S)]); - _, sizes = Groups.generate_balls(S, radius=4); + _, sizes = Groups.wlmetric_ball(S, radius=4); @test sizes == [7, 33, 141, 561] end