From b47a253c0a219833bc0d8c2e2241e0d7b356942a Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 28 Nov 2023 13:28:16 +0100 Subject: [PATCH 01/10] =?UTF-8?q?rename=20Characters.constituents=20?= =?UTF-8?q?=E2=86=92=20Characters.multiplicities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Characters/Characters.jl | 6 ++- src/Characters/class_functions.jl | 90 +++++++++++++++++++++---------- src/matrix_projections.jl | 4 +- src/sa_basis.jl | 18 +++---- test/action_permutation.jl | 4 +- test/characters.jl | 4 +- 6 files changed, 83 insertions(+), 43 deletions(-) diff --git a/src/Characters/Characters.jl b/src/Characters/Characters.jl index e188e80..8caca5d 100644 --- a/src/Characters/Characters.jl +++ b/src/Characters/Characters.jl @@ -11,7 +11,11 @@ import PermutationGroups export AbstractClassFunction, Character, CharacterTable export conjugacy_classes, - constituents, degree, irreducible_characters, isirreducible, table + multiplicities, + degree, + irreducible_characters, + isirreducible, + table include("gf.jl") diff --git a/src/Characters/class_functions.jl b/src/Characters/class_functions.jl index 642a06f..f87327c 100644 --- a/src/Characters/class_functions.jl +++ b/src/Characters/class_functions.jl @@ -59,29 +59,30 @@ end Struct representing (possibly virtual) character of a group. Characters are backed by `table(χ)::CharacterTable` which actually stores the -character values. The constituents (decomposition into the irreducible summands) -of a given character can be obtained by calling `constituents(χ)` which returns a -vector of coefficients of `χ` in the basis of `irreducible_characters(table(χ))`. +character values. The multiplicities (decomposition into the irreducible +summands) of a given character can be obtained by calling `multiplicities(χ)` +which returns a vector of coefficients of `χ` in the basis of +`irreducible_characters(table(χ))`. -It is assumed that equal class functions on the same group will have **identical** -(ie. `===`) character tables. +It is assumed that equal class functions on the same group will have +**identical** (ie. `===`) character tables. """ struct Character{T,S,ChT<:CharacterTable} <: AbstractClassFunction{T} table::ChT - constituents::Vector{S} + multips::Vector{S} end function Character{R}( chtbl::CharacterTable{Gr,T}, - constituents::AbstractVector{S}, + multips::AbstractVector{S}, ) where {R,Gr,T,S} - return Character{R,S,typeof(chtbl)}(chtbl, constituents) + return Character{R,S,typeof(chtbl)}(chtbl, multips) end -function Character(chtbl::CharacterTable, constituents::AbstractVector) - R = Base._return_type(*, Tuple{eltype(chtbl),eltype(constituents)}) +function Character(chtbl::CharacterTable, multips::AbstractVector) + R = Base._return_type(*, Tuple{eltype(chtbl),eltype(multips)}) @assert R ≠ Any - return Character{R}(chtbl, constituents) + return Character{R}(chtbl, multips) end function Character(chtbl::CharacterTable, i::Integer) @@ -95,14 +96,14 @@ function Character{T}(chtbl::CharacterTable, i::Integer) where {T} end function Character{T}(χ::Character) where {T} - S = eltype(constituents(χ)) + S = eltype(multiplicities(χ)) ChT = typeof(table(χ)) - return Character{T,S,ChT}(table(χ), constituents(χ)) + return Character{T,S,ChT}(table(χ), multiplicities(χ)) end ## Accessors table(χ::Character) = χ.table -constituents(χ::Character) = χ.constituents +multiplicities(χ::Character) = χ.multips ## AbstractClassFunction api Base.parent(χ::Character) = parent(table(χ)) @@ -122,7 +123,7 @@ Base.@propagate_inbounds function Base.getindex( T, sum( c * table(χ)[idx, i] for - (idx, c) in enumerate(constituents(χ)) if !iszero(c); + (idx, c) in enumerate(multiplicities(χ)) if !iszero(c); init = zero(T), ), ) @@ -131,12 +132,12 @@ end ## Basic functionality function Base.:(==)(χ::Character, ψ::Character) - return table(χ) === table(ψ) && constituents(χ) == constituents(ψ) + return table(χ) === table(ψ) && multiplicities(χ) == multiplicities(ψ) end -Base.hash(χ::Character, h::UInt) = hash(table(χ), hash(constituents(χ), h)) +Base.hash(χ::Character, h::UInt) = hash(table(χ), hash(multiplicities(χ), h)) function Base.deepcopy_internal(χ::Character, ::IdDict) - return Character(table(χ), copy(constituents(χ))) + return Character(table(χ), copy(multiplicities(χ))) end ## Character arithmetic @@ -145,17 +146,52 @@ for f in (:+, :-) @eval begin function Base.$f(χ::Character, ψ::Character) @assert table(χ) === table(ψ) - return Character(table(χ), $f(constituents(χ), constituents(ψ))) + return Character(table(χ), $f(multiplicities(χ), multiplicities(ψ))) end end end -Base.:*(χ::Character, c::Number) = Character(table(χ), c .* constituents(χ)) +Base.:*(χ::Character, c::Number) = Character(table(χ), c .* multiplicities(χ)) Base.:*(c::Number, χ::Character) = χ * c -Base.:/(χ::Character, c::Number) = Character(table(χ), constituents(χ) ./ c) +Base.:/(χ::Character, c::Number) = Character(table(χ), multiplicities(χ) ./ c) Base.zero(χ::Character) = 0 * χ +function __decompose(T::Type, values::AbstractVector, tbl::CharacterTable) + ψ = ClassFunction(values, conjugacy_classes(tbl), tbl.inv_of) + return decompose(T, ψ, tbl) +end + +function decompose(cfun::AbstractClassFunction, tbl::CharacterTable) + return decompose(eltype(cfun), cfun, tbl) +end + +function decompose(T::Type, cfun::AbstractClassFunction, tbl::CharacterTable) + vals = Vector{T}(undef, length(conjugacy_classes(tbl))) + return decompose!(vals, cfun, tbl) +end + +function decompose!( + vals::AbstractVector, + cfun::AbstractClassFunction, + tbl::CharacterTable, +) + @assert length(vals) == length(conjugacy_classes(tbl)) + @assert conjugacy_classes(tbl) === conjugacy_classes(cfun) + + for (i, idx) in enumerate(eachindex(vals)) + χ = Character(tbl, i) + vals[idx] = dot(χ, cfun) + end + return vals +end + +function Base.:*(χ::Character, ψ::Character) + @assert table(χ) == table(ψ) + values = Characters.values(χ) .* Characters.values(ψ) + return Character(table(χ), __decompose(Int, values, table(χ))) +end + ## Group-theoretic functions: PermutationGroups.degree(χ::Character) = Int(χ(one(parent(χ)))) @@ -170,16 +206,16 @@ function Base.conj(χ::Character{T,S}) where {T,S} all(isreal, vals) && return Character{T}(χ) tbl = table(χ) ψ = ClassFunction(vals[tbl.inv_of], conjugacy_classes(tbl), tbl.inv_of) - constituents = S[dot(ψ, χ) for χ in irreducible_characters(tbl)] - return Character{T,eltype(constituents),typeof(tbl)}(tbl, constituents) + multips = S[dot(ψ, χ) for χ in irreducible_characters(tbl)] + return Character{T,eltype(multips),typeof(tbl)}(tbl, multips) end function isvirtual(χ::Character) - return any(<(0), constituents(χ)) || any(!isinteger, constituents(χ)) + return any(<(0), multiplicities(χ)) || any(!isinteger, multiplicities(χ)) end function isirreducible(χ::Character) - C = constituents(χ) + C = multiplicities(χ) k = findfirst(!iszero, C) k !== nothing || return false # χ is zero isone(C[k]) || return false # muliplicity is ≠ 1 @@ -196,7 +232,7 @@ representation, modifying `χ` in place. function affordable_real!(χ::Character) ι = frobenius_schur(χ) if ι <= 0 # i.e. χ is complex or quaternionic - χ.constituents .+= constituents(conj(χ)) + χ.multips .+= multiplicities(conj(χ)) end return χ end @@ -242,7 +278,7 @@ Base.show(io::IO, χ::Character) = _print_char(io, χ) function _print_char(io::IO, χ::Character) first = true - for (i, c) in enumerate(constituents(χ)) + for (i, c) in enumerate(multiplicities(χ)) iszero(c) && continue first || print(io, " ") print(io, ((c < 0 || first) ? "" : '+')) diff --git a/src/matrix_projections.jl b/src/matrix_projections.jl index dacd8c2..f6133d3 100644 --- a/src/matrix_projections.jl +++ b/src/matrix_projections.jl @@ -110,7 +110,7 @@ _mproj_fitsT!(args...) = _mproj_outsT!(args...) function _mproj_outsT!(mproj::AbstractMatrix{T}, χ::Character) where {T} mproj .= sum( c .* matrix_projection_irr(Character(table(χ), i)) for - (i, c) in pairs(constituents(χ)) if !iszero(c) + (i, c) in pairs(multiplicities(χ)) if !iszero(c) ) return mproj end @@ -122,7 +122,7 @@ function _mproj_outsT!( ) where {T} mproj .= sum( c .* matrix_projection_irr(hom, Character(table(χ), i)) for - (i, c) in pairs(constituents(χ)) if !iszero(c) + (i, c) in pairs(multiplicities(χ)) if !iszero(c) ) return mproj end diff --git a/src/sa_basis.jl b/src/sa_basis.jl index 3ece7fd..0b93778 100644 --- a/src/sa_basis.jl +++ b/src/sa_basis.jl @@ -198,18 +198,18 @@ end function _constituents_decomposition(ψ::Character, tbl::CharacterTable) irr = irreducible_characters(tbl) degrees = degree.(irr) - multiplicities = constituents(ψ) + multips = multiplicities(ψ) @debug "Decomposition into character spaces: degrees: $(join([lpad(d, 6) for d in degrees], "")) - multiplicities: $(join([lpad(m, 6) for m in multiplicities], ""))" + multiplicities: $(join([lpad(m, 6) for m in multips], ""))" - @assert dot(multiplicities, degrees) == degree(ψ) + @assert dot(multips, degrees) == degree(ψ) "Something went wrong: characters do not constitute a complete basis for action: - $(dot(multiplicities, degrees)) ≠ $(degree(ψ))" + $(dot(multips, degrees)) ≠ $(degree(ψ))" - present_irreps = [i for (i, m) in enumerate(multiplicities) if m ≠ 0] - return irr[present_irreps], multiplicities[present_irreps] + present_irreps = [i for (i, m) in enumerate(multips) if m ≠ 0] + return irr[present_irreps], multips[present_irreps] end function _symmetry_adapted_basis( @@ -236,14 +236,14 @@ end function _symmetry_adapted_basis( T::Type, irr::AbstractVector{<:Character}, - multiplicities::AbstractVector{<:Integer}, + multips::AbstractVector{<:Integer}, RG::StarAlgebra{<:Group}, hom = nothing, ) mps, ranks = minimal_projection_system(irr, RG) degrees = degree.(irr) @debug "ranks of projections obtained by mps:" degrees - res = map(zip(mps, multiplicities, degrees, ranks)) do (µ, m, deg, r) + res = map(zip(mps, multips, degrees, ranks)) do (µ, m, deg, r) Threads.@spawn begin µT = eltype(µ) == T ? µ : AlgebraElement{T}(µ) # here we use algebra to compute the dimension of image; @@ -259,7 +259,7 @@ function _symmetry_adapted_basis( for (χ, ds) in zip(irr, direct_summands) if issimple(ds) && (d = size(ds, 1)) != - (e = multiplicity(ds) * sum(constituents(χ) .> 0)) + (e = multiplicity(ds) * sum(multiplicities(χ) .> 0)) throw( "The dimension of the projection doesn't match with simple summand multiplicity: $d ≠ $e", ) diff --git a/test/action_permutation.jl b/test/action_permutation.jl index 72eaeff..8b048c1 100644 --- a/test/action_permutation.jl +++ b/test/action_permutation.jl @@ -87,9 +87,9 @@ end @test typeof(SymbolicWedderburn.induce(ehom, one(G))) == Perm{UInt32} # the default ψ = SymbolicWedderburn.action_character(ehom, tbl) - @test SymbolicWedderburn.constituents(ψ) == [40, 22, 18] + @test SymbolicWedderburn.multiplicities(ψ) == [40, 22, 18] irr = SymbolicWedderburn.irreducible_characters(tbl) - multips = SymbolicWedderburn.constituents(ψ) + multips = SymbolicWedderburn.multiplicities(ψ) @test dot(SymbolicWedderburn.degree.(irr), multips) == length(words) simple = isone.(SymbolicWedderburn.degree.(irr)) @test simple == [false, true, true] diff --git a/test/characters.jl b/test/characters.jl index a0edbe2..f821042 100644 --- a/test/characters.jl +++ b/test/characters.jl @@ -23,8 +23,8 @@ @test Characters.table(ψ) === Characters.table(χ) @test collect(values(ψ)) == collect(values(χ)) - @test Characters.constituents(ψ) == Characters.constituents(χ) - @test Characters.constituents(ψ) !== Characters.constituents(χ) + @test Characters.multiplicities(ψ) == Characters.multiplicities(χ) + @test Characters.multiplicities(ψ) !== Characters.multiplicities(χ) @test hash(ψ) == hash(χ) From 187892e1f02233e82a0a0d08ef9ee87e64b1f749 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 28 Nov 2023 13:31:53 +0100 Subject: [PATCH 02/10] add multiplication of characters --- src/action_characters.jl | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/action_characters.jl b/src/action_characters.jl index 17721b7..21fb840 100644 --- a/src/action_characters.jl +++ b/src/action_characters.jl @@ -63,8 +63,8 @@ function action_character( tbl::CharacterTable, ) where {T} ac_char = _action_class_fun(conjugacy_clss) - constituents = Int[dot(ac_char, χ) for χ in irreducible_characters(tbl)] - return Character{T}(tbl, constituents) + vals = Characters.decompose(Int, ac_char, tbl) + return Character{T}(tbl, vals) end function action_character( @@ -73,14 +73,8 @@ function action_character( tbl::CharacterTable, ) where {T<:Union{AbstractFloat,ComplexF64}} ac_char = _action_class_fun(conjugacy_cls) - - constituents = [dot(ac_char, χ) for χ in irreducible_characters(tbl)] - all(constituents) do c - ac = abs(c) - return abs(ac - round(ac)) < eps(real(T)) * length(conjugacy_cls) - end - - return Character{T}(tbl, round.(Int, abs.(constituents))) + vals = Characters.decompose(T, ac_char, tbl) + return Character{T}(tbl, round.(Int, abs.(vals))) end """ @@ -100,8 +94,7 @@ function action_character( hom::InducedActionHomomorphism, tbl::CharacterTable, ) where {T} - ac_char = _action_class_fun(hom, conjugacy_classes(tbl)) - vals = [dot(ac_char, χ) for χ in irreducible_characters(T, tbl)] - constituents = convert(Vector{Int}, vals) - return Character{T}(tbl, constituents) + act_character = _action_class_fun(hom, conjugacy_classes(tbl)) + vals = Characters.decompose(Int, act_character, tbl) + return Character{T}(tbl, vals) end From 02440781e95a7e0d8011e58abe51b4c7f389b598 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 28 Nov 2023 14:01:23 +0100 Subject: [PATCH 03/10] sort the character table to make it more deterministic --- src/Characters/character_tables.jl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Characters/character_tables.jl b/src/Characters/character_tables.jl index 85c5510..57aa5e1 100644 --- a/src/Characters/character_tables.jl +++ b/src/Characters/character_tables.jl @@ -58,6 +58,10 @@ function CharacterTable( G::Group, cclasses = conjugacy_classes(G), ) + # make sure that the first class contains the indentity + k = findfirst(cl -> one(G) in cl, cclasses) + cclasses[k], cclasses[1] = cclasses[1], cclasses[k] + Ns = [CMMatrix(cclasses, i) for i in 1:length(cclasses)] esd = common_esd(Ns, Fp) @assert isdiag(esd) @@ -71,6 +75,17 @@ function CharacterTable( ) tbl = normalize!(tbl) + + let vals = tbl.values + # make order of characters deterministic + vals .= sortslices(vals; dims = 1) + # and that the trivial character is first + k = findfirst(r -> all(isone, r), eachrow(vals)) + if k ≠ 1 + _swap_rows!(vals, 1, k) + end + end + return tbl end From 9d711c84b74a0b1da984698802512d79b4ad2de6 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 28 Nov 2023 14:01:48 +0100 Subject: [PATCH 04/10] multithread the normalization of CharacterTable --- src/Characters/character_tables.jl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Characters/character_tables.jl b/src/Characters/character_tables.jl index 57aa5e1..1ce50bd 100644 --- a/src/Characters/character_tables.jl +++ b/src/Characters/character_tables.jl @@ -146,7 +146,8 @@ end function normalize!(chtbl::CharacterTable{<:Group,<:FiniteFields.GF}) id = one(parent(chtbl)) - for (i, χ) in enumerate(irreducible_characters(chtbl)) + Threads.@threads for i in axes(chtbl, 1) + χ = Character(chtbl, i) k = χ(id) if !isone(k) chtbl.values[i, :] .*= inv(k) From 85b88462466927fc24b71101853b9f7eee8a4db2 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 28 Nov 2023 16:48:25 +0100 Subject: [PATCH 05/10] fix remaining tests due to change in ordering of chars --- test/action_permutation.jl | 22 +++++++++++----------- test/ccmatrix.jl | 2 +- test/characters.jl | 4 ++-- test/dixon.jl | 21 ++++++++++----------- test/sa_basis.jl | 10 +++++----- 5 files changed, 29 insertions(+), 30 deletions(-) diff --git a/test/action_permutation.jl b/test/action_permutation.jl index 8b048c1..accd24a 100644 --- a/test/action_permutation.jl +++ b/test/action_permutation.jl @@ -87,12 +87,12 @@ end @test typeof(SymbolicWedderburn.induce(ehom, one(G))) == Perm{UInt32} # the default ψ = SymbolicWedderburn.action_character(ehom, tbl) - @test SymbolicWedderburn.multiplicities(ψ) == [40, 22, 18] + @test SymbolicWedderburn.multiplicities(ψ) == [22, 18, 40] irr = SymbolicWedderburn.irreducible_characters(tbl) multips = SymbolicWedderburn.multiplicities(ψ) @test dot(SymbolicWedderburn.degree.(irr), multips) == length(words) simple = isone.(SymbolicWedderburn.degree.(irr)) - @test simple == [false, true, true] + @test simple == [true, true, false] inv_vec = SymbolicWedderburn.invariant_vectors( tbl, @@ -106,19 +106,19 @@ end let i = 1 χ, m, s = irr[i], multips[i], simple[i] b = SymbolicWedderburn.image_basis(ehom, χ) - @test size(b, 1) == SymbolicWedderburn.degree(χ) * m == 80 + @test size(b, 1) == SymbolicWedderburn.degree(χ) * m == 22 end let i = 2 χ, m, s = irr[i], multips[i], simple[i] b = SymbolicWedderburn.image_basis(ehom, χ) - @test size(b, 1) == SymbolicWedderburn.degree(χ) * m == 22 + @test size(b, 1) == SymbolicWedderburn.degree(χ) * m == 18 end let i = 3 χ, m, s = irr[i], multips[i], simple[i] b = SymbolicWedderburn.image_basis(ehom, χ) - @test size(b, 1) == SymbolicWedderburn.degree(χ) * m == 18 + @test size(b, 1) == SymbolicWedderburn.degree(χ) * m == 80 end @test symmetry_adapted_basis(G, action, words; semisimple = true) isa @@ -146,11 +146,11 @@ end @test [convert(Matrix{Float64}, b) for b in sa_basis] isa Vector{Matrix{Float64}} @test length(sa_basis) == 3 - @test multiplicity.(sa_basis) == [40, 22, 18] - @test SymbolicWedderburn.degree.(sa_basis) == [2, 1, 1] + @test multiplicity.(sa_basis) == [22, 18, 40] + @test SymbolicWedderburn.degree.(sa_basis) == [1, 1, 2] @test size.(sa_basis, 1) == multips .* SymbolicWedderburn.degree.(irr) == - [80, 22, 18] + [22, 18, 80] @test sum(first ∘ size, sa_basis) == length(words) end @@ -222,9 +222,9 @@ end sa_basis = symmetry_adapted_basis(G, action, words) @test length(sa_basis) == 3 - @test multiplicity.(sa_basis) == [40, 22, 18] - @test SymbolicWedderburn.degree.(sa_basis) == [2, 1, 1] + @test multiplicity.(sa_basis) == [22, 18, 40] + @test SymbolicWedderburn.degree.(sa_basis) == [1, 1, 2] @test all(issimple, sa_basis) - @test size.(sa_basis, 1) == multips == [40, 22, 18] + @test size.(sa_basis, 1) == multips == [22, 18, 40] end end diff --git a/test/ccmatrix.jl b/test/ccmatrix.jl index 8422d4d..401750a 100644 --- a/test/ccmatrix.jl +++ b/test/ccmatrix.jl @@ -147,7 +147,7 @@ end end @testset "random subgroups of SymetricGroup(N)" begin - for i in 2:6 + for i in 3:6 G = if i == 2 PermGroup(perm"(1,2)") else diff --git a/test/characters.jl b/test/characters.jl index f821042..f089618 100644 --- a/test/characters.jl +++ b/test/characters.jl @@ -1,9 +1,9 @@ @testset "Frobenius-Schur & projections" begin G = SmallPermGroups[8][4] - irr = Characters.irreducible_characters(G) + tbl = Characters.CharacterTable(Rational{Int}, G) # quaternionic character - χ = irr[1] + χ = Characters.Character{Rational{Int}}(tbl, 5) @test collect(values(χ)) == [2, 0, -2, 0, 0] ι = Characters.frobenius_schur diff --git a/test/dixon.jl b/test/dixon.jl index 133b75d..c95156b 100644 --- a/test/dixon.jl +++ b/test/dixon.jl @@ -119,10 +119,9 @@ end tbl = Characters.CharacterTable(F, G) chars = Characters.irreducible_characters(tbl) - @test sort(SymbolicWedderburn.degree.(chars)) == [1, 1, 1, 3] + @test SymbolicWedderburn.degree.(chars) == [1, 1, 1, 3] - @test Set(Int.(values(χ)) for χ in chars) == - Set([[3, 0, 0, 6], [1, 4, 2, 1], [1, 2, 4, 1], [1, 1, 1, 1]]) + @test [Int.(values(χ)) for χ in chars] == [[1, 1, 1, 1], [1, 2, 4, 1], [1, 4, 2, 1], [3, 0, 0, 6]] end end @@ -157,10 +156,10 @@ end E = Characters.Cyclotomics.E @test [collect(values(χ)) for χ in chars_C] == [ - E(3, 0) .* [3, 0, 0, -1], - E(3, 0) .* [1, E(3, 2), E(3, 1), 1], - E(3, 0) .* [1, E(3, 1), E(3, 2), 1], E(3, 0) .* [1, 1, 1, 1], + E(3, 0) .* [1, E(3, 1), E(3, 2), 1], + E(3, 0) .* [1, E(3, 2), E(3, 1), 1], + E(3, 0) .* [3, 0, 0, -1], ] end @@ -180,12 +179,12 @@ end chars = Characters.irreducible_characters(G, ccG) - @test sort(SymbolicWedderburn.degree.(chars)) == [1, 1, 2, 3, 3] + @test SymbolicWedderburn.degree.(chars) == [1, 1, 2, 3, 3] @test [collect(values(χ)) for χ in chars] == [ - [2, 0, 2, -1, 0], - [3, 1, -1, 0, -1], [1, 1, 1, 1, 1], [1, -1, 1, 1, -1], + [2, 0, 2, -1, 0], + [3, 1, -1, 0, -1], [3, -1, -1, 0, 1], ] end @@ -209,11 +208,11 @@ end @test sort(SymbolicWedderburn.degree.(chars)) == [1, 1, 1, 1, 4] @test [collect(values(χ)) for χ in chars] == [ - E(4, 0) .* [4, 0, 0, 0, -1], E(4, 0) .* [1, 1, 1, 1, 1], E(4, 0) .* [1, 1, -1, -1, 1], - E(4, 0) .* [1, -1, E(4), -E(4), 1], E(4, 0) .* [1, -1, -E(4), E(4), 1], + E(4, 0) .* [1, -1, E(4), -E(4), 1], + E(4, 0) .* [4, 0, 0, 0, -1], ] end end diff --git a/test/sa_basis.jl b/test/sa_basis.jl index 6de76b0..ee9101c 100644 --- a/test/sa_basis.jl +++ b/test/sa_basis.jl @@ -29,7 +29,7 @@ end irr = SymbolicWedderburn.irreducible_characters(G) @test irr isa AbstractVector{<:SymbolicWedderburn.Character{<:Cyclotomic}} - @test SymbolicWedderburn.degree.(irr) == [2, 1, 1] + @test SymbolicWedderburn.degree.(irr) == [1, 1, 2] RG = let G = G b = StarAlgebras.Basis{UInt16}(collect(G)) @@ -48,8 +48,8 @@ end mps, ranks = SymbolicWedderburn.minimal_projection_system(irr, RG) @test all(isone, ranks) - @test rank(float.(SymbolicWedderburn.matrix_projection(irr[1]))) == 2 - @test rank(float.(SymbolicWedderburn.matrix_representation(mps[1]))) == + @test rank(float.(SymbolicWedderburn.matrix_projection(irr[3]))) == 2 + @test rank(float.(SymbolicWedderburn.matrix_representation(mps[3]))) == 1 sa_basis_ssimple = symmetry_adapted_basis( @@ -59,8 +59,8 @@ end semisimple = true, ) - @test issimple.(sa_basis_ssimple) == [false, true] - @test rank.(convert.(Matrix, sa_basis_ssimple)) == [2, 1] + @test issimple.(sa_basis_ssimple) == [true, false] + @test rank.(convert.(Matrix, sa_basis_ssimple)) == [1, 2] @test dot( multiplicity.(sa_basis_ssimple), SymbolicWedderburn.degree.(sa_basis_ssimple), From e509a9eaf3e263e2df3bb3649e78085857f19e13 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 28 Nov 2023 19:05:05 +0100 Subject: [PATCH 06/10] fix deepcopy for Characters --- src/Characters/class_functions.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Characters/class_functions.jl b/src/Characters/class_functions.jl index f87327c..b04f0b3 100644 --- a/src/Characters/class_functions.jl +++ b/src/Characters/class_functions.jl @@ -136,8 +136,9 @@ function Base.:(==)(χ::Character, ψ::Character) end Base.hash(χ::Character, h::UInt) = hash(table(χ), hash(multiplicities(χ), h)) -function Base.deepcopy_internal(χ::Character, ::IdDict) - return Character(table(χ), copy(multiplicities(χ))) +function Base.deepcopy_internal(χ::Character{T}, d::IdDict) where {T} + haskey(d, χ) && return d[χ] + return Character{T}(table(χ), copy(multiplicities(χ))) end ## Character arithmetic From ace63bc7cad1d220bea425123762f7d9a6382840 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Tue, 28 Nov 2023 19:05:39 +0100 Subject: [PATCH 07/10] use scs in tests (csdp is sloooooow) --- examples/ex_S4.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/ex_S4.jl b/examples/ex_S4.jl index cbe008e..82b2898 100644 --- a/examples/ex_S4.jl +++ b/examples/ex_S4.jl @@ -10,7 +10,7 @@ include(joinpath(@__DIR__, "solver.jl")) const N = 4 @polyvar x[1:N] -OPTIMIZER = csdp_optimizer(; max_iters = 2_000, accel = 10, eps = 1e-7) +OPTIMIZER = scs_optimizer(; max_iters = 2_000, accel = 10, eps = 1e-7) f = 1 + From 6ccd119cc74940092969674e0afda46fe006f53f Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 29 Nov 2023 00:29:08 +0100 Subject: [PATCH 08/10] simplify trivial_character, now that we know it comes first --- src/Characters/character_tables.jl | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Characters/character_tables.jl b/src/Characters/character_tables.jl index 1ce50bd..20873ee 100644 --- a/src/Characters/character_tables.jl +++ b/src/Characters/character_tables.jl @@ -24,11 +24,13 @@ nirreps(chtbl::CharacterTable) = size(chtbl.values, 1) ## irreps function irreducible_characters(chtbl::CharacterTable) - return [Character(chtbl, i) for i in 1:size(chtbl, 1)] + return irreducible_characters(eltype(chtbl), chtbl) end + function irreducible_characters(T::Type, chtbl::CharacterTable) - return Character{T}[Character{T}(chtbl, i) for i in 1:size(chtbl, 1)] + return [Character{T}(chtbl, i) for i in axes(chtbl, 1)] end + function irreducible_characters(G::Group, cclasses = conjugacy_classes(G)) return irreducible_characters(Rational{Int}, G, cclasses) end @@ -41,18 +43,14 @@ function irreducible_characters( return irreducible_characters(CharacterTable(R, G, cclasses)) end -function trivial_character(chtbl::CharacterTable) - # return Character(chtbl, findfirst(r->all(isone, r), eachrow(chtbl))) - # can't use findfirst(f, eachrow(...)) on julia-1.6 - for i in 1:size(chtbl, 1) - all(isone, @view(chtbl[i, :])) && return Character(chtbl, i) - end - # never hit, to keep compiler happy - return Character(chtbl, 0) -end +trivial_character(chtbl::CharacterTable) = Character(chtbl, 1) ## construcing tables +function CharacterTable(G::Group, cclasses = conjugacy_classes(G)) + return CharacterTable(Rational{Int}, G, cclasses) +end + function CharacterTable( Fp::Type{<:FiniteFields.GF}, G::Group, From 26fe52edb3f5cd5ad71d52eb53bd9331470a3a7e Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 29 Nov 2023 00:37:46 +0100 Subject: [PATCH 09/10] workaround findfirst problems on julia-1.6 --- src/Characters/character_tables.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Characters/character_tables.jl b/src/Characters/character_tables.jl index 20873ee..fb3a437 100644 --- a/src/Characters/character_tables.jl +++ b/src/Characters/character_tables.jl @@ -78,7 +78,9 @@ function CharacterTable( # make order of characters deterministic vals .= sortslices(vals; dims = 1) # and that the trivial character is first - k = findfirst(r -> all(isone, r), eachrow(vals)) + k = findfirst(i -> all(isone, @views vals[i, :]), axes(vals, 1)) + # doesn't work on julia-1.6 + # k = findfirst(r -> all(isone, r), eachrow(vals)) if k ≠ 1 _swap_rows!(vals, 1, k) end From 2847813b98bbe600dbb6cdf9abb87d605fe7403e Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Wed, 29 Nov 2023 10:16:59 +0100 Subject: [PATCH 10/10] add tests for characters multiplication --- src/Characters/class_functions.jl | 2 ++ test/characters.jl | 43 +++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/src/Characters/class_functions.jl b/src/Characters/class_functions.jl index b04f0b3..307d8db 100644 --- a/src/Characters/class_functions.jl +++ b/src/Characters/class_functions.jl @@ -193,6 +193,8 @@ function Base.:*(χ::Character, ψ::Character) return Character(table(χ), __decompose(Int, values, table(χ))) end +Base.:^(χ::Character, n::Integer) = Base.power_by_squaring(χ, n) + ## Group-theoretic functions: PermutationGroups.degree(χ::Character) = Int(χ(one(parent(χ)))) diff --git a/test/characters.jl b/test/characters.jl index f089618..74f0cff 100644 --- a/test/characters.jl +++ b/test/characters.jl @@ -46,6 +46,49 @@ @test zero(χ) == Characters.Character(Characters.table(χ), zeros(5)) @test dot(χ, zero(χ)) == 0 @test dot(zero(χ), zero(χ)) == 0 + + @testset "characters multiplication: Sym(3)" begin + G = PermGroup(perm"(1,2,3)", perm"(1,2)") + tbl = Characters.CharacterTable(G) + χ = Characters.irreducible_characters(tbl) + + # χ[1] - the trivial character + # χ[2] - the alternating character + # χ[3] - the non-trivial, degree-2 character + + @test all(χ[1] * ψ == ψ for ψ in χ) + @test χ[2]^2 == χ[1] + @test χ[2] * χ[3] == χ[3] * χ[2] == χ[3] + + @test χ[3]^2 == χ[1] + χ[2] + χ[3] + end + + @testset "characters multiplication: Sym(4)" begin + G = PermGroup(perm"(1,2,3,4)", perm"(1,2)") + tbl = Characters.CharacterTable(G) + χ = Characters.irreducible_characters(tbl) + + # χ[1] - the trivial character + # χ[2] - the alternating character + # χ[3] - the non-trivial, degree-2 character + # χ[4] - the non-trivial, degree-3 character + # χ[5] - χ[4]*χ[2] + + @test all(χ[1] * ψ == ψ for ψ in χ) + @test χ[2]^2 == χ[1] + @test χ[2] * χ[3] == χ[3] + @test χ[2] * χ[4] == χ[5] + @test χ[2] * χ[5] == χ[4] + + @test χ[3]^2 == χ[1] + χ[2] + χ[3] + @test χ[3] * χ[4] == χ[4] + χ[5] + @test χ[3] * χ[5] == χ[4] + χ[5] + + @test χ[4]^2 == χ[1] + χ[3] + χ[4] + χ[5] + @test χ[4] * χ[5] == χ[2] + χ[3] + χ[4] + χ[5] + + @test χ[5]^2 == χ[1] + χ[3] + χ[4] + χ[5] + end end @testset "Characters io" begin