Skip to content

Commit

Permalink
change the handling of the init argument in map_word (oscar-syste…
Browse files Browse the repository at this point in the history
…m#3892)

* change the handling of the `init` argument in `map_word`

- use `init` whenever it is different from `nothing`
  (this differs from the documented behaviour for the Julia functions
  `sum` and `prod`)

- extend the `map_word` method for `PcGroupElem` to `SubPcGroupElem`

- add a special `map_word` method where the images are `FinGenAbGroupElem`s,
  where evaluating means to add images instead of multiplying them

- extend the tests

* improve the documentation
  • Loading branch information
ThomasBreuer authored Jul 1, 2024
1 parent a760cbb commit 870b624
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 27 deletions.
2 changes: 1 addition & 1 deletion docs/src/Groups/pcgroup.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ DocTestSetup = Oscar.doctestsetup()
```@docs
PcGroup
PcGroupElem
map_word(g::PcGroupElem, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
map_word(g::Union{PcGroupElem, SubPcGroupElem}, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
```

Julia has the following functions that allow to generate polycyclic groups:
Expand Down
79 changes: 69 additions & 10 deletions src/Groups/GAPGroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1930,7 +1930,7 @@ end


@doc raw"""
map_word(g::FPGroupElem, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
map_word(g::Union{FPGroupElem, SubFPGroupElem}, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
map_word(v::Vector{Union{Int, Pair{Int, Int}}}, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
Return the product $R_1 R_2 \cdots R_n$
Expand All @@ -1944,7 +1944,9 @@ and $R_j =$ `imgs[`$i_j$`]`$^{e_j}$.
If `g` is an element of (a subgroup of) a finitely presented group
then the result is defined as `map_word` applied to a representing element
of the underlying free group.
of the underlying free group of `full_group(parent(g))`.
In particular, `genimgs` are interpreted as the images of the generators
of this free group, not of `gens(parent(g))`.
If the first argument is a vector `v` of integers $k_i$ or pairs `k_i => e_i`,
respectively,
Expand All @@ -1957,9 +1959,13 @@ to be the inverses of the corresponding entries in `genimgs`,
and the function will use (and set) these entries in order to avoid
calling `inv` (more than once) for entries of `genimgs`.
If `init` is different from `nothing` then the product gets initialized with
`init`.
If `v` has length zero then `init` is returned if also `genimgs` has length
zero, otherwise `one(genimgs[1])` is returned.
In all other cases, `init` is ignored.
Thus the intended value for the empty word must be specified as `init`
whenever it is possible that the elements in `genimgs` do not support `one`.
# Examples
```jldoctest
Expand All @@ -1979,6 +1985,9 @@ julia> map_word(F2, imgs)
julia> map_word(one(F), imgs)
()
julia> map_word(one(F), imgs, init = imgs[1])
(1,2,3,4)
julia> invs = Vector(undef, 2);
julia> map_word(F1^-2*F2, imgs, genimgs_inv = invs)
Expand All @@ -1988,7 +1997,6 @@ julia> invs
2-element Vector{Any}:
(1,4,3,2)
#undef
```
"""
function map_word(g::Union{FPGroupElem, SubFPGroupElem}, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
Expand Down Expand Up @@ -2021,13 +2029,13 @@ end


@doc raw"""
map_word(g::PcGroupElem, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
map_word(g::Union{PcGroupElem, SubPcGroupElem}, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
Return the product $R_1 R_2 \cdots R_n$ that is described by `g`,
which is a product of the form
$g_{i_1}^{e_1} g_{i_2}^{e_2} \cdots g_{i_n}^{e_n}$
where $g_i$ is the $i$-th entry in the defining polycyclic generating sequence
of $G$ and the $e_i$ are nonzero integers,
of `full_group(parent(g))` and the $e_i$ are nonzero integers,
and $R_j =$ `imgs[`$i_j$`]`$^{e_j}$.
# Examples
Expand All @@ -2042,7 +2050,7 @@ julia> map_word(g, gens(free_group(:x, :y)))
x*y^4
```
"""
function map_word(g::PcGroupElem, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
function map_word(g::Union{PcGroupElem, SubPcGroupElem}, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
G = parent(g)
Ggens = gens(G)
if length(Ggens) == 0
Expand All @@ -2061,11 +2069,38 @@ function map_word(g::PcGroupElem, genimgs::Vector; genimgs_inv::Vector = Vector(
end

function map_word(v::Union{Vector{Int}, Vector{Pair{Int, Int}}, Vector{Any}}, genimgs::Vector; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
length(genimgs) == 0 && (@assert length(v) == 0; return init)
length(v) == 0 && return one(genimgs[1])
return prod(i -> _map_word_syllable(i, genimgs, genimgs_inv), v)
if length(v) == 0
# If `init` is given then return it.
init !== nothing && return init
# Otherwise try the `one` of one of the `genimgs`
@req length(genimgs) != 0 "no `init` given in `map_word` without generators"
return one(genimgs[1])
end
res = prod(i -> _map_word_syllable(i, genimgs, genimgs_inv), v)
if init !== nothing
res = init * res
end
return res
end

# Support mapping to `FinGenAbGroupElem`:
# We use `+` instead of `*`, and scalar multiplication instead of powering.
function map_word(v::Union{Vector{Int}, Vector{Pair{Int, Int}}, Vector{Any}}, genimgs::Vector{FinGenAbGroupElem}; genimgs_inv::Vector = Vector(undef, length(genimgs)), init = nothing)
if length(v) == 0
# If `init` is given then return it.
init !== nothing && return init
# Otherwise use the `zero` of one of the `genimgs`.
@req length(genimgs) != 0 "no `init` given in `map_word` without generators"
return zero(parent(genimgs[1]))
end
res = sum(i -> _map_word_syllable_additive(i, genimgs, genimgs_inv), v)
if init !== nothing
res = init + res
end
return res
end


function _map_word_syllable(vi::Int, genimgs::Vector, genimgs_inv::Vector)
vi > 0 && (@assert vi <= length(genimgs); return genimgs[vi])
vi = -vi
Expand All @@ -2090,6 +2125,30 @@ function _map_word_syllable(vi::Pair{Int, Int}, genimgs::Vector, genimgs_inv::Ve
end


function _map_word_syllable_additive(vi::Int, genimgs::Vector, genimgs_inv::Vector)
vi > 0 && (@assert vi <= length(genimgs); return genimgs[vi])
vi = -vi
@assert vi <= length(genimgs)
isassigned(genimgs_inv, vi) && return genimgs_inv[vi]
res = -genimgs[vi]
genimgs_inv[vi] = res
return res
end

function _map_word_syllable_additive(vi::Pair{Int, Int}, genimgs::Vector, genimgs_inv::Vector)
x = vi[1]
@assert (x > 0 && x <= length(genimgs))
e = vi[2]
e > 1 && return e * genimgs[x]
e == 1 && return genimgs[x]
isassigned(genimgs_inv, x) && return (-e) * genimgs_inv[x]
res = -genimgs[x]
genimgs_inv[x] = res
e == -1 && return res
return (-e) * res
end


@doc raw"""
syllables(g::Union{FPGroupElem, SubFPGroupElem})
Expand Down
52 changes: 36 additions & 16 deletions test/Groups/homomorphisms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -145,27 +145,41 @@ end
rels = [F1^2, F2^2, comm(F1, F2)]
FP = quo(F, rels)[1]

# map an element of a free group or a f.p. group ...
# map an element of a (subgroup of a) free group or a f.p. group ...
for f in [F, FP]
f1, f2 = gens(f)
of = one(f)
s1 = gen(sub(f, [f1])[1], 1)

# ... to an element of a permutation group or to a rational number
for imgs in [gens(symmetric_group(4)), QQFieldElem[2, 3]]
# ... to an element of a permutation group or to a rational number ...
for imgs in [gens(symmetric_group(4)),
QQFieldElem[2, 3]]
g1 = imgs[1]
g2 = imgs[2]
for (x, w) in [(f1, g1), (f2, g2), (f2^2*f1^-3, g2^2*g1^-3)]
for (x, w) in [(f1, g1), (f2, g2), (f2^2*f1^-3, g2^2*g1^-3),
(s1^2, g1^2)]
@test map_word(x, imgs) == w
@test map_word(x, imgs, init = g1) == g1*w # `init` is used
end
@test map_word(of, imgs, init = 0) == one(g1) # `init` is ignored
@test map_word(of, imgs, init = 0) == 0 # `init` is returned
end

# ... or to an element in an (additive) abelian group
imgs = gens(abelian_group(3, 5))
g1 = imgs[1]
g2 = imgs[2]
for (x, w) in [(f1, g1), (f2, g2), (f2^2*f1^-3, 2*g2-3*g1)]
@test map_word(x, imgs) == w
@test map_word(x, imgs, init = g1) == g1+w # `init` is used
end
@test map_word(of, imgs, init = 0) == 0 # `init` is returned
end

# empty list of generators
T = free_group(0)
@test_throws ArgumentError map_word(one(T), Int[]) # no `init`
@test map_word(one(T), [], init = 1) == 1 # `init` is returned
@test map_word(one(F), gens(F), init = 1) == one(F) # `init` is ignored
@test_throws ArgumentError map_word(one(T), Int[]) # no `init`
@test map_word(one(T), [], init = 1) == 1 # `init` is returned
@test map_word(one(F), gens(F)) == one(F) # works without `init`

# wrong number of images
@test_throws AssertionError map_word(one(F), [])
Expand Down Expand Up @@ -196,26 +210,32 @@ end

# empty list of generators
@test map_word([], [], init = 0) == 0 # `init` is returned
@test map_word([], [2, 3], init = 0) == 1 # `init` is ignored
@test map_word([], [2, 3], init = 0) == 0 # `init` is returned
@test map_word([], [2, 3]) == 1 # no `init` given, try `one`
G = abelian_group(2)
@test map_word([], gens(G)) == zero(G)

# wrong number of images
@test_throws AssertionError map_word([3], [])
@test_throws AssertionError map_word([-3], [2, 3])
@test_throws AssertionError map_word([3 => 1], [2, 3])
end

@testset "map_word for pc groups" begin
@testset "map_word for (sub) pc groups" begin
for G in [ PcGroup(symmetric_group(4)), # GAP Pc Group
# abelian_group(PcGroup, [2, 3, 4]), # problem with gens vs. pcgs
abelian_group(PcGroup, [0, 3, 4]) ] # GAP Pcp group
n = number_of_generators(G)
F = free_group(n)
for x in [one(G), rand(G)]
img = map_word(x, gens(F))
@test x == map_word(img, gens(G))
invs = Vector(undef, n)
img = map_word(x, gens(F), genimgs_inv = invs)
@test x == map_word(img, gens(G))
S = sub(G, gens(G))[1]
for g in [G, S]
for x in [one(g), rand(g)]
img = map_word(x, gens(F))
@test x == map_word(img, gens(g))
invs = Vector(undef, n)
img = map_word(x, gens(F), genimgs_inv = invs)
@test x == map_word(img, gens(g))
end
end
end
end
Expand Down

0 comments on commit 870b624

Please sign in to comment.