From adb42c89194fb21f261306decd29bb61924fd6e1 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 14:17:40 +0200 Subject: [PATCH 01/14] move autFn and mcg to aut_groups folder --- src/Groups.jl | 5 +++-- src/{groups => aut_groups}/gersten_relations.jl | 0 src/{groups => aut_groups}/mcg.jl | 0 src/{groups => aut_groups}/sautFn.jl | 0 src/{groups => aut_groups}/symplectic_twists.jl | 0 src/{groups => aut_groups}/transvections.jl | 0 6 files changed, 3 insertions(+), 2 deletions(-) rename src/{groups => aut_groups}/gersten_relations.jl (100%) rename src/{groups => aut_groups}/mcg.jl (100%) rename src/{groups => aut_groups}/sautFn.jl (100%) rename src/{groups => aut_groups}/symplectic_twists.jl (100%) rename src/{groups => aut_groups}/transvections.jl (100%) diff --git a/src/Groups.jl b/src/Groups.jl index a2275e8..c662566 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -17,8 +17,9 @@ include("hashing.jl") include("normalform.jl") include("autgroups.jl") -include("groups/sautFn.jl") -include("groups/mcg.jl") +include("aut_groups/sautFn.jl") +include("aut_groups/mcg.jl") + include("wl_ball.jl") end # of module Groups diff --git a/src/groups/gersten_relations.jl b/src/aut_groups/gersten_relations.jl similarity index 100% rename from src/groups/gersten_relations.jl rename to src/aut_groups/gersten_relations.jl diff --git a/src/groups/mcg.jl b/src/aut_groups/mcg.jl similarity index 100% rename from src/groups/mcg.jl rename to src/aut_groups/mcg.jl diff --git a/src/groups/sautFn.jl b/src/aut_groups/sautFn.jl similarity index 100% rename from src/groups/sautFn.jl rename to src/aut_groups/sautFn.jl diff --git a/src/groups/symplectic_twists.jl b/src/aut_groups/symplectic_twists.jl similarity index 100% rename from src/groups/symplectic_twists.jl rename to src/aut_groups/symplectic_twists.jl diff --git a/src/groups/transvections.jl b/src/aut_groups/transvections.jl similarity index 100% rename from src/groups/transvections.jl rename to src/aut_groups/transvections.jl From db5d60134c09ea0c254242ba1c89c624239185f4 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 14:18:53 +0200 Subject: [PATCH 02/14] replace ThreadsX by Folds --- Project.toml | 4 ++-- src/Groups.jl | 2 +- src/wl_ball.jl | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Project.toml b/Project.toml index d9acf6a..3b50d0d 100644 --- a/Project.toml +++ b/Project.toml @@ -4,19 +4,19 @@ authors = ["Marek Kaluba "] version = "0.7.2" [deps] +Folds = "41a02a25-b8f0-4f67-bc48-60067656b558" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a" OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d" [compat] AbstractAlgebra = "0.22" +Folds = "0.2.7" GroupsCore = "0.4" KnuthBendix = "0.3" OrderedCollections = "1" PermutationGroups = "0.3" -ThreadsX = "0.1" julia = "1.3" [extras] diff --git a/src/Groups.jl b/src/Groups.jl index c662566..cc7ec85 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -1,7 +1,7 @@ module Groups using GroupsCore -using ThreadsX +using Folds import KnuthBendix import KnuthBendix: AbstractWord, Alphabet, Word import KnuthBendix: alphabet diff --git a/src/wl_ball.jl b/src/wl_ball.jl index ebb370c..69fce79 100644 --- a/src/wl_ball.jl +++ b/src/wl_ball.jl @@ -8,14 +8,14 @@ radius and multiplication operation to be used. """ function wlmetric_ball_serial(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T} @assert radius >= 1 - old = unique!([center, [center*s for s in S]...]) + old = union!([center], [center*s for s in S]) return _wlmetric_ball(S, old, radius, op, collect, unique!) end function wlmetric_ball_thr(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T} @assert radius >= 1 - old = unique!([center, [center*s for s in S]...]) - return _wlmetric_ball(S, old, radius, op, ThreadsX.collect, ThreadsX.unique) + 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) From cb933c3f872fe5e70f0424cdf38a3def0dcc7abe Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 14:21:42 +0200 Subject: [PATCH 03/14] add implementation of MatrixGroups as fp groups --- src/Groups.jl | 4 ++ src/matrix_groups/MatrixGroups.jl | 17 ++++++ src/matrix_groups/SLn.jl | 42 +++++++++++++ src/matrix_groups/Spn.jl | 68 +++++++++++++++++++++ src/matrix_groups/abstract.jl | 41 +++++++++++++ src/matrix_groups/eltary_matrices.jl | 31 ++++++++++ src/matrix_groups/eltary_symplectic.jl | 85 ++++++++++++++++++++++++++ test/matrix_groups.jl | 24 ++++++++ test/runtests.jl | 2 + 9 files changed, 314 insertions(+) create mode 100644 src/matrix_groups/MatrixGroups.jl create mode 100644 src/matrix_groups/SLn.jl create mode 100644 src/matrix_groups/Spn.jl create mode 100644 src/matrix_groups/abstract.jl create mode 100644 src/matrix_groups/eltary_matrices.jl create mode 100644 src/matrix_groups/eltary_symplectic.jl create mode 100644 test/matrix_groups.jl diff --git a/src/Groups.jl b/src/Groups.jl index cc7ec85..5a2755d 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -10,6 +10,8 @@ import Random import OrderedCollections: OrderedSet export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElement, SpecialAutomorphismGroup +export MatrixGroups + export alphabet, evaluate, word, gens include("types.jl") @@ -20,6 +22,8 @@ include("autgroups.jl") include("aut_groups/sautFn.jl") include("aut_groups/mcg.jl") +include("matrix_groups/MatrixGroups.jl") +using .MatrixGroups include("wl_ball.jl") end # of module Groups diff --git a/src/matrix_groups/MatrixGroups.jl b/src/matrix_groups/MatrixGroups.jl new file mode 100644 index 0000000..4a5bb93 --- /dev/null +++ b/src/matrix_groups/MatrixGroups.jl @@ -0,0 +1,17 @@ +module MatrixGroups + +using GroupsCore +using Groups +using KnuthBendix + +using LinearAlgebra # Identity matrix +using Random # GroupsCore rand + +export SpecialLinearGroup, SymplecticGroup + +include("abstract.jl") + +include("SLn.jl") +include("Spn.jl") + +end # module diff --git a/src/matrix_groups/SLn.jl b/src/matrix_groups/SLn.jl new file mode 100644 index 0000000..c4a1f04 --- /dev/null +++ b/src/matrix_groups/SLn.jl @@ -0,0 +1,42 @@ +include("eltary_matrices.jl") + +struct SpecialLinearGroup{N, T, R, A, S} <: MatrixGroup{N,T} + base_ring::R + alphabet::A + gens::S + + function SpecialLinearGroup{N}(base_ring) where N + S = [ElementaryMatrix{N}(i,j, one(base_ring)) for i in 1:N for j in 1:N if i≠j] + alphabet = Alphabet(S) + + return new{ + N, + eltype(base_ring), + typeof(base_ring), + typeof(alphabet), + typeof(S) + }(base_ring, alphabet, S) + end +end + +GroupsCore.ngens(SL::SpecialLinearGroup{N}) where N = N^2 - N + +Base.show(io::IO, SL::SpecialLinearGroup{N, T}) where {N, T} = + print(io, "special linear group of $N×$N matrices over $T") + +function Base.show( + io::IO, + ::MIME"text/plain", + sl::Groups.AbstractFPGroupElement{<:SpecialLinearGroup{N}} +) where N + + Groups.normalform!(sl) + + print(io, "SL{$N,$(eltype(sl))} matrix: ") + KnuthBendix.print_repr(io, word(sl), alphabet(sl)) + println(io) + Base.print_array(io, matrix_repr(sl)) +end + +Base.show(io::IO, sl::Groups.AbstractFPGroupElement{<:SpecialLinearGroup}) = + KnuthBendix.print_repr(io, word(sl), alphabet(sl)) diff --git a/src/matrix_groups/Spn.jl b/src/matrix_groups/Spn.jl new file mode 100644 index 0000000..66e658c --- /dev/null +++ b/src/matrix_groups/Spn.jl @@ -0,0 +1,68 @@ +include("eltary_symplectic.jl") + +struct SymplecticGroup{N, T, R, A, S} <: MatrixGroup{N,T} + base_ring::R + alphabet::A + gens::S + + function SymplecticGroup{N}(base_ring) where N + S = symplectic_gens(N, eltype(base_ring)) + alphabet = Alphabet(S) + + return new{ + N, + eltype(base_ring), + typeof(base_ring), + typeof(alphabet), + typeof(S) + }(base_ring, alphabet, S) + end +end + +GroupsCore.ngens(Sp::SymplecticGroup) = length(Sp.gens) + +Base.show(io::IO, ::SymplecticGroup{N}) where N = print(io, "group of $N×$N symplectic matrices") + +function Base.show( + io::IO, + ::MIME"text/plain", + sp::Groups.AbstractFPGroupElement{<:SymplecticGroup{N}} +) where {N} + print(io, "$N×$N Symplectic matrix: ") + KnuthBendix.print_repr(io, word(sp), alphabet(sp)) + println(io) + Base.print_array(io, matrix_repr(sp)) +end + +function symplectic_gens(N, T=Int8) + iseven(N) || throw(ArgumentError("N needs to be even!")) + n = N÷2 + + a_ijs = [ElementarySymplectic{N}(:A, i,j, one(T)) for (i,j) in offdiagonal_indexing(n)] + b_is = [ElementarySymplectic{N}(:B, n+i,i, one(T)) for i in 1:n] + c_ijs = [ElementarySymplectic{N}(:B, n+i,j, one(T)) for (i,j) in offdiagonal_indexing(n)] + + S = [a_ijs; b_is; c_ijs] + + S = [S; transpose.(S)] + + return unique(S) +end + +function _std_symplectic_form(m::AbstractMatrix) + r,c = size(m) + r == c || return false + iseven(r) || return false + + n = r÷2 + Ω = zero(m) + for i in 1:n + Ω[2i-1:2i, 2i-1:2i] .= [0 -1; 1 0] + end + return Ω +end + +function issymplectic(mat::M, Ω = _std_symplectic_form(mat)) where M <: AbstractMatrix + r, c = size(mat) + return Ω == transpose(mat) * Ω * mat +end diff --git a/src/matrix_groups/abstract.jl b/src/matrix_groups/abstract.jl new file mode 100644 index 0000000..fd70c41 --- /dev/null +++ b/src/matrix_groups/abstract.jl @@ -0,0 +1,41 @@ +abstract type MatrixGroup{N, T} <: Groups.AbstractFPGroup end +const MatrixGroupElement{N, T} = Groups.AbstractFPGroupElement{<:MatrixGroup{N, T}} + +Base.isone(g::MatrixGroupElement{N, T}) where {N, T} = matrix_repr(g) == I + +function Base.:(==)(m1::M1, m2::M2) where {M1<:MatrixGroupElement, M2<:MatrixGroupElement} + parent(m1) === parent(m2) || return false + word(m1) == word(m2) && return true + return matrix_repr(m1) == matrix_repr(m2) +end + +Base.size(m::MatrixGroupElement{N}) where N = (N, N) +Base.eltype(m::MatrixGroupElement{N, T}) where {N, T} = T + +# three structural assumptions about matrix groups +Groups.word(sl::MatrixGroupElement) = sl.word +Base.parent(sl::MatrixGroupElement) = sl.parent +Groups.alphabet(M::MatrixGroup) = M.alphabet +Groups.rewriting(M::MatrixGroup) = alphabet(M) + +Base.hash(sl::MatrixGroupElement, h::UInt) = + hash(matrix_repr(sl), hash(parent(sl), h)) + +Base.getindex(sl::MatrixGroupElement, i, j) = matrix_repr(sl)[i,j] +# Base.iterate(sl::MatrixGroupElement) = iterate(sl.elts) +# Base.iterate(sl::MatrixGroupElement, state) = iterate(sl.elts, state) + +function matrix_repr(m::MatrixGroupElement{N, T}) where {N, T} + isempty(word(m)) && return StaticArrays.SMatrix{N, N, T}(I) + A = alphabet(parent(m)) + return prod(matrix_repr(A[l]) for l in word(m)) +end + +function Base.rand( + rng::Random.AbstractRNG, + rs::Random.SamplerTrivial{<:MatrixGroup}, + ) + Mgroup = rs[] + S = gens(Mgroup) + return prod(g -> rand(Bool) ? g : inv(g), rand(S, rand(1:30))) +end diff --git a/src/matrix_groups/eltary_matrices.jl b/src/matrix_groups/eltary_matrices.jl new file mode 100644 index 0000000..f78fdff --- /dev/null +++ b/src/matrix_groups/eltary_matrices.jl @@ -0,0 +1,31 @@ +using Groups +using StaticArrays + +struct ElementaryMatrix{N, T} <: Groups.GSymbol + i::Int + j::Int + val::T + ElementaryMatrix{N}(i, j, val=1) where N = + (@assert i≠j; new{N, typeof(val)}(i, j, val)) +end + +function Base.show(io::IO, e::ElementaryMatrix) + print(io, 'E', Groups.subscriptify(e.i), Groups.subscriptify(e.j)) + !isone(e.val) && print(io, "^$(e.val)") +end + +Base.:(==)(e::ElementaryMatrix{N}, f::ElementaryMatrix{N}) where N = + e.i == f.i && e.j == f.j && e.val == f.val + +Base.hash(e::ElementaryMatrix, h::UInt) = + hash(typeof(e), hash((e.i, e.j, e.val), h)) + +Base.inv(e::ElementaryMatrix{N}) where N = + ElementaryMatrix{N}(e.i, e.j, -e.val) + +function matrix_repr(e::ElementaryMatrix{N, T}) where {N, T} + m = StaticArrays.MMatrix{N, N, T}(I) + m[e.i, e.j] = e.val + x = StaticArrays.SMatrix{N, N}(m) + return x +end diff --git a/src/matrix_groups/eltary_symplectic.jl b/src/matrix_groups/eltary_symplectic.jl new file mode 100644 index 0000000..413fcea --- /dev/null +++ b/src/matrix_groups/eltary_symplectic.jl @@ -0,0 +1,85 @@ +using Groups +using StaticArrays + +struct ElementarySymplectic{N, T} <: Groups.GSymbol + symbol::Symbol + i::Int + j::Int + val::T + function ElementarySymplectic{N}(s::Symbol, i::Integer, j::Integer, val=1) where N + @assert s ∈ (:A, :B) + @assert iseven(N) + n = N÷2 + if s === :A + @assert 1 ≤ i ≤ n && 1 ≤ j ≤ n && i ≠ j + elseif s === :B + @assert xor(1 ≤ i ≤ n, 1 ≤ j ≤ n) && xor(n < i ≤ N, n < j ≤ N) + end + return new{N, typeof(val)}(s, i, j, val) + end +end + +function Base.show(io::IO, s::ElementarySymplectic) + i, j = Groups.subscriptify(s.i), Groups.subscriptify(s.j) + print(io, s.symbol, i, j) + !isone(s.val) && print(io, "^$(s.val)") +end + +function Base.show(io::IO, ::MIME"text/plain", s::ElementarySymplectic) + print(io, s) + print(io, " → corresponding root: ") + print(io, Roots.Root(s)) +end + +_ind(s::ElementarySymplectic{N}) where N = (s.i, s.j) +_local_ind(N_half::Integer, i::Integer) = ifelse(i<=N_half, i, i-N_half) +function _dual_ind(s::ElementarySymplectic{N}) where N + if s.symbol === :A && return _ind(s) + else#if s.symbol === :B + return _dual_ind(N÷2, s.i, s.j) + end +end + +function _dual_ind(N_half, i, j) + @assert i <= N_half < j || j <= N_half < i + if i <= N_half # && j > N_half + i, j = j - N_half, i + N_half + else + i, j = j + N_half, i - N_half + end + return i, j +end + +function Base.:(==)(s::ElementarySymplectic{N}, t::ElementarySymplectic{M}) where {N, M} + N == M || return false + s.symbol == t.symbol || return false + s.val == t.val || return false + return _ind(t) == _ind(s) || _ind(t) == _dual_ind(s) +end + +Base.hash(s::ElementarySymplectic, h::UInt) = + hash(Set([_ind(s); _dual_ind(s)]), hash(s.symbol, hash(s.val, h))) + +LinearAlgebra.transpose(s::ElementarySymplectic{N}) where N = + ElementarySymplectic{N}(s.symbol, s.j, s.i, s.val) + +Base.inv(s::ElementarySymplectic{N}) where N = + ElementarySymplectic{N}(s.symbol, s.i, s.j, -s.val) + +function matrix_repr(s::ElementarySymplectic{N, T}) where {N, T} + @assert iseven(N) + n = div(N, 2) + m = StaticArrays.MMatrix{N, N, T}(I) + i,j = _ind(s) + m[i,j] = s.val + if s.symbol === :A + m[n+j, n+i] = -s.val + else#if s.symbol === :B + if i > n + m[j+n, i-n] = s.val + else + m[j-n, i+n] = s.val + end + end + return StaticArrays.SMatrix{N, N}(m) +end diff --git a/test/matrix_groups.jl b/test/matrix_groups.jl new file mode 100644 index 0000000..853ab13 --- /dev/null +++ b/test/matrix_groups.jl @@ -0,0 +1,24 @@ +using Groups.MatrixGroups + +@testset "Matrix Groups" begin + + @testset "SL(n, ℤ)" begin + SL3Z = SpecialLinearGroup{3}(Int8) + + S = gens(SL3Z); union!(S, inv.(S)) + + E, sizes = Groups.wlmetric_ball(S, radius=4) + + @test sizes = [13, 121, 883, 5455] + + @testset "GroupsCore conformance" begin + test_Group_interface(SL3Z) + g = A(rand(1:length(alphabet(SL3Z)), 10)) + h = A(rand(1:length(alphabet(SL3Z)), 10)) + + test_GroupElement_interface(g, h) + end + + end + +end diff --git a/test/runtests.jl b/test/runtests.jl index c0334c4..be4c3a3 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -25,6 +25,8 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) include("free_groups.jl") include("fp_groups.jl") + include("matrix_groups.jl") + include("AutFn.jl") include("AutSigma_41.jl") include("AutSigma3.jl") From dd6588f018229156fb7e70041d57f372ae7d9092 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 14:24:01 +0200 Subject: [PATCH 04/14] add Homomorphisms --- src/Groups.jl | 6 ++- src/abelianize.jl | 23 +++++++++ src/homomorphisms.jl | 114 ++++++++++++++++++++++++++++++++++++++++++ test/homomorphisms.jl | 49 ++++++++++++++++++ test/runtests.jl | 4 +- 5 files changed, 194 insertions(+), 2 deletions(-) create mode 100644 src/abelianize.jl create mode 100644 src/homomorphisms.jl create mode 100644 test/homomorphisms.jl diff --git a/src/Groups.jl b/src/Groups.jl index 5a2755d..5221df4 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -9,15 +9,17 @@ import Random import OrderedCollections: OrderedSet -export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElement, SpecialAutomorphismGroup export MatrixGroups +export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElement, SpecialAutomorphismGroup, Homomorphism + export alphabet, evaluate, word, gens include("types.jl") include("hashing.jl") include("normalform.jl") include("autgroups.jl") +include("homomorphisms.jl") include("aut_groups/sautFn.jl") include("aut_groups/mcg.jl") @@ -25,5 +27,7 @@ include("aut_groups/mcg.jl") include("matrix_groups/MatrixGroups.jl") using .MatrixGroups +include("abelianize.jl") + include("wl_ball.jl") end # of module Groups diff --git a/src/abelianize.jl b/src/abelianize.jl new file mode 100644 index 0000000..b62595b --- /dev/null +++ b/src/abelianize.jl @@ -0,0 +1,23 @@ +function _abelianize( + i::Integer, + source::AutomorphismGroup{<:FreeGroup}, + target::MatrixGroups.SpecialLinearGroup{N, T}) where {N, T} + n = ngens(object(source)) + @assert n == N + aut = alphabet(source)[i] + if aut isa Transvection + # we change (i,j) to (j, i) to be consistent with the action: + # Automorphisms act on the right which corresponds to action on + # the columns in the matrix case + eij = MatrixGroups.ElementaryMatrix{N}( + aut.j, + aut.i, + ifelse(aut.inv, -one(T), one(T)) + ) + k = alphabet(target)[eij] + return word_type(target)([k]) + else + throw("unexpected automorphism symbol: $(typeof(aut))") + end +end + diff --git a/src/homomorphisms.jl b/src/homomorphisms.jl new file mode 100644 index 0000000..f241ba0 --- /dev/null +++ b/src/homomorphisms.jl @@ -0,0 +1,114 @@ +""" + Homomorphism(f, G::AbstractFPGroup, H::AbstractFPGroup[, check=true]) +Struct representing homomorphism map from `G` to `H` given by map `f`. + +To define `h = Homomorphism(f, G, H)` function (or just callable) `f` must +implement method `f(i::Integer, source, target)::AbstractWord` with the +following meaning. Suppose that word `w = Word([i])` consists of a single +letter in the `alphabet` of `source` (usually it means that in `G` it +represents a generator or its inverse). Then `f(i, G, H)` must return the +**word** representing the image in `H` of `G(w)` under the homomorphism. + +In more mathematical terms it means that if `h(G(w)) == h`, then +`f(i, G, H) == word(h)`. + +Images of both `AbstractWord`s and elements of `G` can be obtained by simply +calling `h(w)`, or `h(g)`. + +If `check=true` then the correctness of the definition of `h` will be performed +when creating the homomorphism. + +!!! note + `f(i, G, H)` must be implemented for all letters in the alphabet of `G`, + not only for those `i` which represent `gens(G)`. Function `f` will be + evaluated exactly once per letter of `alphabet(G)` and the results will be + cached. + +# Examples +```julia +julia> F₂ = FreeGroup(2) +free group on 2 generators + +julia> g,h = gens(F₂) +2-element Vector{FPGroupElement{FreeGroup{Symbol, KnuthBendix.LenLex{Symbol}}, …}}: + f1 + f2 + +julia> ℤ² = FPGroup(F₂, [g*h => h*g]) +Finitely presented group generated by: + { f1 f2 }, +subject to relations: + f1*f2 => f2*f1 + +julia> hom = Groups.Homomorphism( + (i, G, H) -> Groups.word_type(H)([i]), + F₂, + ℤ² + ) +Homomorphism + from : free group on 2 generators + to : ⟨ f1 f2 | + f1*f2 => f2*f1 ⟩ + +julia> hom(g*h*inv(g)) +f2 + +julia> hom(g*h*inv(g)) == hom(h) +true +``` + +""" +struct Homomorphism{Gr1, Gr2, I, W} + gens_images::Dict{I, W} + source::Gr1 + target::Gr2 + + function Homomorphism( + f, + source::AbstractFPGroup, + target::AbstractFPGroup; + check=true + ) + A = alphabet(source) + dct = Dict(i=>convert(word_type(target), f(i, source, target)) + for i in 1:length(A)) + I = eltype(word_type(source)) + W = word_type(target) + hom = new{typeof(source), typeof(target), I, W}(dct, source, target) + + if check + @assert hom(one(source)) == one(target) + for x in gens(source) + + @assert hom(x^-1) == hom(x)^-1 + + for y in gens(source) + @assert hom(x*y) == hom(x)*hom(y) + @assert hom(x*y)^-1 == hom(y^-1)*hom(x^-1) + end + end + for (lhs, rhs) in relations(source) + relator = lhs*inv(alphabet(source), rhs) + im_r = hom.target(hom(relator)) + @assert isone(im_r) "Map does not define a homomorphism: h($relator) = $(im_r) ≠ $(one(target))." + end + end + return hom + end +end + +function (h::Homomorphism)(w::AbstractWord) + result = one(word_type(h.target)) # Word + for l in w + append!(result, h.gens_images[l]) + end + return result +end + +function (h::Homomorphism)(g::AbstractFPGroupElement) + @assert parent(g) === h.source + w = h(word(g)) + return h.target(w) +end + +Base.show(io::IO, h::Homomorphism) = print(io, "Homomorphism\n from : $(h.source)\n to : $(h.target)") diff --git a/test/homomorphisms.jl b/test/homomorphisms.jl new file mode 100644 index 0000000..8419b73 --- /dev/null +++ b/test/homomorphisms.jl @@ -0,0 +1,49 @@ +function test_homomorphism(hom) + F = hom.source + @test isone(hom(one(F))) + @test all(inv(hom(g)) == hom(inv(g)) for g in gens(F)) + @test all(isone(hom(g)*hom(inv(g))) for g in gens(F)) + @test all(hom(g*h) == hom(g)*hom(h) for g in gens(F) for h in gens(F)) +end + +@testset "Homomorphisms" begin + + F₂ = FreeGroup(2) + g,h = gens(F₂) + + ℤ² = FPGroup(F₂, [g*h => h*g]) + + let hom = Groups.Homomorphism((i, G, H) -> Groups.word_type(H)([i]), F₂, ℤ²) + + @test hom(word(g)) == word(g) + + @test hom(word(g*h*inv(g))) == [1,3,2] + + @test hom(g*h*inv(g)) == hom(h) + @test isone(hom(g*h*inv(g)*inv(h))) + + @test contains(sprint(print, hom), "Homomorphism") + + test_homomorphism(hom) + end + + SAutF3 = SpecialAutomorphismGroup(FreeGroup(3)) + SL3Z = MatrixGroups.SpecialLinearGroup{3}(Int8) + + let hom = Groups.Homomorphism( + Groups._abelianize, + SAutF3, + SL3Z, + ) + + A = alphabet(SAutF3) + g = SAutF3([A[Groups.ϱ(1,2)]]) + h = SAutF3([A[Groups.λ(1,2)]])^-1 + + @test !isone(g) && !isone(hom(g)) + @test !isone(h) && !isone(hom(h)) + @test !isone(g*h) && isone(hom(g*h)) + + test_homomorphism(hom) + end +end diff --git a/test/runtests.jl b/test/runtests.jl index be4c3a3..b731e8f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -26,8 +26,10 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) include("fp_groups.jl") include("matrix_groups.jl") - include("AutFn.jl") + + include("homomorphisms.jl") + include("AutSigma_41.jl") include("AutSigma3.jl") From e78520f90bf945fe4bbd0983ed2cf22025427bcf Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 14:43:52 +0200 Subject: [PATCH 05/14] add Constructions module with Direct/Wreath product --- Project.toml | 1 + src/Groups.jl | 4 + src/constructions/constructions.jl | 10 ++ src/constructions/direct_power.jl | 112 ++++++++++++++++++++ src/constructions/direct_product.jl | 105 +++++++++++++++++++ src/constructions/wreath_product.jl | 136 +++++++++++++++++++++++++ src/matrix_groups/MatrixGroups.jl | 6 +- src/matrix_groups/abstract.jl | 7 +- src/matrix_groups/eltary_matrices.jl | 5 +- src/matrix_groups/eltary_symplectic.jl | 5 +- test/group_constructions.jl | 43 ++++++++ test/runtests.jl | 2 + 12 files changed, 424 insertions(+), 12 deletions(-) create mode 100644 src/constructions/constructions.jl create mode 100644 src/constructions/direct_power.jl create mode 100644 src/constructions/direct_product.jl create mode 100644 src/constructions/wreath_product.jl create mode 100644 test/group_constructions.jl diff --git a/Project.toml b/Project.toml index 3b50d0d..b7685fe 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ Folds = "41a02a25-b8f0-4f67-bc48-60067656b558" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a" OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d" +PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] diff --git a/src/Groups.jl b/src/Groups.jl index 5221df4..46ae545 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -15,6 +15,10 @@ export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElemen export alphabet, evaluate, word, gens +# general constructions +include(joinpath("constructions", "constructions.jl")) +using .Constructions + include("types.jl") include("hashing.jl") include("normalform.jl") diff --git a/src/constructions/constructions.jl b/src/constructions/constructions.jl new file mode 100644 index 0000000..e371da6 --- /dev/null +++ b/src/constructions/constructions.jl @@ -0,0 +1,10 @@ +module Constructions + +using GroupsCore +using Random + +include("direct_product.jl") +include("direct_power.jl") +include("wreath_product.jl") + +end # of module Constructions diff --git a/src/constructions/direct_power.jl b/src/constructions/direct_power.jl new file mode 100644 index 0000000..7ecfd2b --- /dev/null +++ b/src/constructions/direct_power.jl @@ -0,0 +1,112 @@ +struct DirectPower{Gr,N,GEl<:GroupsCore.GroupElement} <: GroupsCore.Group + group::Gr + + function DirectPower{N}(G::GroupsCore.Group) where {N} + @assert N > 1 + return new{typeof(G),N,eltype(G)}(G) + end +end + +struct DirectPowerElement{GEl,N,Gr<:GroupsCore.Group} <: GroupsCore.GroupElement + elts::NTuple{N,GEl} + parent::DirectPower{Gr,N,GEl} +end + +DirectPowerElement( + elts::AbstractVector{<:GroupsCore.GroupElement}, + G::DirectPower, +) = DirectPowerElement(ntuple(i -> elts[i], _nfold(G)), G) + +_nfold(::DirectPower{Gr,N}) where {Gr,N} = N + +Base.one(G::DirectPower) = + DirectPowerElement(ntuple(_ -> one(G.group), _nfold(G)), G) + +Base.eltype(::Type{<:DirectPower{Gr,N,GEl}}) where {Gr,N,GEl} = + DirectPowerElement{GEl,N,Gr} + +function Base.iterate(G::DirectPower) + itr = Iterators.ProductIterator(ntuple(i -> G.group, _nfold(G))) + res = iterate(itr) + @assert res !== nothing + elt = DirectPowerElement(first(res), G) + return elt, (iterator = itr, state = last(res)) +end + +function Base.iterate(G::DirectPower, state) + itr, st = state.iterator, state.state + res = iterate(itr, st) + res === nothing && return nothing + elt = DirectPowerElement(first(res), G) + return elt, (iterator = itr, state = last(res)) +end + +function Base.IteratorSize(::Type{<:DirectPower{Gr,N}}) where {Gr,N} + Base.IteratorSize(Gr) isa Base.HasLength && return Base.HasShape{N}() + Base.IteratorSize(Gr) isa Base.HasShape && return Base.HasShape{N}() + return Base.IteratorSize(Gr) +end + +Base.size(G::DirectPower) = ntuple(_ -> length(G.group), _nfold(G)) + +GroupsCore.order(::Type{I}, G::DirectPower) where {I<:Integer} = + convert(I, order(I, G.group)^_nfold(G)) + +GroupsCore.ngens(G::DirectPower) = _nfold(G)*ngens(G.group) + +function GroupsCore.gens(G::DirectPower, i::Integer) + k = ngens(G.group) + ci = CartesianIndices((k, _nfold(G))) + @boundscheck checkbounds(ci, i) + r, c = Tuple(ci[i]) + tup = ntuple(j -> j == c ? gens(G.group, r) : one(G.group), _nfold(G)) + return DirectPowerElement(tup, G) +end + +function GroupsCore.gens(G::DirectPower) + N = _nfold(G) + S = gens(G.group) + tups = [ntuple(j->(i == j ? s : one(s)), N) for i in 1:N for s in S] + + return [DirectPowerElement(elts, G) for elts in tups] +end + +Base.isfinite(G::DirectPower) = isfinite(G.group) + +function Base.rand( + rng::Random.AbstractRNG, + rs::Random.SamplerTrivial{<:DirectPower}, +) + G = rs[] + return DirectPowerElement(rand(rng, G.group, _nfold(G)), G) +end + +GroupsCore.parent(g::DirectPowerElement) = g.parent + +Base.:(==)(g::DirectPowerElement, h::DirectPowerElement) = + (parent(g) === parent(h) && g.elts == h.elts) + +Base.hash(g::DirectPowerElement, h::UInt) = hash(g.elts, hash(parent(g), h)) + +Base.deepcopy_internal(g::DirectPowerElement, stackdict::IdDict) = + DirectPowerElement(Base.deepcopy_internal(g.elts, stackdict), parent(g)) + +Base.inv(g::DirectPowerElement) = DirectPowerElement(inv.(g.elts), parent(g)) + +function Base.:(*)(g::DirectPowerElement, h::DirectPowerElement) + @assert parent(g) === parent(h) + return DirectPowerElement(g.elts .* h.elts, parent(g)) +end + +GroupsCore.order(::Type{I}, g::DirectPowerElement) where {I<:Integer} = + convert(I, reduce(lcm, (order(I, h) for h in g.elts), init = one(I))) + +Base.isone(g::DirectPowerElement) = all(isone, g.elts) + +function Base.show(io::IO, G::DirectPower) + n = _nfold(G) + nn = n == 1 ? "1-st" : n == 2 ? "2-nd" : n == 3 ? "3-rd" : "$n-th" + print(io, "Direct $(nn) power of $(G.group)") +end +Base.show(io::IO, g::DirectPowerElement) = + print(io, "( ", join(g.elts, ", "), " )") diff --git a/src/constructions/direct_product.jl b/src/constructions/direct_product.jl new file mode 100644 index 0000000..802931b --- /dev/null +++ b/src/constructions/direct_product.jl @@ -0,0 +1,105 @@ +using Random +using GroupsCore + +struct DirectProduct{Gt,Ht,GEl,HEl} <: GroupsCore.Group + first::Gt + last::Ht + + function DirectProduct(G::GroupsCore.Group, H::GroupsCore.Group) + return new{typeof(G),typeof(H),eltype(G),eltype(H)}(G, H) + end +end + +struct DirectProductElement{GEl,HEl,Gt,Ht} <: GroupsCore.GroupElement + elts::Tuple{GEl,HEl} + parent::DirectProduct{Gt,Ht,GEl,HEl} +end + +DirectProductElement(g, h, G::DirectProduct) = DirectProduct((g, h), G) + +Base.one(G::DirectProduct) = + DirectProductElement((one(G.first), one(G.last)), G) + +Base.eltype(::Type{<:DirectProduct{Gt,Ht,GEl,HEl}}) where {Gt,Ht,GEl,HEl} = + DirectProductElement{GEl,HEl,Gt,Ht} + +function Base.iterate(G::DirectProduct) + itr = Iterators.product(G.first, G.last) + res = iterate(itr) + @assert res !== nothing + elt = DirectProductElement(first(res), G) + return elt, (iterator = itr, state = last(res)) +end + +function Base.iterate(G::DirectProduct, state) + itr, st = state.iterator, state.state + res = iterate(itr, st) + res === nothing && return nothing + elt = DirectProductElement(first(res), G) + return elt, (iterator = itr, state = last(res)) +end + +function Base.IteratorSize(::Type{<:DirectProduct{Gt,Ht}}) where {Gt,Ht} + Gi = Base.IteratorSize(Gt) + Hi = Base.IteratorSize(Ht) + if Gi isa Base.IsInfinite || Hi isa Base.IsInfinite + return Base.IsInfinite() + elseif Gi isa Base.SizeUnknown || Hi isa Base.SizeUnknown + return Base.SizeUnknown() + else + return Base.HasShape{2}() + end +end + +Base.size(G::DirectProduct) = (length(G.first), length(G.last)) + +GroupsCore.order(::Type{I}, G::DirectProduct) where {I<:Integer} = + convert(I, order(I, G.first) * order(I, G.last)) + +GroupsCore.ngens(G::DirectProduct) = ngens(G.first) + ngens(G.last) + +function GroupsCore.gens(G::DirectProduct) + gens_first = [DirectProductElement((g, one(G.last)), G) for g in gens(G.first)] + + gens_last = [DirectProductElement((one(G.first), g), G) for g in gens(G.last)] + + return [gens_first; gens_last] +end + +Base.isfinite(G::DirectProduct) = isfinite(G.first) && isfinite(G.last) + +function Base.rand( + rng::Random.AbstractRNG, + rs::Random.SamplerTrivial{<:DirectProduct}, +) + G = rs[] + return DirectProductElement((rand(rng, G.first), rand(rng, G.last)), G) +end + +GroupsCore.parent(g::DirectProductElement) = g.parent + +Base.:(==)(g::DirectProductElement, h::DirectProductElement) = + (parent(g) === parent(h) && g.elts == h.elts) + +Base.hash(g::DirectProductElement, h::UInt) = hash(g.elts, hash(parent(g), h)) + +Base.deepcopy_internal(g::DirectProductElement, stackdict::IdDict) = + DirectProductElement(Base.deepcopy_internal(g.elts, stackdict), parent(g)) + +Base.inv(g::DirectProductElement) = + DirectProductElement(inv.(g.elts), parent(g)) + +function Base.:(*)(g::DirectProductElement, h::DirectProductElement) + @assert parent(g) === parent(h) + return DirectProductElement(g.elts .* h.elts, parent(g)) +end + +GroupsCore.order(::Type{I}, g::DirectProductElement) where {I<:Integer} = + convert(I, lcm(order(I, first(g.elts)), order(I, last(g.elts)))) + +Base.isone(g::DirectProductElement) = all(isone, g.elts) + +Base.show(io::IO, G::DirectProduct) = + print(io, "Direct product of $(G.first) and $(G.last)") +Base.show(io::IO, g::DirectProductElement) = + print(io, "( $(join(g.elts, ",")) )") diff --git a/src/constructions/wreath_product.jl b/src/constructions/wreath_product.jl new file mode 100644 index 0000000..41ad021 --- /dev/null +++ b/src/constructions/wreath_product.jl @@ -0,0 +1,136 @@ +import PermutationGroups: AbstractPermutationGroup, AbstractPerm, degree, SymmetricGroup + +""" + WreathProduct(G::Group, P::AbstractPermutationGroup) <: Group +Return the wreath product of a group `G` by permutation group `P`, usually +written as `G ≀ P`. + +As set `G ≀ P` is the same as `Gᵈ × P` and the group can be understood as a +semi-direct product of `P` acting on `d`-fold cartesian product of `G` by +permuting coordinates. To be more precise, the multiplication inside wreath +product is defined as +> `(n, σ) * (m, τ) = (n*(m^σ), σ*τ)` +where `m^σ` denotes the action (from the right) of the permutation `σ` on +`d`-tuples of elements from `G`. +""" +struct WreathProduct{DP<:DirectPower,PGr<:AbstractPermutationGroup} <: + GroupsCore.Group + N::DP + P::PGr + + function WreathProduct(G::Group, P::AbstractPermutationGroup) + N = DirectPower{degree(P)}(G) + return new{typeof(N),typeof(P)}(N, P) + end +end + +struct WreathProductElement{ + DPEl<:DirectPowerElement, + PEl<:AbstractPerm, + Wr<:WreathProduct, +} <: GroupsCore.GroupElement + n::DPEl + p::PEl + parent::Wr + + function WreathProductElement( + n::DirectPowerElement, + p::AbstractPerm, + W::WreathProduct, + ) + new{typeof(n),typeof(p),typeof(W)}(n, p, W) + end +end + +Base.one(W::WreathProduct) = WreathProductElement(one(W.N), one(W.P), W) + +Base.eltype(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr} = + WreathProductElement{eltype(DP),eltype(PGr),WreathProduct{DP,PGr}} + +function Base.iterate(G::WreathProduct) + itr = Iterators.product(G.N, G.P) + res = iterate(itr) + @assert res !== nothing + elt = WreathProductElement(first(res)..., G) + return elt, (iterator = itr, state = last(res)) +end + +function Base.iterate(G::WreathProduct, state) + itr, st = state.iterator, state.state + res = iterate(itr, st) + res === nothing && return nothing + elt = WreathProductElement(first(res)..., G) + return elt, (iterator = itr, state = last(res)) +end + +function Base.IteratorSize(::Type{<:WreathProduct{DP,PGr}}) where {DP,PGr} + dpI = Base.IteratorSize(DP) + pgI = Base.IteratorSize(PGr) + + if dpI isa Base.IsInfinite || pgI isa Base.IsInfinite + return Base.IsInfinite() + elseif dpI isa Base.SizeUnknown || pgI isa Base.SizeUnknown + return Base.SizeUnknown() + else + return Base.HasShape{2}() + end +end + +Base.size(G::WreathProduct) = (length(G.N), length(G.P)) + +GroupsCore.order(::Type{I}, G::WreathProduct) where {I<:Integer} = + convert(I, order(I, G.N) * order(I, G.P)) + +function GroupsCore.gens(G::WreathProduct) + N_gens = [WreathProductElement(n, one(G.P), G) for n in gens(G.N)] + P_gens = [WreathProductElement(one(G.N), p, G) for p in gens(G.P)] + return [N_gens; P_gens] +end + +Base.isfinite(G::WreathProduct) = isfinite(G.N) && isfinite(G.P) + +function Base.rand( + rng::Random.AbstractRNG, + rs::Random.SamplerTrivial{<:WreathProduct}, +) + + G = rs[] + return WreathProductElement(rand(rng, G.N), rand(rng, G.P), G) +end + +GroupsCore.parent(g::WreathProductElement) = g.parent + +Base.:(==)(g::WreathProductElement, h::WreathProductElement) = + parent(g) === parent(h) && g.n == h.n && g.p == h.p + +Base.hash(g::WreathProductElement, h::UInt) = + hash(g.n, hash(g.p, hash(g.parent, h))) + +function Base.deepcopy_internal(g::WreathProductElement, stackdict::IdDict) + return WreathProductElement( + Base.deepcopy_internal(g.n, stackdict), + Base.deepcopy_internal(g.p, stackdict), + parent(g), + ) +end + +_act(p::AbstractPerm, n::DirectPowerElement) = + DirectPowerElement(n.elts^p, parent(n)) + +function Base.inv(g::WreathProductElement) + pinv = inv(g.p) + return WreathProductElement(_act(pinv, inv(g.n)), pinv, parent(g)) +end + +function Base.:(*)(g::WreathProductElement, h::WreathProductElement) + @assert parent(g) === parent(h) + return WreathProductElement(g.n * _act(g.p, h.n), g.p * h.p, parent(g)) +end + +Base.isone(g::WreathProductElement) = isone(g.n) && isone(g.p) + +Base.show(io::IO, G::WreathProduct) = + print(io, "Wreath product of $(G.N.group) by $(G.P)") +Base.show(io::IO, g::WreathProductElement) = print(io, "( $(g.n)≀$(g.p) )") + +Base.copy(g::WreathProductElement) = WreathProductElement(g.n, g.p, parent(g)) diff --git a/src/matrix_groups/MatrixGroups.jl b/src/matrix_groups/MatrixGroups.jl index 4a5bb93..80ab1db 100644 --- a/src/matrix_groups/MatrixGroups.jl +++ b/src/matrix_groups/MatrixGroups.jl @@ -1,11 +1,13 @@ module MatrixGroups +using StaticArrays + using GroupsCore using Groups using KnuthBendix -using LinearAlgebra # Identity matrix -using Random # GroupsCore rand +import LinearAlgebra # Identity matrix +import Random # GroupsCore rand export SpecialLinearGroup, SymplecticGroup diff --git a/src/matrix_groups/abstract.jl b/src/matrix_groups/abstract.jl index fd70c41..5e19c86 100644 --- a/src/matrix_groups/abstract.jl +++ b/src/matrix_groups/abstract.jl @@ -1,7 +1,8 @@ abstract type MatrixGroup{N, T} <: Groups.AbstractFPGroup end const MatrixGroupElement{N, T} = Groups.AbstractFPGroupElement{<:MatrixGroup{N, T}} -Base.isone(g::MatrixGroupElement{N, T}) where {N, T} = matrix_repr(g) == I +Base.isone(g::MatrixGroupElement{N, T}) where {N, T} = + isone(word(g)) || matrix_repr(g) == LinearAlgebra.I function Base.:(==)(m1::M1, m2::M2) where {M1<:MatrixGroupElement, M2<:MatrixGroupElement} parent(m1) === parent(m2) || return false @@ -26,7 +27,9 @@ Base.getindex(sl::MatrixGroupElement, i, j) = matrix_repr(sl)[i,j] # Base.iterate(sl::MatrixGroupElement, state) = iterate(sl.elts, state) function matrix_repr(m::MatrixGroupElement{N, T}) where {N, T} - isempty(word(m)) && return StaticArrays.SMatrix{N, N, T}(I) + if isone(word(m)) + return StaticArrays.SMatrix{N, N, T}(LinearAlgebra.I) + end A = alphabet(parent(m)) return prod(matrix_repr(A[l]) for l in word(m)) end diff --git a/src/matrix_groups/eltary_matrices.jl b/src/matrix_groups/eltary_matrices.jl index f78fdff..947faca 100644 --- a/src/matrix_groups/eltary_matrices.jl +++ b/src/matrix_groups/eltary_matrices.jl @@ -1,6 +1,3 @@ -using Groups -using StaticArrays - struct ElementaryMatrix{N, T} <: Groups.GSymbol i::Int j::Int @@ -24,7 +21,7 @@ Base.inv(e::ElementaryMatrix{N}) where N = ElementaryMatrix{N}(e.i, e.j, -e.val) function matrix_repr(e::ElementaryMatrix{N, T}) where {N, T} - m = StaticArrays.MMatrix{N, N, T}(I) + m = StaticArrays.MMatrix{N, N, T}(LinearAlgebra.I) m[e.i, e.j] = e.val x = StaticArrays.SMatrix{N, N}(m) return x diff --git a/src/matrix_groups/eltary_symplectic.jl b/src/matrix_groups/eltary_symplectic.jl index 413fcea..dba3b43 100644 --- a/src/matrix_groups/eltary_symplectic.jl +++ b/src/matrix_groups/eltary_symplectic.jl @@ -1,6 +1,3 @@ -using Groups -using StaticArrays - struct ElementarySymplectic{N, T} <: Groups.GSymbol symbol::Symbol i::Int @@ -69,7 +66,7 @@ Base.inv(s::ElementarySymplectic{N}) where N = function matrix_repr(s::ElementarySymplectic{N, T}) where {N, T} @assert iseven(N) n = div(N, 2) - m = StaticArrays.MMatrix{N, N, T}(I) + m = StaticArrays.MMatrix{N, N, T}(LinearAlgebra.I) i,j = _ind(s) m[i,j] = s.val if s.symbol === :A diff --git a/test/group_constructions.jl b/test/group_constructions.jl new file mode 100644 index 0000000..09edc11 --- /dev/null +++ b/test/group_constructions.jl @@ -0,0 +1,43 @@ +@testset "GroupConstructions" begin + + @testset "DirectProduct" begin + GH = + let G = PermutationGroups.SymmetricGroup(3), + H = PermutationGroups.SymmetricGroup(4) + + Groups.Constructions.DirectProduct(G, H) + end + test_Group_interface(GH) + test_GroupElement_interface(rand(GH, 2)...) + + @test collect(GH) isa Array{eltype(GH), 2} + @test contains(sprint(print, GH), "Direct product") + @test sprint(print, rand(GH)) isa String + end + + @testset "DirectPower" begin + GGG = Groups.Constructions.DirectPower{3}( + PermutationGroups.SymmetricGroup(3), + ) + test_Group_interface(GGG) + test_GroupElement_interface(rand(GGG, 2)...) + + @test collect(GGG) isa Array{eltype(GGG), 3} + @test contains(sprint(print, GGG), "Direct 3-rd power") + @test sprint(print, rand(GGG)) isa String + end + @testset "WreathProduct" begin + W = + let G = PermutationGroups.SymmetricGroup(2), + P = PermutationGroups.SymmetricGroup(4) + + Groups.Constructions.WreathProduct(G, P) + end + test_Group_interface(W) + test_GroupElement_interface(rand(W, 2)...) + + @test collect(W) isa Array{eltype(W), 2} + @test contains(sprint(print, W), "Wreath product") + @test sprint(print, rand(W)) isa String + end +end diff --git a/test/runtests.jl b/test/runtests.jl index b731e8f..409f204 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -33,6 +33,8 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) include("AutSigma_41.jl") include("AutSigma3.jl") + include("group_constructions.jl") + # if !haskey(ENV, "CI") # include("benchmarks.jl") # end From 22a8809c6fad1a3c4da789e71a65a7ced1f1f918 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 14:53:06 +0200 Subject: [PATCH 06/14] rm AbstractAlgebra --- Project.toml | 3 +-- test/matrix_groups.jl | 18 ++++++++++++++---- test/runtests.jl | 14 -------------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/Project.toml b/Project.toml index b7685fe..5d897ed 100644 --- a/Project.toml +++ b/Project.toml @@ -12,7 +12,6 @@ PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420" Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] -AbstractAlgebra = "0.22" Folds = "0.2.7" GroupsCore = "0.4" KnuthBendix = "0.3" @@ -27,4 +26,4 @@ PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" [targets] -test = ["Test", "BenchmarkTools", "AbstractAlgebra", "PermutationGroups"] +test = ["Test", "BenchmarkTools"] diff --git a/test/matrix_groups.jl b/test/matrix_groups.jl index 853ab13..d047575 100644 --- a/test/matrix_groups.jl +++ b/test/matrix_groups.jl @@ -1,7 +1,6 @@ using Groups.MatrixGroups @testset "Matrix Groups" begin - @testset "SL(n, ℤ)" begin SL3Z = SpecialLinearGroup{3}(Int8) @@ -9,7 +8,20 @@ using Groups.MatrixGroups E, sizes = Groups.wlmetric_ball(S, radius=4) - @test sizes = [13, 121, 883, 5455] + @test sizes == [13, 121, 883, 5455] + + E(i,j) = SL3Z([A[MatrixGroups.ElementaryMatrix{3}(i,j, Int8(1))]]) + + A = alphabet(SL3Z) + w = E(1,2) + r = E(2,3)^-3 + s = E(1,3)^2*E(3,2)^-1 + + S = [w,r,s]; 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] @testset "GroupsCore conformance" begin test_Group_interface(SL3Z) @@ -18,7 +30,5 @@ using Groups.MatrixGroups test_GroupElement_interface(g, h) end - end - end diff --git a/test/runtests.jl b/test/runtests.jl index 409f204..aeed59a 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,4 @@ using Test -import AbstractAlgebra using Groups import KnuthBendix: Word @@ -9,19 +8,6 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) @testset "Groups" begin - @testset "wlmetric_ball" begin - M = AbstractAlgebra.MatrixAlgebra(AbstractAlgebra.zz, 3) - w = one(M); w[1,2] = 1; - r = one(M); r[2,3] = -3; - s = one(M); s[1,3] = 2; s[3,2] = -1; - - S = [w,r,s]; 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] - end - include("free_groups.jl") include("fp_groups.jl") From 93d97ff75fb7a17aafc2c52db1492b99f413cf12 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 15:50:03 +0200 Subject: [PATCH 07/14] fixing and ordering imports --- Project.toml | 5 +++-- src/Groups.jl | 12 +++++++----- src/constructions/constructions.jl | 2 +- src/constructions/direct_product.jl | 3 --- src/matrix_groups/MatrixGroups.jl | 10 +++++----- test/runtests.jl | 1 + 6 files changed, 17 insertions(+), 16 deletions(-) diff --git a/Project.toml b/Project.toml index 5d897ed..285de98 100644 --- a/Project.toml +++ b/Project.toml @@ -7,9 +7,10 @@ version = "0.7.2" Folds = "41a02a25-b8f0-4f67-bc48-60067656b558" GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120" KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a" +LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" 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" @@ -18,9 +19,9 @@ KnuthBendix = "0.3" OrderedCollections = "1" PermutationGroups = "0.3" julia = "1.3" +StaticArrays = "1" [extras] -AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d" BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420" Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" diff --git a/src/Groups.jl b/src/Groups.jl index 46ae545..1ccf667 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -1,13 +1,15 @@ module Groups +import Folds + using GroupsCore -using Folds +import GroupsCore.Random + +import OrderedCollections: OrderedSet + import KnuthBendix import KnuthBendix: AbstractWord, Alphabet, Word import KnuthBendix: alphabet -import Random - -import OrderedCollections: OrderedSet export MatrixGroups @@ -17,7 +19,7 @@ export alphabet, evaluate, word, gens # general constructions include(joinpath("constructions", "constructions.jl")) -using .Constructions +import .Constructions include("types.jl") include("hashing.jl") diff --git a/src/constructions/constructions.jl b/src/constructions/constructions.jl index e371da6..537de41 100644 --- a/src/constructions/constructions.jl +++ b/src/constructions/constructions.jl @@ -1,7 +1,7 @@ module Constructions using GroupsCore -using Random +import GroupsCore.Random include("direct_product.jl") include("direct_power.jl") diff --git a/src/constructions/direct_product.jl b/src/constructions/direct_product.jl index 802931b..2f6db67 100644 --- a/src/constructions/direct_product.jl +++ b/src/constructions/direct_product.jl @@ -1,6 +1,3 @@ -using Random -using GroupsCore - struct DirectProduct{Gt,Ht,GEl,HEl} <: GroupsCore.Group first::Gt last::Ht diff --git a/src/matrix_groups/MatrixGroups.jl b/src/matrix_groups/MatrixGroups.jl index 80ab1db..486baf1 100644 --- a/src/matrix_groups/MatrixGroups.jl +++ b/src/matrix_groups/MatrixGroups.jl @@ -1,13 +1,13 @@ module MatrixGroups +import LinearAlgebra # Identity matrix + using StaticArrays using GroupsCore -using Groups -using KnuthBendix - -import LinearAlgebra # Identity matrix -import Random # GroupsCore rand +import GroupsCore.Random # GroupsCore rand +using ..Groups +using Groups.KnuthBendix export SpecialLinearGroup, SymplecticGroup diff --git a/test/runtests.jl b/test/runtests.jl index aeed59a..90ca83d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,5 +1,6 @@ using Test using Groups +using PermutationGroups import KnuthBendix: Word From 9e867a58e68e1709aba4b4f7e942a19d67d8f387 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 15:51:29 +0200 Subject: [PATCH 08/14] use NullLogger to suppress warnings --- Project.toml | 1 + src/Groups.jl | 1 + src/aut_groups/sautFn.jl | 5 ++++- test/AutFn.jl | 13 +++++++------ test/fp_groups.jl | 13 +++++++++---- test/matrix_groups.jl | 12 +++++++----- test/runtests.jl | 2 ++ 7 files changed, 31 insertions(+), 16 deletions(-) diff --git a/Project.toml b/Project.toml index 285de98..380cecb 100644 --- a/Project.toml +++ b/Project.toml @@ -8,6 +8,7 @@ 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" StaticArrays = "90137ffa-7385-5640-81b9-e52037218182" diff --git a/src/Groups.jl b/src/Groups.jl index 1ccf667..7c97e77 100644 --- a/src/Groups.jl +++ b/src/Groups.jl @@ -1,6 +1,7 @@ module Groups import Folds +import Logging using GroupsCore import GroupsCore.Random diff --git a/src/aut_groups/sautFn.jl b/src/aut_groups/sautFn.jl index d7b0bd1..4e86a37 100644 --- a/src/aut_groups/sautFn.jl +++ b/src/aut_groups/sautFn.jl @@ -10,7 +10,10 @@ function SpecialAutomorphismGroup(F::FreeGroup; ordering = KnuthBendix.LenLex, k maxrules = 1000*n rws = KnuthBendix.RewritingSystem(rels, ordering(A)) - KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...) + Logging.with_logger(Logging.NullLogger()) do + # the rws is not confluent, let's suppress warning about it + KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...) + end return AutomorphismGroup(F, S, rws, ntuple(i -> gens(F, i), n)) end diff --git a/test/AutFn.jl b/test/AutFn.jl index 47f09e3..f7e18be 100644 --- a/test/AutFn.jl +++ b/test/AutFn.jl @@ -176,12 +176,13 @@ ) end - @testset "GroupsCore conformance" begin - test_Group_interface(A) - g = A(rand(1:length(alphabet(A)), 10)) - h = A(rand(1:length(alphabet(A)), 10)) + Logging.with_logger(Logging.NullLogger()) do + @testset "GroupsCore conformance" begin + test_Group_interface(A) + g = A(rand(1:length(alphabet(A)), 10)) + h = A(rand(1:length(alphabet(A)), 10)) - test_GroupElement_interface(g, h) + test_GroupElement_interface(g, h) + end end - end diff --git a/test/fp_groups.jl b/test/fp_groups.jl index 0fb5b1e..e06f165 100644 --- a/test/fp_groups.jl +++ b/test/fp_groups.jl @@ -54,9 +54,14 @@ Groups.normalform!(h) @test h == H([5]) - @testset "GroupsCore conformance: H" begin - test_Group_interface(H) - test_GroupElement_interface(rand(H, 2)...) - end + @test_logs (:warn, "using generic isfiniteorder(::AbstractFPGroupElement): the returned `false` might be wrong") isfiniteorder(h) + @test_logs (:warn, "using generic isfinite(::AbstractFPGroup): the returned `false` might be wrong") isfinite(H) + + Logging.with_logger(Logging.NullLogger()) do + @testset "GroupsCore conformance: H" begin + test_Group_interface(H) + test_GroupElement_interface(rand(H, 2)...) + end + end end diff --git a/test/matrix_groups.jl b/test/matrix_groups.jl index d047575..fb13d6f 100644 --- a/test/matrix_groups.jl +++ b/test/matrix_groups.jl @@ -23,12 +23,14 @@ using Groups.MatrixGroups _, sizes = Groups.wlmetric_ball_serial(S, radius=4); @test sizes == [7, 33, 141, 561] - @testset "GroupsCore conformance" begin - test_Group_interface(SL3Z) - g = A(rand(1:length(alphabet(SL3Z)), 10)) - h = A(rand(1:length(alphabet(SL3Z)), 10)) + Logging.with_logger(Logging.NullLogger()) do + @testset "GroupsCore conformance" begin + test_Group_interface(SL3Z) + g = SL3Z(rand(1:length(alphabet(SL3Z)), 10)) + h = SL3Z(rand(1:length(alphabet(SL3Z)), 10)) - test_GroupElement_interface(g, h) + test_GroupElement_interface(g, h) + end end end end diff --git a/test/runtests.jl b/test/runtests.jl index 90ca83d..fabfb6e 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -2,6 +2,8 @@ using Test using Groups using PermutationGroups +import Logging + import KnuthBendix: Word using GroupsCore From 4431ae714430a5431654bfc91e706df68d8daae4 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 15:52:24 +0200 Subject: [PATCH 09/14] bump julia to 1.6 --- .github/workflows/runtests.yml | 2 +- Project.toml | 2 +- test/AutFn.jl | 4 +--- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.github/workflows/runtests.yml b/.github/workflows/runtests.yml index 6ccb8a7..24406d7 100644 --- a/.github/workflows/runtests.yml +++ b/.github/workflows/runtests.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: version: - - '1.3' + - '1.6' - '1' - 'nightly' os: diff --git a/Project.toml b/Project.toml index 380cecb..ac74155 100644 --- a/Project.toml +++ b/Project.toml @@ -19,8 +19,8 @@ GroupsCore = "0.4" KnuthBendix = "0.3" OrderedCollections = "1" PermutationGroups = "0.3" -julia = "1.3" StaticArrays = "1" +julia = "1.6" [extras] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" diff --git a/test/AutFn.jl b/test/AutFn.jl index f7e18be..8c566ea 100644 --- a/test/AutFn.jl +++ b/test/AutFn.jl @@ -96,9 +96,7 @@ @test evaluate(g*h) == evaluate(h*g) @test (g*h).savedhash == zero(UInt) - if VERSION >= v"1.6.0" - @test sprint(show, typeof(g)) == "Automorphism{FreeGroup{Symbol, KnuthBendix.LenLex{Symbol}}, …}" - end + @test sprint(show, typeof(g)) == "Automorphism{FreeGroup{Symbol, KnuthBendix.LenLex{Symbol}}, …}" a = g*h b = h*g From de600de0a225793e2d841f0626221f6ac4d08359 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 15:53:55 +0200 Subject: [PATCH 10/14] better reporting of timings in tests --- test/AutSigma_41.jl | 13 ++++++++++--- test/benchmarks.jl | 3 +-- test/free_groups.jl | 3 ++- test/runtests.jl | 34 ++++++++++++++++++++++------------ 4 files changed, 35 insertions(+), 18 deletions(-) diff --git a/test/AutSigma_41.jl b/test/AutSigma_41.jl index 7a9e0d1..49e0135 100644 --- a/test/AutSigma_41.jl +++ b/test/AutSigma_41.jl @@ -121,15 +121,22 @@ using Groups.KnuthBendix u = (a6 * a5)^-1 * b1 * (a6 * a5) x = (a6 * a5 * a4 * a3 * a2 * u * a1^-1 * a2^-1 * a3^-1 * a4^-1) # yet another auxillary # x = (a4^-1*a3^-1*a2^-1*a1^-1*u*a2*a3*a4*a5*a6) - @time evaluate(x) + b3 = x * a0 * x^-1 - b3im = @time evaluate(b3) - b3cim = @time let g = b3 + b3im = evaluate(b3) + b3cim = let g = b3 f = Groups.compiled(g) f(Groups.domain(g)) end @test b3im == b3cim @test a0 * b2 * b1 == a1 * a3 * a5 * b3 + + @time evaluate(x) + let g = b3 + f = Groups.compiled(g) + f(Groups.domain(g)) + @time f(Groups.domain(g)) + end end end diff --git a/test/benchmarks.jl b/test/benchmarks.jl index 4a6db1f..44db248 100644 --- a/test/benchmarks.jl +++ b/test/benchmarks.jl @@ -2,7 +2,6 @@ using BenchmarkTools using Test using Groups -using Groups.New function wl_ball(F; radius::Integer) g, state = iterate(F) @@ -47,7 +46,7 @@ end @testset "iteration: SAut(F_n)" begin R = 4 FN = FreeGroup(N) - SAutFN = New.SpecialAutomorphismGroup(FN) + SAutFN = SpecialAutomorphismGroup(FN) let G = SAutFN S = unique([gens(G); inv.(gens(G))]) diff --git a/test/free_groups.jl b/test/free_groups.jl index ced86a8..f35d85e 100644 --- a/test/free_groups.jl +++ b/test/free_groups.jl @@ -37,6 +37,8 @@ @test k == a*b^-1 @time k = test_iteration(F3, 1000) + + @time test_iteration(F3, 1000) @test k == (a^2)*c^2*a^-1 end @@ -67,5 +69,4 @@ test_Group_interface(F3) test_GroupElement_interface(rand(F3, 2)...) end - end diff --git a/test/runtests.jl b/test/runtests.jl index fabfb6e..061fac8 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -11,20 +11,30 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl")) @testset "Groups" begin - include("free_groups.jl") - include("fp_groups.jl") + _, t = @timed include("free_groups.jl") + @info "free_groups.jl took $(round(t, digits=2))s" + _, t = @timed include("fp_groups.jl") + @info "fp_groups.jl took $(round(t, digits=2))s" - include("matrix_groups.jl") - include("AutFn.jl") + _, t = @timed include("matrix_groups.jl") + @info "matrix_groups.jl took $(round(t, digits=2))s" + _, t = @timed include("AutFn.jl") + @info "AutFn.jl took $(round(t, digits=2))s" - include("homomorphisms.jl") + _, t = @timed include("homomorphisms.jl") + @info "homomorphisms.jl took $(round(t, digits=2))s" - include("AutSigma_41.jl") - include("AutSigma3.jl") + if haskey(ENV, "CI") + _, t = @timed include("AutSigma_41.jl") + @info "AutSigma_41 took $(round(t, digits=2))s" + _, t = @timed include("AutSigma3.jl") + @info "AutSigma3 took $(round(t, digits=2))s" + end - include("group_constructions.jl") - - # if !haskey(ENV, "CI") - # include("benchmarks.jl") - # end + _, t = @timed include("group_constructions.jl") + @info "Constructions took $(round(t, digits=2))s" +end + +if !haskey(ENV, "CI") + include("benchmarks.jl") end From f68a0c2f27ff3016963e1f4130e3de057480a1fe Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 16:19:08 +0200 Subject: [PATCH 11/14] add tests for SpNs --- src/abelianize.jl | 46 ++++++++++++++++++++++++++++++++++++++++ src/matrix_groups/Spn.jl | 7 ++++-- test/homomorphisms.jl | 26 +++++++++++++++++++++-- test/matrix_groups.jl | 12 +++++++++++ 4 files changed, 87 insertions(+), 4 deletions(-) diff --git a/src/abelianize.jl b/src/abelianize.jl index b62595b..e059feb 100644 --- a/src/abelianize.jl +++ b/src/abelianize.jl @@ -21,3 +21,49 @@ function _abelianize( end end +function _abelianize( + i::Integer, + source::AutomorphismGroup{<:Groups.SurfaceGroup}, + target::MatrixGroups.SpecialLinearGroup{N, T}) where {N, T} + n = ngens(Groups.object(source)) + @assert n == N + g = alphabet(source)[i].autFn_word + result = one(target) + for l in word(g) + append!(word(result), _abelianize(l, parent(g), target)) + end + + return word(result) +end + +function Groups._abelianize( + i::Integer, + source::AutomorphismGroup{<:Groups.SurfaceGroup}, + target::MatrixGroups.SymplecticGroup{N, T} +) where {N, T} + @assert iseven(N) + As = alphabet(source) + At = alphabet(target) + + SlN = let genus = Groups.genus(Groups.object(source)) + @assert 2genus == N + MatrixGroups.SpecialLinearGroup{2genus}(T) + end + + ab = Groups.Homomorphism(Groups._abelianize, source, SlN, check=false) + + matrix_spn_map = let S = gens(target) + Dict(MatrixGroups.matrix_repr(g)=> word(g) for g in union(S, inv.(S))) + end + + # renumeration: + # (f1, f2, f3, f4, f5, f6) = (a₁, a₂, a₃, b₁, b₂, b₃) → + # → (b₃, a₃, b₂, a₂, b₁, a₁) + # hence p = [6, 4, 2, 5, 3, 1] + p = [reverse(2:2:N); reverse(1:2:N)] + + g = source([i]) + Mg = MatrixGroups.matrix_repr(ab(g))[p,p] + + return matrix_spn_map[Mg] +end diff --git a/src/matrix_groups/Spn.jl b/src/matrix_groups/Spn.jl index 66e658c..630a56a 100644 --- a/src/matrix_groups/Spn.jl +++ b/src/matrix_groups/Spn.jl @@ -28,19 +28,22 @@ function Base.show( ::MIME"text/plain", sp::Groups.AbstractFPGroupElement{<:SymplecticGroup{N}} ) where {N} + normalform!(sp) print(io, "$N×$N Symplectic matrix: ") KnuthBendix.print_repr(io, word(sp), alphabet(sp)) println(io) Base.print_array(io, matrix_repr(sp)) end +_offdiag_idcs(n) = ((i,j) for i in 1:n for j in 1:n if i ≠ j) + function symplectic_gens(N, T=Int8) iseven(N) || throw(ArgumentError("N needs to be even!")) n = N÷2 - a_ijs = [ElementarySymplectic{N}(:A, i,j, one(T)) for (i,j) in offdiagonal_indexing(n)] + a_ijs = [ElementarySymplectic{N}(:A, i,j, one(T)) for (i,j) in _offdiag_idcs(n)] b_is = [ElementarySymplectic{N}(:B, n+i,i, one(T)) for i in 1:n] - c_ijs = [ElementarySymplectic{N}(:B, n+i,j, one(T)) for (i,j) in offdiagonal_indexing(n)] + c_ijs = [ElementarySymplectic{N}(:B, n+i,j, one(T)) for (i,j) in _offdiag_idcs(n)] S = [a_ijs; b_is; c_ijs] diff --git a/test/homomorphisms.jl b/test/homomorphisms.jl index 8419b73..97f4736 100644 --- a/test/homomorphisms.jl +++ b/test/homomorphisms.jl @@ -2,8 +2,12 @@ function test_homomorphism(hom) F = hom.source @test isone(hom(one(F))) @test all(inv(hom(g)) == hom(inv(g)) for g in gens(F)) - @test all(isone(hom(g)*hom(inv(g))) for g in gens(F)) - @test all(hom(g*h) == hom(g)*hom(h) for g in gens(F) for h in gens(F)) + @test all(isone(hom(g) * hom(inv(g))) for g in gens(F)) + @test all(hom(g * h) == hom(g) * hom(h) for g in gens(F) for h in gens(F)) + @test all( + hom(inv(g * h)) == inv(hom(g * h)) == hom(inv(h)) * hom(inv(g)) for + g in gens(F) for h in gens(F) + ) end @testset "Homomorphisms" begin @@ -46,4 +50,22 @@ end test_homomorphism(hom) end + + @testset "Correctness of autπ₁Σ → SpN" begin + + GENUS = 3 + π₁Σ = Groups.SurfaceGroup(GENUS, 0) + autπ₁Σ = AutomorphismGroup(π₁Σ) + + SpN = MatrixGroups.SymplecticGroup{2GENUS}(Int8) + + hom = Groups.Homomorphism( + Groups._abelianize, + autπ₁Σ, + SpN, + check = false, + ) + + test_homomorphism(hom) + end end diff --git a/test/matrix_groups.jl b/test/matrix_groups.jl index fb13d6f..92555b2 100644 --- a/test/matrix_groups.jl +++ b/test/matrix_groups.jl @@ -33,4 +33,16 @@ using Groups.MatrixGroups end end end + + @testset "Sp(6, ℤ)" begin + Sp6 = MatrixGroups.SymplecticGroup{6}(Int8) + + @testset "GroupsCore conformance" begin + test_Group_interface(Sp6) + g = Sp6(rand(1:length(alphabet(Sp6)), 10)) + h = Sp6(rand(1:length(alphabet(Sp6)), 10)) + + test_GroupElement_interface(g, h) + end + end end From c9a4b00cebd12f7d3f3aaad722c9cd7ac986e9f9 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sat, 2 Apr 2022 16:19:20 +0200 Subject: [PATCH 12/14] bump to 0.7.3 --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index ac74155..93f0e92 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Groups" uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557" authors = ["Marek Kaluba "] -version = "0.7.2" +version = "0.7.3" [deps] Folds = "41a02a25-b8f0-4f67-bc48-60067656b558" From 6774d40d11200ae1604c4fca9fdc8a2e2a13e6e4 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 3 Apr 2022 16:58:49 +0200 Subject: [PATCH 13/14] more coverage: MatrixGroups --- src/matrix_groups/Spn.jl | 4 ++-- src/matrix_groups/abstract.jl | 4 ---- src/matrix_groups/eltary_symplectic.jl | 6 ------ test/matrix_groups.jl | 27 ++++++++++++++++++++++++++ 4 files changed, 29 insertions(+), 12 deletions(-) diff --git a/src/matrix_groups/Spn.jl b/src/matrix_groups/Spn.jl index 630a56a..ba7c8c5 100644 --- a/src/matrix_groups/Spn.jl +++ b/src/matrix_groups/Spn.jl @@ -28,8 +28,8 @@ function Base.show( ::MIME"text/plain", sp::Groups.AbstractFPGroupElement{<:SymplecticGroup{N}} ) where {N} - normalform!(sp) - print(io, "$N×$N Symplectic matrix: ") + Groups.normalform!(sp) + print(io, "$N×$N symplectic matrix: ") KnuthBendix.print_repr(io, word(sp), alphabet(sp)) println(io) Base.print_array(io, matrix_repr(sp)) diff --git a/src/matrix_groups/abstract.jl b/src/matrix_groups/abstract.jl index 5e19c86..1a4cf1f 100644 --- a/src/matrix_groups/abstract.jl +++ b/src/matrix_groups/abstract.jl @@ -22,10 +22,6 @@ Groups.rewriting(M::MatrixGroup) = alphabet(M) Base.hash(sl::MatrixGroupElement, h::UInt) = hash(matrix_repr(sl), hash(parent(sl), h)) -Base.getindex(sl::MatrixGroupElement, i, j) = matrix_repr(sl)[i,j] -# Base.iterate(sl::MatrixGroupElement) = iterate(sl.elts) -# Base.iterate(sl::MatrixGroupElement, state) = iterate(sl.elts, state) - function matrix_repr(m::MatrixGroupElement{N, T}) where {N, T} if isone(word(m)) return StaticArrays.SMatrix{N, N, T}(LinearAlgebra.I) diff --git a/src/matrix_groups/eltary_symplectic.jl b/src/matrix_groups/eltary_symplectic.jl index dba3b43..c1d3d68 100644 --- a/src/matrix_groups/eltary_symplectic.jl +++ b/src/matrix_groups/eltary_symplectic.jl @@ -22,12 +22,6 @@ function Base.show(io::IO, s::ElementarySymplectic) !isone(s.val) && print(io, "^$(s.val)") end -function Base.show(io::IO, ::MIME"text/plain", s::ElementarySymplectic) - print(io, s) - print(io, " → corresponding root: ") - print(io, Roots.Root(s)) -end - _ind(s::ElementarySymplectic{N}) where N = (s.i, s.j) _local_ind(N_half::Integer, i::Integer) = ifelse(i<=N_half, i, i-N_half) function _dual_ind(s::ElementarySymplectic{N}) where N diff --git a/test/matrix_groups.jl b/test/matrix_groups.jl index 92555b2..d882215 100644 --- a/test/matrix_groups.jl +++ b/test/matrix_groups.jl @@ -32,6 +32,19 @@ using Groups.MatrixGroups test_GroupElement_interface(g, h) end end + + + x = w*inv(w)*r + + @test length(word(x)) == 5 + @test size(x) == (3,3) + @test eltype(x) == Int8 + + @test contains(sprint(print, SL3Z), "special linear group of 3×3") + @test contains(sprint(show, MIME"text/plain"(), x), "SL{3,Int8} matrix:") + @test sprint(print, x) isa String + + @test length(word(x)) == 3 end @testset "Sp(6, ℤ)" begin @@ -44,5 +57,19 @@ using Groups.MatrixGroups test_GroupElement_interface(g, h) end + + @test contains(sprint(print, Sp6), "group of 6×6 symplectic matrices") + + x = gens(Sp6, 1) + x *= inv(x) * gens(Sp6, 2) + + @test length(word(x)) == 3 + @test size(x) == (6,6) + @test eltype(x) == Int8 + + @test contains(sprint(show, MIME"text/plain"(), x), "6×6 symplectic matrix:") + @test sprint(print, x) isa String + + @test length(word(x)) == 1 end end From 53773efac7a42323f25596f472b4ad4c7d9b3910 Mon Sep 17 00:00:00 2001 From: Marek Kaluba Date: Sun, 3 Apr 2022 18:40:02 +0200 Subject: [PATCH 14/14] fix and test issymplectic --- src/matrix_groups/Spn.jl | 9 ++++----- test/matrix_groups.jl | 4 ++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/matrix_groups/Spn.jl b/src/matrix_groups/Spn.jl index ba7c8c5..4287773 100644 --- a/src/matrix_groups/Spn.jl +++ b/src/matrix_groups/Spn.jl @@ -58,14 +58,13 @@ function _std_symplectic_form(m::AbstractMatrix) iseven(r) || return false n = r÷2 - Ω = zero(m) - for i in 1:n - Ω[2i-1:2i, 2i-1:2i] .= [0 -1; 1 0] - end + 𝕆 = zeros(eltype(m), n, n) + 𝕀 = one(eltype(m))*LinearAlgebra.I + Ω = [𝕆 -𝕀 + 𝕀 𝕆] return Ω end function issymplectic(mat::M, Ω = _std_symplectic_form(mat)) where M <: AbstractMatrix - r, c = size(mat) return Ω == transpose(mat) * Ω * mat end diff --git a/test/matrix_groups.jl b/test/matrix_groups.jl index d882215..316ab8c 100644 --- a/test/matrix_groups.jl +++ b/test/matrix_groups.jl @@ -71,5 +71,9 @@ using Groups.MatrixGroups @test sprint(print, x) isa String @test length(word(x)) == 1 + + for g in gens(Sp6) + @test MatrixGroups.issymplectic(MatrixGroups.matrix_repr(g)) + end end end