From bdc37e38c52dcb0bc32bbe7286f2cf9215efb388 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 26 Jul 2023 09:52:37 +0200 Subject: [PATCH 01/10] avoid copy/recreation of Basis in WedderburnDecomposition --- src/wedderburn_decomposition.jl | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/wedderburn_decomposition.jl b/src/wedderburn_decomposition.jl index 7be4fc3..4c8a88e 100644 --- a/src/wedderburn_decomposition.jl +++ b/src/wedderburn_decomposition.jl @@ -34,8 +34,28 @@ function WedderburnDecomposition( T::Type, G::Group, action::Action, - basis_full, - basis_half, + basis_full::AbstractVector, + basis_half::AbstractVector, + S = Rational{Int}; + semisimple = false, +) + return WedderburnDecomposition( + T, + G, + action, + StarAlgebras.Basis{UInt32}(basis_full), + StarAlgebras.Basis{UInt32}(basis_half), + S; + semisimple = semisimple, + ) +end + +function WedderburnDecomposition( + T::Type, + G::Group, + action::Action, + basis_full::StarAlgebras.Basis, + basis_half::StarAlgebras.Basis, S = Rational{Int}; semisimple = false, ) From f50a715cec7577c4a83fb33d77189fb2aab7add0 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 26 Jul 2023 09:56:15 +0200 Subject: [PATCH 02/10] add invariant_vectors(::Group, ::Action, ::Basis) --- src/wedderburn_decomposition.jl | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/wedderburn_decomposition.jl b/src/wedderburn_decomposition.jl index 4c8a88e..e695f71 100644 --- a/src/wedderburn_decomposition.jl +++ b/src/wedderburn_decomposition.jl @@ -163,16 +163,23 @@ end function invariant_vectors( tbl::Characters.CharacterTable, + act::Union{<:ByPermutations,<:BySignedPermutations}, + basis::StarAlgebras.Basis, +) + return invariant_vectors(parent(tbl), act, basis) +end + +function invariant_vectors( + G::Group, act::ByPermutations, basis::StarAlgebras.Basis{T,I}, ) where {T,I} - G = parent(tbl) tovisit = trues(length(basis)) - invariant_vs = Vector{SparseVector{Rational{Int}}}() - + invariant_vs = Vector{SparseVector{Rational{Int},I}}() ordG = order(Int, G) elts = collect(G) orbit = zeros(I, ordG) + sizehint!(invariant_vs, 2length(basis) ÷ ordG) for i in eachindex(basis) if tovisit[i] @@ -191,11 +198,10 @@ function invariant_vectors( end function invariant_vectors( - tbl::Characters.CharacterTable, + G::Group, act::BySignedPermutations, basis::StarAlgebras.Basis{T,I}, ) where {T,I} - G = parent(tbl) ordG = order(Int, G) elts = collect(G) orbit = zeros(I, ordG) From 747d2f5b28d8b302f439033df84774ead09e351a Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 26 Jul 2023 10:15:10 +0200 Subject: [PATCH 03/10] invariant vecs: partition basis to chunks & @spawn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes multithreading a bit coarser, but provides better control to GC (e.g. no more than 12GB used with basis of SAut(F₅)) --- src/wedderburn_decomposition.jl | 37 +++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/src/wedderburn_decomposition.jl b/src/wedderburn_decomposition.jl index e695f71..1f7e4e2 100644 --- a/src/wedderburn_decomposition.jl +++ b/src/wedderburn_decomposition.jl @@ -178,22 +178,37 @@ function invariant_vectors( invariant_vs = Vector{SparseVector{Rational{Int},I}}() ordG = order(Int, G) elts = collect(G) - orbit = zeros(I, ordG) sizehint!(invariant_vs, 2length(basis) ÷ ordG) - for i in eachindex(basis) - if tovisit[i] - bi = basis[i] - Threads.@threads for j in eachindex(elts) - orbit[j] = basis[action(act, elts[j], bi)] + lck = Threads.SpinLock() # to guard tovisit & invariant_vs + + tasks_per_thread = 2 + chunk_size = max(1, length(basis) ÷ (tasks_per_thread * Threads.nthreads())) + data_chunks = Iterators.partition(eachindex(basis), chunk_size) + + states = map(data_chunks) do chunk + Threads.@spawn begin + orbit = zeros(I, ordG) + for i in chunk + if tovisit[i] + bi = basis[i] + for j in eachindex(elts) + orbit[j] = basis[action(act, elts[j], bi)] + end + vals = fill(1 // ordG, ordG) + v = sparsevec(orbit, vals, length(basis)) + lock(lck) do + if tovisit[i] + tovisit[orbit] .= false + push!(invariant_vs, v) + end + end + end end - tovisit[orbit] .= false - push!( - invariant_vs, - sparsevec(orbit, fill(1 // ordG, ordG), length(basis)), - ) + return true end end + fetch.(states) return invariant_vs end From 9cab87105bee96d56b78c84bf6d855881b751571 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 26 Jul 2023 10:16:37 +0200 Subject: [PATCH 04/10] better multithreading in WedderburnDecomposition --- src/wedderburn_decomposition.jl | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/wedderburn_decomposition.jl b/src/wedderburn_decomposition.jl index 1f7e4e2..22d145c 100644 --- a/src/wedderburn_decomposition.jl +++ b/src/wedderburn_decomposition.jl @@ -60,15 +60,24 @@ function WedderburnDecomposition( semisimple = false, ) tbl = CharacterTable(S, G) + invariants = Threads.@spawn invariant_vectors(tbl, action, basis_full) + ehom = CachedExtensionHomomorphism(G, action, basis_half; precompute = true) check_group_action(G, ehom; full_check = false) - Uπs = symmetry_adapted_basis(T, tbl, ehom; semisimple = semisimple) - - basis = StarAlgebras.Basis{UInt32}(basis_full) - invariants = invariant_vectors(tbl, action, basis) + Uπs = Threads.@spawn symmetry_adapted_basis( + T, + tbl, + ehom; + semisimple = semisimple, + ) - return WedderburnDecomposition(basis, invariants, Uπs, ehom) + return WedderburnDecomposition( + basis_full, + fetch(invariants), + fetch(Uπs), + ehom, + ) end function Base.show(io::IO, wbdec::SymbolicWedderburn.WedderburnDecomposition) From 05213589f277c16b807ff1f16203a89ba3d5ed9b Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 26 Jul 2023 10:17:35 +0200 Subject: [PATCH 05/10] minor improvements in invariant vecs BySignedPermutations --- src/wedderburn_decomposition.jl | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/wedderburn_decomposition.jl b/src/wedderburn_decomposition.jl index 22d145c..c6e4926 100644 --- a/src/wedderburn_decomposition.jl +++ b/src/wedderburn_decomposition.jl @@ -245,11 +245,12 @@ function invariant_vectors( coeffs[j] = c end @view(tovisit[orbit]) .= false - v = sparsevec(orbit, 1 // ordG .* coeffs, length(basis)) - if (VT = eltype(v)) <: Union{AbstractFloat,Complex} - droptol!(v, eps(real(VT)) * length(v)) + vals = sparsevec(orbit, coeffs, length(basis)) + if CT <: Union{AbstractFloat,Complex} + droptol!(v, eps(real(CT)) * length(vals)) end if !iszero(v) + v .*= 1 // ordG push!(invariant_vs, v) end end From 39d6253858f1e648278188f3dd9e0dec89572e5e Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Thu, 27 Jul 2023 00:25:41 +0200 Subject: [PATCH 06/10] fix: sort invariant vectors by their first nz entry this is to bring determinism back --- src/wedderburn_decomposition.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/wedderburn_decomposition.jl b/src/wedderburn_decomposition.jl index c6e4926..a6f55cd 100644 --- a/src/wedderburn_decomposition.jl +++ b/src/wedderburn_decomposition.jl @@ -218,7 +218,7 @@ function invariant_vectors( end end fetch.(states) - return invariant_vs + return sort!(invariant_vs; by = first ∘ SparseArrays.nonzeroinds) end function invariant_vectors( From eee1c30d122fae9ace71521f70bc8847249f1ff6 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Thu, 27 Jul 2023 00:26:04 +0200 Subject: [PATCH 07/10] fix test for type-stability if inv_vecs --- test/action_permutation.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/action_permutation.jl b/test/action_permutation.jl index 4b13485..0e49821 100644 --- a/test/action_permutation.jl +++ b/test/action_permutation.jl @@ -73,7 +73,7 @@ end SymbolicWedderburn.basis(ehom), ) @test length(inv_vec) == 22 - @test eltype(inv_vec) == SparseVector{Rational{Int}} + @test eltype(eltype(inv_vec)) == Rational{Int} @testset "semisimple decomposition" begin let i = 1 From 9b9d1ff04c95c2a810a975e2d4f79170d54e5b59 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Thu, 27 Jul 2023 00:26:34 +0200 Subject: [PATCH 08/10] fix: inv_vecs for BySignedPermutation --- src/wedderburn_decomposition.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/wedderburn_decomposition.jl b/src/wedderburn_decomposition.jl index a6f55cd..5d0e226 100644 --- a/src/wedderburn_decomposition.jl +++ b/src/wedderburn_decomposition.jl @@ -245,9 +245,9 @@ function invariant_vectors( coeffs[j] = c end @view(tovisit[orbit]) .= false - vals = sparsevec(orbit, coeffs, length(basis)) + v = sparsevec(orbit, coeffs, length(basis)) if CT <: Union{AbstractFloat,Complex} - droptol!(v, eps(real(CT)) * length(vals)) + droptol!(v, eps(real(CT)) * length(v)) end if !iszero(v) v .*= 1 // ordG From c0394af96bf742334dd98ae68d1a414b98d20fa8 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Thu, 27 Jul 2023 09:07:25 +0200 Subject: [PATCH 09/10] use same multithreading to BySignedPermutations --- src/wedderburn_decomposition.jl | 54 +++++++++++++++++++++------------ 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/src/wedderburn_decomposition.jl b/src/wedderburn_decomposition.jl index 5d0e226..e4253a2 100644 --- a/src/wedderburn_decomposition.jl +++ b/src/wedderburn_decomposition.jl @@ -208,7 +208,7 @@ function invariant_vectors( v = sparsevec(orbit, vals, length(basis)) lock(lck) do if tovisit[i] - tovisit[orbit] .= false + @view(tovisit[orbit]) .= false push!(invariant_vs, v) end end @@ -228,32 +228,46 @@ function invariant_vectors( ) where {T,I} ordG = order(Int, G) elts = collect(G) - orbit = zeros(I, ordG) CT = promote_type(coeff_type(act), Rational{Int}) # output coeff type - coeffs = Vector{CT}(undef, ordG) tovisit = trues(length(basis)) invariant_vs = Vector{SparseVector{CT,Int}}() sizehint!(invariant_vs, length(basis) ÷ ordG) - for (i, b) in enumerate(basis) - if tovisit[i] - Threads.@threads for j in eachindex(elts) - g = elts[j] - gb, c = SymbolicWedderburn.action(act, g, b) - orbit[j] = basis[gb] - coeffs[j] = c - end - @view(tovisit[orbit]) .= false - v = sparsevec(orbit, coeffs, length(basis)) - if CT <: Union{AbstractFloat,Complex} - droptol!(v, eps(real(CT)) * length(v)) - end - if !iszero(v) - v .*= 1 // ordG - push!(invariant_vs, v) + lck = Threads.SpinLock() # to guard tovisit & invariant_vs + + tasks_per_thread = 2 + chunk_size = max(1, length(basis) ÷ (tasks_per_thread * Threads.nthreads())) + data_chunks = Iterators.partition(eachindex(basis), chunk_size) + + states = map(data_chunks) do chunk + Threads.@spawn begin + orbit = zeros(I, ordG) + coeffs = Vector{CT}(undef, ordG) + for i in chunk + if tovisit[i] + bi = basis[i] + for j in eachindex(elts) + gb, c = SymbolicWedderburn.action(act, elts[j], bi) + orbit[j] = basis[gb] + coeffs[j] = c + end + v = sparsevec(orbit, coeffs .// ordG, length(basis)) + if CT <: Union{AbstractFloat,Complex} + droptol!(v, eps(real(CT)) * length(v)) + end + if !iszero(v) + lock(lck) do + if tovisit[i] + @view(tovisit[orbit]) .= false + push!(invariant_vs, v) + end + end + end + end end end end - return invariant_vs + fetch.(states) + return sort!(invariant_vs; by = first ∘ SparseArrays.nonzeroinds) end From 0ca11583e6812a353fcad866f1831c8c67a5b9e5 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Fri, 28 Jul 2023 12:44:48 +0200 Subject: [PATCH 10/10] bump version to 0.3.6 --- Project.toml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Project.toml b/Project.toml index 1c494e6..fcf17c1 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,10 @@ name = "SymbolicWedderburn" uuid = "858aa9a9-4c7c-4c62-b466-2421203962a2" -authors = ["Marek Kaluba ", "tweisser "] -version = "0.3.5" +authors = [ + "Marek Kaluba ", + "Tillmann Weisser ", +] +version = "0.3.6" [deps] Cyclotomics = "da8f5974-afbb-4dc8-91d8-516d5257c83b"