mirror of
https://github.com/kalmarek/Groups.jl.git
synced 2024-12-26 02:20:30 +01:00
Merge pull request #22 from kalmarek/enh/MatrixGroups
Enh/matrix groups
This commit is contained in:
commit
2e544d623f
2
.github/workflows/runtests.yml
vendored
2
.github/workflows/runtests.yml
vendored
@ -13,7 +13,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
version:
|
version:
|
||||||
- '1.3'
|
- '1.6'
|
||||||
- '1'
|
- '1'
|
||||||
- 'nightly'
|
- 'nightly'
|
||||||
os:
|
os:
|
||||||
|
18
Project.toml
18
Project.toml
@ -1,29 +1,31 @@
|
|||||||
name = "Groups"
|
name = "Groups"
|
||||||
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
uuid = "5d8bd718-bd84-11e8-3b40-ad14f4a32557"
|
||||||
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
authors = ["Marek Kaluba <kalmar@amu.edu.pl>"]
|
||||||
version = "0.7.2"
|
version = "0.7.3"
|
||||||
|
|
||||||
[deps]
|
[deps]
|
||||||
|
Folds = "41a02a25-b8f0-4f67-bc48-60067656b558"
|
||||||
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"
|
GroupsCore = "d5909c97-4eac-4ecc-a3dc-fdd0858a4120"
|
||||||
KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a"
|
KnuthBendix = "c2604015-7b3d-4a30-8a26-9074551ec60a"
|
||||||
|
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
|
||||||
|
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
|
||||||
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
|
||||||
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
|
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
|
||||||
ThreadsX = "ac1d9e8a-700a-412c-b207-f0111f4b6c0d"
|
StaticArrays = "90137ffa-7385-5640-81b9-e52037218182"
|
||||||
|
|
||||||
[compat]
|
[compat]
|
||||||
AbstractAlgebra = "0.22"
|
Folds = "0.2.7"
|
||||||
GroupsCore = "0.4"
|
GroupsCore = "0.4"
|
||||||
KnuthBendix = "0.3"
|
KnuthBendix = "0.3"
|
||||||
OrderedCollections = "1"
|
OrderedCollections = "1"
|
||||||
PermutationGroups = "0.3"
|
PermutationGroups = "0.3"
|
||||||
ThreadsX = "0.1"
|
StaticArrays = "1"
|
||||||
julia = "1.3"
|
julia = "1.6"
|
||||||
|
|
||||||
[extras]
|
[extras]
|
||||||
AbstractAlgebra = "c3fe647b-3220-5bb0-a1ea-a7954cac585d"
|
|
||||||
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf"
|
||||||
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
|
PermutationGroups = "8bc5a954-2dfc-11e9-10e6-cd969bffa420"
|
||||||
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
|
||||||
|
|
||||||
[targets]
|
[targets]
|
||||||
test = ["Test", "BenchmarkTools", "AbstractAlgebra", "PermutationGroups"]
|
test = ["Test", "BenchmarkTools"]
|
||||||
|
@ -1,24 +1,40 @@
|
|||||||
module Groups
|
module Groups
|
||||||
|
|
||||||
|
import Folds
|
||||||
|
import Logging
|
||||||
|
|
||||||
using GroupsCore
|
using GroupsCore
|
||||||
using ThreadsX
|
import GroupsCore.Random
|
||||||
import KnuthBendix
|
|
||||||
import KnuthBendix: AbstractWord, Alphabet, Word
|
|
||||||
import KnuthBendix: alphabet
|
|
||||||
import Random
|
|
||||||
|
|
||||||
import OrderedCollections: OrderedSet
|
import OrderedCollections: OrderedSet
|
||||||
|
|
||||||
export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElement, SpecialAutomorphismGroup
|
import KnuthBendix
|
||||||
|
import KnuthBendix: AbstractWord, Alphabet, Word
|
||||||
|
import KnuthBendix: alphabet
|
||||||
|
|
||||||
|
export MatrixGroups
|
||||||
|
|
||||||
|
export Alphabet, AutomorphismGroup, FreeGroup, FreeGroup, FPGroup, FPGroupElement, SpecialAutomorphismGroup, Homomorphism
|
||||||
|
|
||||||
export alphabet, evaluate, word, gens
|
export alphabet, evaluate, word, gens
|
||||||
|
|
||||||
|
# general constructions
|
||||||
|
include(joinpath("constructions", "constructions.jl"))
|
||||||
|
import .Constructions
|
||||||
|
|
||||||
include("types.jl")
|
include("types.jl")
|
||||||
include("hashing.jl")
|
include("hashing.jl")
|
||||||
include("normalform.jl")
|
include("normalform.jl")
|
||||||
include("autgroups.jl")
|
include("autgroups.jl")
|
||||||
|
include("homomorphisms.jl")
|
||||||
|
|
||||||
include("groups/sautFn.jl")
|
include("aut_groups/sautFn.jl")
|
||||||
include("groups/mcg.jl")
|
include("aut_groups/mcg.jl")
|
||||||
|
|
||||||
|
include("matrix_groups/MatrixGroups.jl")
|
||||||
|
using .MatrixGroups
|
||||||
|
|
||||||
|
include("abelianize.jl")
|
||||||
|
|
||||||
include("wl_ball.jl")
|
include("wl_ball.jl")
|
||||||
end # of module Groups
|
end # of module Groups
|
||||||
|
69
src/abelianize.jl
Normal file
69
src/abelianize.jl
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
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
|
@ -10,7 +10,10 @@ function SpecialAutomorphismGroup(F::FreeGroup; ordering = KnuthBendix.LenLex, k
|
|||||||
maxrules = 1000*n
|
maxrules = 1000*n
|
||||||
|
|
||||||
rws = KnuthBendix.RewritingSystem(rels, ordering(A))
|
rws = KnuthBendix.RewritingSystem(rels, ordering(A))
|
||||||
|
Logging.with_logger(Logging.NullLogger()) do
|
||||||
|
# the rws is not confluent, let's suppress warning about it
|
||||||
KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...)
|
KnuthBendix.knuthbendix!(rws; maxrules=maxrules, kwargs...)
|
||||||
|
end
|
||||||
return AutomorphismGroup(F, S, rws, ntuple(i -> gens(F, i), n))
|
return AutomorphismGroup(F, S, rws, ntuple(i -> gens(F, i), n))
|
||||||
end
|
end
|
||||||
|
|
10
src/constructions/constructions.jl
Normal file
10
src/constructions/constructions.jl
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
module Constructions
|
||||||
|
|
||||||
|
using GroupsCore
|
||||||
|
import GroupsCore.Random
|
||||||
|
|
||||||
|
include("direct_product.jl")
|
||||||
|
include("direct_power.jl")
|
||||||
|
include("wreath_product.jl")
|
||||||
|
|
||||||
|
end # of module Constructions
|
112
src/constructions/direct_power.jl
Normal file
112
src/constructions/direct_power.jl
Normal file
@ -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, ", "), " )")
|
102
src/constructions/direct_product.jl
Normal file
102
src/constructions/direct_product.jl
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
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, ",")) )")
|
136
src/constructions/wreath_product.jl
Normal file
136
src/constructions/wreath_product.jl
Normal file
@ -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))
|
114
src/homomorphisms.jl
Normal file
114
src/homomorphisms.jl
Normal 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)")
|
19
src/matrix_groups/MatrixGroups.jl
Normal file
19
src/matrix_groups/MatrixGroups.jl
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
module MatrixGroups
|
||||||
|
|
||||||
|
import LinearAlgebra # Identity matrix
|
||||||
|
|
||||||
|
using StaticArrays
|
||||||
|
|
||||||
|
using GroupsCore
|
||||||
|
import GroupsCore.Random # GroupsCore rand
|
||||||
|
using ..Groups
|
||||||
|
using Groups.KnuthBendix
|
||||||
|
|
||||||
|
export SpecialLinearGroup, SymplecticGroup
|
||||||
|
|
||||||
|
include("abstract.jl")
|
||||||
|
|
||||||
|
include("SLn.jl")
|
||||||
|
include("Spn.jl")
|
||||||
|
|
||||||
|
end # module
|
42
src/matrix_groups/SLn.jl
Normal file
42
src/matrix_groups/SLn.jl
Normal file
@ -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))
|
70
src/matrix_groups/Spn.jl
Normal file
70
src/matrix_groups/Spn.jl
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
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}
|
||||||
|
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))
|
||||||
|
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 _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 _offdiag_idcs(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
|
||||||
|
𝕆 = zeros(eltype(m), n, n)
|
||||||
|
𝕀 = one(eltype(m))*LinearAlgebra.I
|
||||||
|
Ω = [𝕆 -𝕀
|
||||||
|
𝕀 𝕆]
|
||||||
|
return Ω
|
||||||
|
end
|
||||||
|
|
||||||
|
function issymplectic(mat::M, Ω = _std_symplectic_form(mat)) where M <: AbstractMatrix
|
||||||
|
return Ω == transpose(mat) * Ω * mat
|
||||||
|
end
|
40
src/matrix_groups/abstract.jl
Normal file
40
src/matrix_groups/abstract.jl
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
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} =
|
||||||
|
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
|
||||||
|
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))
|
||||||
|
|
||||||
|
function matrix_repr(m::MatrixGroupElement{N, T}) where {N, T}
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
28
src/matrix_groups/eltary_matrices.jl
Normal file
28
src/matrix_groups/eltary_matrices.jl
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
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}(LinearAlgebra.I)
|
||||||
|
m[e.i, e.j] = e.val
|
||||||
|
x = StaticArrays.SMatrix{N, N}(m)
|
||||||
|
return x
|
||||||
|
end
|
76
src/matrix_groups/eltary_symplectic.jl
Normal file
76
src/matrix_groups/eltary_symplectic.jl
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
_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}(LinearAlgebra.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
|
@ -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}
|
function wlmetric_ball_serial(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T}
|
||||||
@assert radius >= 1
|
@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!)
|
return _wlmetric_ball(S, old, radius, op, collect, unique!)
|
||||||
end
|
end
|
||||||
|
|
||||||
function wlmetric_ball_thr(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T}
|
function wlmetric_ball_thr(S::AbstractVector{T}, center::T=one(first(S)); radius = 2, op = *) where {T}
|
||||||
@assert radius >= 1
|
@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, ThreadsX.collect, ThreadsX.unique)
|
return _wlmetric_ball(S, old, radius, op, Folds.collect, Folds.unique)
|
||||||
end
|
end
|
||||||
|
|
||||||
function _wlmetric_ball(S, old, radius, op, collect, unique)
|
function _wlmetric_ball(S, old, radius, op, collect, unique)
|
||||||
|
@ -96,9 +96,7 @@
|
|||||||
@test evaluate(g*h) == evaluate(h*g)
|
@test evaluate(g*h) == evaluate(h*g)
|
||||||
@test (g*h).savedhash == zero(UInt)
|
@test (g*h).savedhash == zero(UInt)
|
||||||
|
|
||||||
if VERSION >= v"1.6.0"
|
|
||||||
@test sprint(show, typeof(g)) == "Automorphism{FreeGroup{Symbol, KnuthBendix.LenLex{Symbol}}, …}"
|
@test sprint(show, typeof(g)) == "Automorphism{FreeGroup{Symbol, KnuthBendix.LenLex{Symbol}}, …}"
|
||||||
end
|
|
||||||
|
|
||||||
a = g*h
|
a = g*h
|
||||||
b = h*g
|
b = h*g
|
||||||
@ -176,6 +174,7 @@
|
|||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Logging.with_logger(Logging.NullLogger()) do
|
||||||
@testset "GroupsCore conformance" begin
|
@testset "GroupsCore conformance" begin
|
||||||
test_Group_interface(A)
|
test_Group_interface(A)
|
||||||
g = A(rand(1:length(alphabet(A)), 10))
|
g = A(rand(1:length(alphabet(A)), 10))
|
||||||
@ -183,5 +182,5 @@
|
|||||||
|
|
||||||
test_GroupElement_interface(g, h)
|
test_GroupElement_interface(g, h)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -121,15 +121,22 @@ using Groups.KnuthBendix
|
|||||||
u = (a6 * a5)^-1 * b1 * (a6 * a5)
|
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 = (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)
|
# x = (a4^-1*a3^-1*a2^-1*a1^-1*u*a2*a3*a4*a5*a6)
|
||||||
@time evaluate(x)
|
|
||||||
b3 = x * a0 * x^-1
|
b3 = x * a0 * x^-1
|
||||||
b3im = @time evaluate(b3)
|
b3im = evaluate(b3)
|
||||||
b3cim = @time let g = b3
|
b3cim = let g = b3
|
||||||
f = Groups.compiled(g)
|
f = Groups.compiled(g)
|
||||||
f(Groups.domain(g))
|
f(Groups.domain(g))
|
||||||
end
|
end
|
||||||
@test b3im == b3cim
|
@test b3im == b3cim
|
||||||
@test a0 * b2 * b1 == a1 * a3 * a5 * b3
|
@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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ using BenchmarkTools
|
|||||||
using Test
|
using Test
|
||||||
|
|
||||||
using Groups
|
using Groups
|
||||||
using Groups.New
|
|
||||||
|
|
||||||
function wl_ball(F; radius::Integer)
|
function wl_ball(F; radius::Integer)
|
||||||
g, state = iterate(F)
|
g, state = iterate(F)
|
||||||
@ -47,7 +46,7 @@ end
|
|||||||
@testset "iteration: SAut(F_n)" begin
|
@testset "iteration: SAut(F_n)" begin
|
||||||
R = 4
|
R = 4
|
||||||
FN = FreeGroup(N)
|
FN = FreeGroup(N)
|
||||||
SAutFN = New.SpecialAutomorphismGroup(FN)
|
SAutFN = SpecialAutomorphismGroup(FN)
|
||||||
|
|
||||||
let G = SAutFN
|
let G = SAutFN
|
||||||
S = unique([gens(G); inv.(gens(G))])
|
S = unique([gens(G); inv.(gens(G))])
|
||||||
|
@ -54,9 +54,14 @@
|
|||||||
Groups.normalform!(h)
|
Groups.normalform!(h)
|
||||||
@test h == H([5])
|
@test h == H([5])
|
||||||
|
|
||||||
|
@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
|
@testset "GroupsCore conformance: H" begin
|
||||||
test_Group_interface(H)
|
test_Group_interface(H)
|
||||||
test_GroupElement_interface(rand(H, 2)...)
|
test_GroupElement_interface(rand(H, 2)...)
|
||||||
end
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -37,6 +37,8 @@
|
|||||||
@test k == a*b^-1
|
@test k == a*b^-1
|
||||||
|
|
||||||
@time k = test_iteration(F3, 1000)
|
@time k = test_iteration(F3, 1000)
|
||||||
|
|
||||||
|
@time test_iteration(F3, 1000)
|
||||||
@test k == (a^2)*c^2*a^-1
|
@test k == (a^2)*c^2*a^-1
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -67,5 +69,4 @@
|
|||||||
test_Group_interface(F3)
|
test_Group_interface(F3)
|
||||||
test_GroupElement_interface(rand(F3, 2)...)
|
test_GroupElement_interface(rand(F3, 2)...)
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
43
test/group_constructions.jl
Normal file
43
test/group_constructions.jl
Normal file
@ -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
|
71
test/homomorphisms.jl
Normal file
71
test/homomorphisms.jl
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
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(
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
@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
|
79
test/matrix_groups.jl
Normal file
79
test/matrix_groups.jl
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
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]
|
||||||
|
|
||||||
|
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]
|
||||||
|
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
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
|
||||||
|
|
||||||
|
@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
|
||||||
|
|
||||||
|
for g in gens(Sp6)
|
||||||
|
@test MatrixGroups.issymplectic(MatrixGroups.matrix_repr(g))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -1,6 +1,8 @@
|
|||||||
using Test
|
using Test
|
||||||
import AbstractAlgebra
|
|
||||||
using Groups
|
using Groups
|
||||||
|
using PermutationGroups
|
||||||
|
|
||||||
|
import Logging
|
||||||
|
|
||||||
import KnuthBendix: Word
|
import KnuthBendix: Word
|
||||||
|
|
||||||
@ -9,27 +11,30 @@ include(joinpath(pathof(GroupsCore), "..", "..", "test", "conformance_test.jl"))
|
|||||||
|
|
||||||
@testset "Groups" begin
|
@testset "Groups" begin
|
||||||
|
|
||||||
@testset "wlmetric_ball" begin
|
_, t = @timed include("free_groups.jl")
|
||||||
M = AbstractAlgebra.MatrixAlgebra(AbstractAlgebra.zz, 3)
|
@info "free_groups.jl took $(round(t, digits=2))s"
|
||||||
w = one(M); w[1,2] = 1;
|
_, t = @timed include("fp_groups.jl")
|
||||||
r = one(M); r[2,3] = -3;
|
@info "fp_groups.jl took $(round(t, digits=2))s"
|
||||||
s = one(M); s[1,3] = 2; s[3,2] = -1;
|
|
||||||
|
|
||||||
S = [w,r,s]; S = unique([S; inv.(S)]);
|
_, t = @timed include("matrix_groups.jl")
|
||||||
_, sizes = Groups.wlmetric_ball(S, radius=4);
|
@info "matrix_groups.jl took $(round(t, digits=2))s"
|
||||||
@test sizes == [7, 33, 141, 561]
|
_, t = @timed include("AutFn.jl")
|
||||||
_, sizes = Groups.wlmetric_ball_serial(S, radius=4);
|
@info "AutFn.jl took $(round(t, digits=2))s"
|
||||||
@test sizes == [7, 33, 141, 561]
|
|
||||||
|
_, t = @timed include("homomorphisms.jl")
|
||||||
|
@info "homomorphisms.jl took $(round(t, digits=2))s"
|
||||||
|
|
||||||
|
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
|
end
|
||||||
|
|
||||||
include("free_groups.jl")
|
_, t = @timed include("group_constructions.jl")
|
||||||
include("fp_groups.jl")
|
@info "Constructions took $(round(t, digits=2))s"
|
||||||
|
end
|
||||||
include("AutFn.jl")
|
|
||||||
include("AutSigma_41.jl")
|
if !haskey(ENV, "CI")
|
||||||
include("AutSigma3.jl")
|
include("benchmarks.jl")
|
||||||
|
|
||||||
# if !haskey(ENV, "CI")
|
|
||||||
# include("benchmarks.jl")
|
|
||||||
# end
|
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user