Skip to content

Commit

Permalink
Use @varnames_interface free_group
Browse files Browse the repository at this point in the history
- enables many variants of the `free_group` argument syntax
- adds `@free_group` macro
- ... but also changes `free_group` return value to consist of the
  group followed by generators, which matches e.g. polynomial rings
  but is different from all other group constructors...
  • Loading branch information
fingolfin committed Feb 5, 2024
1 parent e4bb400 commit b625163
Show file tree
Hide file tree
Showing 15 changed files with 67 additions and 81 deletions.
4 changes: 1 addition & 3 deletions docs/src/Groups/quotients.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@ This is the typical way to build finitely presented groups.

**Example:**
```jldoctest
julia> F=free_group(2);
julia> (f1,f2)=gens(F);
julia> F, (f1,f2) = free_group(2);
julia> G,_=quo(F,[f1^2,f2^3,(f1*f2)^2]);
Expand Down
10 changes: 5 additions & 5 deletions experimental/GModule/Cohomology.jl
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ function fp_group_with_isomorphism(C::GModule)
#TODO: better for PcGroup!!!
if !isdefined(C, :F)
if order(Group(C)) == 1
C.F = free_group(0)
C.F = free_group(0)[1]
C.mF = hom(C.F, Group(C), gens(C.F), elem_type(Group(C))[])
else
C.F, C.mF = fp_group_with_isomorphism(gens(Group(C)))
Expand Down Expand Up @@ -1780,7 +1780,7 @@ function pc_group_with_isomorphism(M::FinGenAbGroup; refine::Bool = true)
mM = hom(M, M, gens(M))
end

G = free_group(ngens(M))
G, _ = free_group(ngens(M))
h = rels(M)
@assert !any(x->h[x,x] == 1, 1:ncols(h))

Expand Down Expand Up @@ -1848,7 +1848,7 @@ function pc_group_with_isomorphism(M::AbstractAlgebra.FPModule{<:FinFieldElem};
k = base_ring(M)
p = characteristic(k)

G = free_group(degree(k)*dim(M))
G, _ = free_group(degree(k)*dim(M))

C = GAP.Globals.CombinatorialCollector(G.X,
GAP.Obj([p for i=1:ngens(G)], recursive = true))
Expand Down Expand Up @@ -1943,7 +1943,7 @@ function extension(c::CoChain{2,<:Oscar.GAPGroupElem})
mfM = inv(isomorphism(FPGroup, M))
fM = domain(mfM)

N = free_group(ngens(G) + ngens(fM))
N, _ = free_group(ngens(G) + ngens(fM))
function fMtoN(g)
return reduce(*, [gen(N, ngens(G)+abs(w))^sign(w) for w = word(g)], init = one(N))
end
Expand Down Expand Up @@ -2000,7 +2000,7 @@ function extension(::Type{PcGroup}, c::CoChain{2,<:Oscar.PcGroupElem})
iac = inv_action(C)
fM, mfM = pc_group_with_isomorphism(M)

N = free_group(ngens(G) + ngens(fM))
N, _ = free_group(ngens(G) + ngens(fM))
Gp = GAP.Globals.Pcgs(G.X)
@assert length(Gp) == ngens(G)
# @assert all(x->Gp[x] == gen(G, x).X, 1:ngens(G))
Expand Down
2 changes: 1 addition & 1 deletion src/Combinatorics/SimplicialComplexes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ julia> describe(pi_1)
"""
function fundamental_group(K::SimplicialComplex)
ngens, relations = pm_object(K).FUNDAMENTAL_GROUP
F = free_group(ngens)
F = free_group(ngens)[1]
nrels = length(relations)
if nrels==0
return F
Expand Down
32 changes: 16 additions & 16 deletions src/Groups/GAPGroups.jl
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ Return `true` if `G` is finite, and `false` otherwise.
julia> is_finite(symmetric_group(5))
true
julia> is_finite(free_group(2))
julia> is_finite(free_group(2)[1])
false
```
Expand All @@ -84,7 +84,7 @@ Return `true` if `g` has finite order, and `false` otherwise.
julia> is_finiteorder(gen(symmetric_group(5), 1))
true
julia> is_finiteorder(gen(free_group(2), 1))
julia> is_finiteorder(gen(free_group(2)[1], 1))
false
```
Expand Down Expand Up @@ -116,7 +116,7 @@ julia> order(g)
julia> order(gen(g, 1))
3
julia> g = free_group(1);
julia> g, _ = free_group(1);
julia> is_finite(g)
false
Expand Down Expand Up @@ -425,7 +425,7 @@ Return whether generators for the group `G` are known.
# Examples
```jldoctest
julia> F = free_group(2)
julia> F, _ = free_group(2)
Free group of rank 2
julia> has_gens(F)
Expand Down Expand Up @@ -1606,7 +1606,7 @@ end
#julia> rank(symmetric_group(5))
#2
#
#julia> rank(free_group(5))
#julia> rank(free_group(5)[1])
#5
#```
#`"""
Expand All @@ -1627,7 +1627,7 @@ Return whether `G` is a finitely generated group.
# Examples
```jldoctest
julia> F = free_group(2)
julia> F = free_group(2)[1]
Free group of rank 2
julia> is_finitely_generated(F)
Expand All @@ -1654,7 +1654,7 @@ false
#
## Examples
#```jldoctest
#julia> F = free_group(2)
#julia> F, _ = free_group(2)
#<free group on the generators [ f1, f2 ]>
#
#julia> is_free(F)
Expand Down Expand Up @@ -1682,7 +1682,7 @@ subgroups.
# Examples
```jldoctest
julia> f = free_group(2); is_full_fp_group(f)
julia> f = free_group(2)[1]; is_full_fp_group(f)
true
julia> s = sub(f, gens(f))[1]; is_full_fp_group(s)
Expand Down Expand Up @@ -1710,7 +1710,7 @@ full finitely presented group, see [`is_full_fp_group`](@ref).
# Examples
```jldoctest
julia> f = free_group(2); (x, y) = gens(f);
julia> f, (x,y) = free_group(2);
julia> q = quo(f, [x^2, y^2, comm(x, y)])[1]; relators(q)
3-element Vector{FPGroupElem}:
Expand Down Expand Up @@ -1761,7 +1761,7 @@ In all other cases, `init` is ignored.
# Examples
```jldoctest
julia> F = free_group(2); F1 = gen(F, 1); F2 = gen(F, 2);
julia> F, (F1, F2) = free_group(2);
julia> imgs = gens(symmetric_group(4))
2-element Vector{PermGroupElem}:
Expand Down Expand Up @@ -1854,7 +1854,7 @@ Return the syllables of `g` as a list of pairs `gen => exp` where
# Examples
```jldoctest
julia> F = free_group(2); F1, F2 = gens(F);
julia> F, (F1, F2) = free_group(2);
julia> syllables(F1^5*F2^-3)
2-element Vector{Pair{Int64, Int64}}:
Expand Down Expand Up @@ -1887,7 +1887,7 @@ numbers.
# Examples
```jldoctest
julia> F = free_group(2); F1, F2 = gens(F);
julia> F, (F1, F2) = free_group(2);
julia> letters(F1^5*F2^-3)
8-element Vector{Int64}:
Expand Down Expand Up @@ -1931,7 +1931,7 @@ if `g` is an element of a free group, otherwise a exception is thrown.
# Examples
```jldoctest
julia> F = free_group(2); F1 = gen(F, 1); F2 = gen(F, 2);
julia> F, (F1, F2) = free_group(2);
julia> length(F1*F2^-2)
3
Expand Down Expand Up @@ -1962,7 +1962,7 @@ nonzero then `pairs` is the vector of syllables of `x`, see [`syllables`](@ref).
# Examples
```jldoctest
julia> G = free_group(2); pairs = [1 => 3, 2 => -1];
julia> G = free_group(2)[1]; pairs = [1 => 3, 2 => -1];
julia> x = G(pairs)
f1^3*f2^-1
Expand Down Expand Up @@ -2092,10 +2092,10 @@ julia> describe(g)
julia> describe(sylow_subgroup(g,2)[1])
"C2 x D8"
julia> describe(sylow_subgroup(g, 3)[1])
julia> describe(sylow_subgroup(g,3)[1])
"C3 x C3"
julia> describe(free_group(3))
julia> describe(free_group(3)[1])
"a free group of rank 3"
```
Expand Down
30 changes: 12 additions & 18 deletions src/Groups/group_constructors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,9 @@ projective_omega_group(n::Int, q::Int) = projective_omega_group(0, n, q)
free_group(L::Vector{<:VarName}) -> FPGroup
free_group(L::VarName...) -> FPGroup
TODO: update the list of argument variants to match new reality; maybe also point to
point to relevant AA docs
The first form returns the free group of rank `n`, where the generators are
printed as `s1`, `s2`, ..., the default being `f1`, `f2`, ...
If `eltype` has the value `:syllable` then each element in the free group is
Expand All @@ -472,30 +475,21 @@ where the `i`-th generator is printed as `L[i]`.
!!! warning "Note"
Variables named like the group generators are *not* created by this function.
"""
function free_group(n::Int, s::VarName = :f; eltype::Symbol = :letter)
@req n >= 0 "n must be a non-negative integer"
function free_group(L::Vector{<:Symbol}; eltype::Symbol = :letter)
J = GAP.GapObj(L, recursive = true)
if eltype == :syllable
G = FPGroup(GAP.Globals.FreeGroup(n, GAP.GapObj(s); FreeGroupFamilyType = GapObj("syllable"))::GapObj)
G = FPGroup(GAP.Globals.FreeGroup(J; FreeGroupFamilyType = GapObj("syllable"))::GapObj)
elseif eltype == :letter
G = FPGroup(GAP.Globals.FreeGroup(J)::GapObj)
else
G = FPGroup(GAP.Globals.FreeGroup(n, GAP.GapObj(s))::GapObj)
error("eltype must be :letter or :letter, not ", eltype)
end
GAP.Globals.SetRankOfFreeGroup(G.X, n)
return G
end

function free_group(L::Vector{<:VarName})
J = GAP.GapObj(L, recursive = true)
G = FPGroup(GAP.Globals.FreeGroup(J)::GapObj)
GAP.Globals.SetRankOfFreeGroup(G.X, length(J))
return G
return G, gens(G)
end

function free_group(L::VarName...)
J = GAP.GapObj(L, recursive = true)
G = FPGroup(GAP.Globals.FreeGroup(J)::GapObj)
GAP.Globals.SetRankOfFreeGroup(G.X, length(J))
return G
end
AbstractAlgebra.@varnames_interface free_group(s)


# FIXME: a function `free_abelian_group` with the same signature is
# already being defined by Hecke
Expand Down
4 changes: 2 additions & 2 deletions src/Groups/homomorphisms.jl
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,7 @@ function isomorphism(::Type{FPGroup}, A::FinGenAbGroup)
# Known isomorphisms are cached in the attribute `:isomorphisms`.
isos = get_attribute!(Dict{Type, Any}, A, :isomorphisms)::Dict{Type, Any}
return get!(isos, FPGroup) do
G = free_group(ngens(A); eltype = :syllable)
G = free_group(ngens(A); eltype = :syllable)[1]
R = rels(A)
s = vcat(elem_type(G)[i*j*inv(i)*inv(j) for i = gens(G) for j = gens(G) if i != j],
elem_type(G)[prod([gen(G, i)^R[j,i] for i=1:ngens(A) if !iszero(R[j,i])], init = one(G)) for j=1:nrows(R)])
Expand Down Expand Up @@ -837,7 +837,7 @@ generators, the number of relators, and the relator lengths.
# Examples
```jldoctest
julia> F = free_group(3)
julia> F = free_group(3)[1]
Free group of rank 3
julia> G = quo(F, [gen(F,1)])[1]
Expand Down
2 changes: 1 addition & 1 deletion src/Groups/sub.jl
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ true
julia> typeof(F)
PcGroup
julia> typeof(maximal_abelian_quotient(free_group(1))[1])
julia> typeof(maximal_abelian_quotient(free_group(1)[1])[1])
FPGroup
julia> typeof(maximal_abelian_quotient(PermGroup, G)[1])
Expand Down
1 change: 1 addition & 0 deletions src/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# execute: sort < src/exports.jl > dummy ; mv dummy src/exports.jl
export *
export @check
export @free_group
export @pbw_relations
export @perm
export @permutation_group
Expand Down
2 changes: 1 addition & 1 deletion test/Combinatorics/SimplicialComplexes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
@test minimal_nonfaces(sphere) == [Set{Int}([1, 2, 3, 4])]
R, _ = polynomial_ring(ZZ, ["a", "x", "i_7", "n"])
@test stanley_reisner_ideal(R, sphere) == ideal([R([1], [[1, 1, 1, 1]])])
@test is_isomorphic(fundamental_group(sphere), free_group())
@test is_trivial(fundamental_group(sphere))

# from #1440, make sure empty columns at the end are kept
sc = simplicial_complex([[1, 2, 4], [2, 3, 4]])
Expand Down
2 changes: 1 addition & 1 deletion test/Groups/conformance.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
L = [ alternating_group(5), cyclic_group(18), SL(3,3), free_group(0), free_group(1), free_group(2) ]
L = [ alternating_group(5), cyclic_group(18), SL(3,3), free_group(0)[1], free_group(1)[1], free_group(2)[1] ]

import Oscar.AbstractAlgebra
import Oscar.AbstractAlgebra: Group
Expand Down
16 changes: 8 additions & 8 deletions test/Groups/constructors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -129,38 +129,38 @@ end
@test_throws ArgumentError mathieu_group(25)


F = free_group("x","y")
F, = free_group(["x","y"])
@test F isa FPGroup
@test_throws InfiniteOrderError{FPGroup} order(F)
@test_throws ArgumentError index(F, trivial_subgroup(F)[1])
@test_throws MethodError degree(F)
@test !is_finite(F)
@test !is_abelian(F)

F = free_group(:x,:y)
F, = free_group([:x,:y])
@test F isa FPGroup
@test_throws InfiniteOrderError{FPGroup} order(F)
@test_throws ArgumentError index(F, trivial_subgroup(F)[1])
@test_throws MethodError degree(F)
@test !is_finite(F)
@test !is_abelian(F)

F = free_group("x",:y)
F, = free_group("x"=>1:3)
@test F isa FPGroup

F = free_group(2)
F, = free_group(2)
@test F isa FPGroup

F = free_group(["x","y"])
F, = free_group(["x","y"])
@test F isa FPGroup

F = free_group([:x,:y])
F, = free_group([:x,:y])
@test F isa FPGroup

F = free_group(3,"y")
F, = free_group(3,"y")
@test F isa FPGroup

F = free_group(3,:y)
F, = free_group(3,:y)
@test F isa FPGroup

@test_throws ArgumentError free_group(-1)
Expand Down
16 changes: 8 additions & 8 deletions test/Groups/describe.jl
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,12 @@ end
end

@testset "describe() for free groups" begin
@test describe(free_group(0)) == "1"
@test describe(free_group(1)) == "Z"
@test describe(free_group(2)) == "a free group of rank 2"
@test describe(free_group(3)) == "a free group of rank 3"
@test describe(free_group(0)[1]) == "1"
@test describe(free_group(1)[1]) == "Z"
@test describe(free_group(2)[1]) == "a free group of rank 2"
@test describe(free_group(3)[1]) == "a free group of rank 3"

F = free_group(4)
F, _ = free_group(4)
subs = [sub(F, gens(F)[1:n])[1] for n in 0:4]
@test describe(subs[1]) == "1"
@test describe(subs[2]) == "Z"
Expand All @@ -60,16 +60,16 @@ end
end

@testset "describe() for finitely presented groups" begin
F = free_group(1)
F, _ = free_group(1)
@test describe(direct_product(F, F)) == "Z x Z"

F = free_group(2)
F, _ = free_group(2)
G = quo(F, [gen(F,2)])[1]
@test describe(G) == "Z"
G = quo(F, [gen(F,2)/gen(F,1)])[1]
@test describe(G) == "Z"

F = free_group(3)
F, _ = free_group(3)
G, _ = quo(F, [gen(F,1)^2,gen(F,2)^2])
@test describe(G) == "a finitely presented infinite group"
is_abelian(G)
Expand Down
Loading

0 comments on commit b625163

Please sign in to comment.