1
0
mirror of https://github.com/kalmarek/Groups.jl.git synced 2024-11-19 06:30:29 +01:00

add Homomorphisms

This commit is contained in:
Marek Kaluba 2022-04-02 14:24:01 +02:00
parent cb933c3f87
commit dd6588f018
No known key found for this signature in database
GPG Key ID: 8BF1A3855328FC15
5 changed files with 194 additions and 2 deletions

View File

@ -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

23
src/abelianize.jl Normal file
View File

@ -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

114
src/homomorphisms.jl Normal file
View File

@ -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)")

49
test/homomorphisms.jl Normal file
View File

@ -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

View File

@ -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")