Compare commits

...

79 Commits

Author SHA1 Message Date
fa326a147f
add deprecation warning 2019-07-10 23:32:49 +02:00
f1d0ff9c14 \lambda = 50.0 got certified! 2018-11-24 13:59:32 +01:00
8666cc6b2c last minute functional changes 2018-11-24 13:58:49 +01:00
9fc23c43d7 ignore python files 2018-11-17 22:44:50 +01:00
c075b93313 split definitions of action to a separate file 2018-11-17 22:43:42 +01:00
bf8fc67bb4 update check_positivity 2018-11-17 22:42:42 +01:00
523d783614 add check_positivity.jl 2018-11-06 08:31:37 +01:00
7b06dc1eb2 fix upper-bound in Settings 2018-09-17 00:22:35 +02:00
7dd061cdb7 fix typos 2018-09-16 17:53:40 +02:00
b45adc2e00 add external constructors for SAut i SL 2018-09-16 17:53:10 +02:00
8ee8eebd9e GAPGroups are trivial structs 2018-09-09 13:15:13 +02:00
4cd2212554 setup paths and logger using fullpath(::Settings) 2018-09-09 13:14:07 +02:00
b84b23d486 make solver a runtime choice 2018-09-09 13:13:21 +02:00
c7b8776e2d dispatch main ::PropertyTGroup 2018-09-09 13:12:53 +02:00
1b45625a71 create Property.Settings from PropertyTGroup and args 2018-09-09 13:09:43 +02:00
1b6793f37c parametrize PropertyTGroups types by N 2018-09-09 13:05:49 +02:00
8a2aa6542e if no "nosymmetry" in args, assume symmetrization 2018-09-06 22:45:16 +02:00
aaa36b2d5c fix MCG constructor 2018-09-06 22:43:28 +02:00
88fa1ded31 remove files for running particular groups
everything is handled by run.jl
2018-09-05 18:30:41 +02:00
b41f2ccdc8 add default log name 2018-09-05 18:27:18 +02:00
1ea396330c fix typo in MCG constructor 2018-09-05 18:26:51 +02:00
a1e4a917a6 include all groups in runtests.jl 2018-09-05 17:57:25 +02:00
385d2d4f5e selection of PropertyTGroup happens inside PropertyTGroups 2018-09-05 17:53:26 +02:00
e02b410240 update main 2018-09-05 17:52:23 +02:00
c8f58e1c93 one global run file (for all groups) 2018-09-05 17:51:43 +02:00
0a0856bb51 update groups to the new input format 2018-09-05 17:48:59 +02:00
df4b63a6a6 export group names 2018-08-20 03:28:09 +02:00
01405cb3cc Group <-> Ring dispatch problem is solved when generating Laplacian 2018-08-20 03:27:25 +02:00
6abb2c4186 dispatch method of main is decided based on G 2018-08-20 03:24:49 +02:00
0cae4882f1 replace Standard -→ Naive (methods of build problem) 2018-08-20 03:22:16 +02:00
707efaca3a move the information on solvers 2018-08-20 02:55:19 +02:00
57e9e3c404 use external logging 2018-08-20 02:54:50 +02:00
000069961b fix SCSSolver params to the most stable ones 2018-08-18 23:25:47 +02:00
ab49755863 add tests! 2018-08-18 23:25:20 +02:00
b5b9917536 add generatingset(::MatSpace,...) 2018-08-18 23:24:57 +02:00
95066b9c56 fix the action of WreathProdElem on MatElem
mul! is not correct for NmodRing??
2018-08-18 23:23:52 +02:00
42f915b91d fix use of Nemo's FiniteField 2018-08-15 17:20:25 +02:00
1e54e25672 pass the right Id element for rings 2018-08-15 17:20:02 +02:00
45d8d85189 add perm action from the right (by inverse) on MatElems 2018-08-15 17:19:37 +02:00
7257c02820 rename SymmetricGroup → SymmetrizedGroup 2018-08-15 17:19:28 +02:00
2c7bd86418 Merge branch 'fix/new_type_system' of git.wmi.amu.edu.pl:kalmar/GroupsWithPropertyT into fix/new_type_system 2018-08-08 19:43:12 +02:00
97f64fdee3 small fixes to GAPGroups 2018-08-08 19:38:22 +02:00
87658ea46e separate solver selection 2018-08-08 19:37:57 +02:00
3d59ecc874 update FPgroup.jl to PropertyTGroup interface 2018-08-08 19:37:28 +02:00
16bc76e950 update FPgroup.jl to PropertyTGroup interface 2018-08-08 00:37:08 +02:00
5ea5093f42 remove main_gapgroup.jl 2018-08-08 00:30:06 +02:00
7f4824492c initial adjust of GAPGroups files 2018-08-08 00:29:22 +02:00
02825a8ec0 dispatch main on SymmetricGroup, GAPGroup 2018-08-08 00:28:15 +02:00
34066a7128 global usings 2018-08-08 00:26:39 +02:00
52b0e598c6 use Allgroups.jl, separate params and summarize functions 2018-08-08 00:26:15 +02:00
fd29784a00 more efficient actions of autS 2018-08-08 00:24:17 +02:00
1d82aeed8d implement the SymmetricGroup interface for SLn and AutFn 2018-08-08 00:21:13 +02:00
7dc95cf93a add PropertyTGroup, SymmetricGroup, GAPGroup hierarchy 2018-08-08 00:17:44 +02:00
788da99d82 separate set_parallel_mthread(N::Int, workers::Bool) 2018-08-08 00:16:05 +02:00
6f0f1d5cc6 mv MCGn.jl to FPgroup.jl 2018-07-31 16:55:36 +02:00
3859a26c22 generalize MCG to all FP GAP groups 2018-07-31 16:54:35 +02:00
b5cf3946a2 move MCG.jl to MCGn.jl 2018-07-31 16:44:41 +02:00
8a2d6086cb use AbstractAlgebra in mcg 2018-07-31 15:52:16 +02:00
0d2f15c887 remove the unused Orbit.jl 2018-07-31 15:51:20 +02:00
80f181da11 make ignores more specific 2018-07-31 12:43:45 +02:00
b23a98d23e update to AbstractAlgebra 2018-07-31 12:42:58 +02:00
df940c8c07 rework starting scripts 2018-07-31 12:41:48 +02:00
6acdff2b70 increase O.maxstates to 1000*$maxeqns 2018-03-22 11:41:35 +01:00
ed12d87b7a Add tools to compute in mapping class groups 2018-03-22 11:09:03 +01:00
e0d3cb607b standardise usage of GROUP modules 2018-03-22 11:07:52 +01:00
10cbd4c6f3 use CPUselect.jl 2018-03-22 11:05:36 +01:00
4c26db6ee8 standardise interface and fix comments 2018-03-22 11:03:11 +01:00
98b2417ae1 move specific group-related code to groups/... 2018-03-22 10:59:27 +01:00
a75ee1f645 use --warmstart option 2018-01-07 16:12:51 +01:00
2ae11d9f33 indentation and cosmetics 2018-01-04 22:09:53 +01:00
76ff889366 update for enh/rework-logging of PropertyT 2018-01-04 22:09:13 +01:00
cb16c78690 rewrite 2018-01-04 22:08:32 +01:00
9cc5bcfc00 rename parsed_args 2018-01-04 10:50:26 +01:00
961f04b964 integrate with SLNs.jl 2018-01-04 10:49:49 +01:00
98df554624 add leading "o" to groupname in Orbit.jl 2018-01-04 10:48:54 +01:00
476d532d93 use CPUSelect 2018-01-04 10:47:00 +01:00
122ebe92bf save GAP output to GAP.log 2018-01-04 10:46:40 +01:00
86cadd0440 set alpha=1.95, acceleration_lookback=1 for numerical stability 2017-12-30 00:26:36 +01:00
403d7f7921 add MCG code 2017-12-29 17:45:09 +01:00
23 changed files with 1206 additions and 657 deletions

17
.gitignore vendored
View File

@ -1,8 +1,15 @@
Articles
Higman
MCG*
notebooks
Oldies
oSAutF*
oSL*
SAutF*
SL*_*
*ipynb*
*.gws
*/*.jld
*/*.log
Old*
.*
o*
*test*
tests*
*.py
*.pyc

118
AutFN.jl
View File

@ -1,118 +0,0 @@
using ArgParse
using Nemo
import SCS.SCSSolver
using PropertyT
using Groups
Nemo.setpermstyle(:cycles)
#=
Note that the element
α(i,j,k) = ϱ(i,j)*ϱ(i,k)*inv(ϱ(i,j))*inv(ϱ(i,k)),
which surely belongs to ball of radius 4 in Aut(F₄) becomes trivial under the representation
Aut(F₄) GL₄()ℤ⁴ GL₅().
Moreover, due to work of Potapchik and Rapinchuk [1] every real representation of Aut(Fₙ) into GLₘ() (for m 2n-2) factors through GLₙ()ℤⁿ, so will have the same problem.
We need a different approach: Here we actually compute in Aut(𝔽₄)
=#
###############################################################################
#
# Generating set
#
###############################################################################
function SOutFN_generating_set(N::Int)
SOutFN = AutGroup(FreeGroup(N), special=true, outer=true)
S = gens(SOutFN);
S = [S; [inv(s) for s in S]]
return SOutFN, unique(S)
end
###############################################################################
#
# Parsing command line
#
###############################################################################
function cpuinfo_physicalcores()
maxcore = -1
for line in eachline("/proc/cpuinfo")
if startswith(line, "core id")
maxcore = max(maxcore, parse(Int, split(line, ':')[2]))
end
end
maxcore < 0 && error("failure to read core ids from /proc/cpuinfo")
return maxcore + 1
end
function parse_commandline()
s = ArgParseSettings()
@add_arg_table s begin
"--tol"
help = "set numerical tolerance for the SDP solver"
arg_type = Float64
default = 1e-6
"--iterations"
help = "set maximal number of iterations for the SDP solver"
arg_type = Int
default = 20000
"--upper-bound"
help = "Set an upper bound for the spectral gap"
arg_type = Float64
default = Inf
"--cpus"
help = "Set number of cpus used by solver (default: auto)"
arg_type = Int
required = false
"-N"
help = "Consider automorphisms of free group on N generators"
arg_type = Int
default = 2
end
return parse_args(s)
end
function main()
parsed_args = parse_commandline()
if parsed_args["cpus"] nothing
if parsed_args["cpus"] > cpuinfo_physicalcores()
warn("Number of specified cores exceeds the physical core cound. Performance will suffer.")
end
Blas.set_num_threads(parsed_args["cpus"])
end
N = parsed_args["N"]
radius = 2
tol = parsed_args["tol"]
iterations = parsed_args["iterations"]
upper_bound = parsed_args["upper-bound"]
dirname = "SOutF$(N)_$(upper_bound)_r=$radius"
logger = PropertyT.setup_logging(dirname)
info(logger, "Group: $dirname")
info(logger, "Iterations: $iterations")
info(logger, "Precision: $tol")
info(logger, "Upper bound: $upper_bound")
G, S = SOutFN_generating_set(N)
info(logger, G)
info(logger, "Symmetric generating set of size $(length(S))")
info(logger, S)
Id = G()
solver = SCSSolver(eps=tol, max_iters=iterations, linearsolver=SCS.Direct)
@time PropertyT.check_property_T(dirname, S, Id, solver, upper_bound, tol, 2)
return 0
end
main()

View File

@ -1,54 +0,0 @@
using ArgParse
###############################################################################
#
# Parsing command line
#
###############################################################################
function parse_commandline()
settings = ArgParseSettings()
@add_arg_table settings begin
"--tol"
help = "set numerical tolerance for the SDP solver"
arg_type = Float64
default = 1e-14
"--iterations"
help = "set maximal number of iterations for the SDP solver (default: 20000)"
arg_type = Int
default = 60000
"--upper-bound"
help = "Set an upper bound for the spectral gap"
arg_type = Float64
default = Inf
"--cpus"
help = "Set number of cpus used by solver (default: auto)"
arg_type = Int
required = false
"-N"
help = "Consider automorphisms of free group on N generators"
arg_type = Int
default = 2
"--radius"
help = "Radius of ball B_r(e,S) to find solution over"
arg_type = Int
default = 2
"--warmstart"
help = "Use warmstart.jl as the initial guess for SCS"
action = :store_true
end
return parse_args(settings)
end
parsed_args = parse_commandline()
include("CPUselect.jl")
set_parallel_mthread(parsed_args, workers=true)
include("SAutFNs.jl")
include("Orbit.jl")
main(SAutFNs, parsed_args)

View File

@ -9,15 +9,9 @@ function cpuinfo_physicalcores()
return maxcore + 1
end
function set_parallel_mthread(parsed_args; workers=false)
if parsed_args["cpus"] == nothing
N = cpuinfo_physicalcores()
else
N = parsed_args["cpus"]
if N > cpuinfo_physicalcores()
warn("Number of specified cores exceeds the physical core count. Performance may suffer.")
end
function set_parallel_mthread(N::Int, workers::Bool)
if N > cpuinfo_physicalcores()
warn("Number of specified cores exceeds the physical core count. Performance may suffer.")
end
if workers
@ -28,5 +22,14 @@ function set_parallel_mthread(parsed_args; workers=false)
BLAS.set_num_threads(N)
info("Using $N threads in BLAS.")
return N
end
function set_parallel_mthread(parsed_args::Dict; workers=false)
if parsed_args["cpus"] == nothing
N = cpuinfo_physicalcores()
else
N = parsed_args["cpus"]
end
set_parallel_mthread(N, workers)
end

157
FPGroups_GAP.jl Normal file
View File

@ -0,0 +1,157 @@
using JLD
function GAP_code(group_code, dir, R; maxeqns=10_000, infolevel=2)
code = """
LogTo("$(dir)/GAP.log");
RequirePackage("kbmag");
SetInfoLevel(InfoRWS, $infolevel);
MetricBalls := function(rws, R)
local l, basis, sizes, i;
l := EnumerateReducedWords(rws, 0, R);;
SortBy(l, Length);
sizes := [1..R];
Apply(sizes, i -> Number(l, w -> Length(w) <= i));
return [l, sizes];
end;;
ProductMatrix := function(rws, basis, len)
local result, dict, g, tmpList, t;
result := [];
dict := NewDictionary(basis[1], true);
t := Runtime();
for g in [1..Length(basis)] do;
AddDictionary(dict, basis[g], g);
od;
Print("Creating dictionary: \t\t", StringTime(Runtime()-t), "\\n");
for g in basis{[1..len]} do;
tmpList := List(Inverse(g)*basis{[1..len]}, w->ReducedForm(rws, w));
#t := Runtime();
tmpList := List(tmpList, x -> LookupDictionary(dict, x));
#Print(Runtime()-t, "\\n");
Assert(1, ForAll(tmpList, x -> x <> fail));
Add(result, tmpList);
od;
return result;
end;;
SaveCSV := function(fname, pm)
local file, i, j, k;
file := OutputTextFile(fname, false);;
for i in pm do;
k := 1;
for j in i do;
if k < Length(i) then
AppendTo(file, j, ", ");
else
AppendTo(file, j, "\\n");
fi;
k := k+1;
od;
od;
CloseStream(file);
end;;
$group_code
# G:= SimplifiedFpGroup(G);
RWS := KBMAGRewritingSystem(G);
# ResetRewritingSystem(RWS);
O:=OptionsRecordOfKBMAGRewritingSystem(RWS);;
O.maxeqns := $maxeqns;
O.maxstates := 1000*$maxeqns;
#O.maxstoredlen := [100,100];
before := Runtimes();;
KnuthBendix(RWS);
after := Runtimes();;
delta := after.user_time_children - before.user_time_children;;
Print("Knuth-Bendix completion: \t", StringTime(delta), "\\n");
t := Runtime();
res := MetricBalls(RWS,$(2*R));;
Print("Metric-Balls generation: \t", StringTime(Runtime()-t), "\\n");
B := res[1];; sizes := res[2];;
Print("Sizes of generated Balls: \t", sizes, "\\n");
t := Runtime();
pm := ProductMatrix(RWS, B, sizes[$R]);;
Print("Computing ProductMatrix: \t", StringTime(Runtime()-t), "\\n");
S := EnumerateReducedWords(RWS, 1, 1);
S := List(S, s -> Position(B,s));
SaveCSV("$(dir)/pm.csv", pm);
SaveCSV("$(dir)/S.csv", [S]);
SaveCSV("$(dir)/sizes.csv", [sizes]);
Print("DONE!\\n");
quit;""";
return code
end
function GAP_groupcode(S, rels)
F = "FreeGroup("*join(["\"$v\""for v in S], ", ") *");"
m = match(r".*(\[.*\])$", string(rels))
rels = replace(m.captures[1], " ", "\n")
code = """
F := $F;
AssignGeneratorVariables(F);;
relations := $rels;;
G := F/relations;
"""
return code
end
function GAP_execute(gap_code, dir)
isdir(dir) || mkdir(dir)
GAP_file = joinpath(dir, "GAP_code.g")
@show dir
@show GAP_file;
open(GAP_file, "w") do io
write(io, gap_code)
end
run(pipeline(`cat $(GAP_file)`, `gap -q`))
end
function prepare_pm_delta_csv(name, group_code, R; maxeqns=10_000, infolevel=2)
info("Preparing multiplication table using GAP (via kbmag)")
gap_code = GAP_code(group_code, name, R, maxeqns=maxeqns, infolevel=infolevel)
GAP_execute(gap_code, name)
end
function prepare_pm_delta(name, group_code, R; maxeqns=100_000, infolevel=2)
pm_fname = joinpath(name, "pm.csv")
S_fname = joinpath(name, "S.csv")
sizes_fname = joinpath(name, "sizes.csv")
delta_fname = joinpath(name, "delta.jld")
if !isfile(pm_fname) || !isfile(S_fname) || !isfile(sizes_fname)
prepare_pm_delta_csv(name, group_code, R, maxeqns=maxeqns, infolevel=infolevel)
end
if isfile(sizes_fname)
sizes = readcsv(sizes_fname, Int)[1,:]
if 2R > length(sizes)
prepare_pm_delta_csv(name, group_code, R, maxeqns=maxeqns, infolevel=infolevel)
end
end
pm = readcsv(pm_fname, Int)
S = readcsv(S_fname, Int)[1,:]
sizes = readcsv(sizes_fname, Int)[1,:]
Δ = spzeros(sizes[2R])
Δ[S] .= -1
Δ[1] = length(S)
pm = pm[1:sizes[R], 1:sizes[R]]
save(joinpath(name, "pm.jld"), "pm", pm)
save(joinpath(name, "delta.jld"), "Δ", Δ)
end

View File

@ -1,53 +0,0 @@
using SCS.SCSSolver
# using Mosek
# using CSDP
# using SDPA
using Nemo
using PropertyT
using Groups
function main(GROUP, parsed_args)
radius = parsed_args["radius"]
tol = parsed_args["tol"]
iterations = parsed_args["iterations"]
upper_bound = parsed_args["upper-bound"]
warm = parsed_args["warmstart"]
name, N = GROUP.groupname(parsed_args)
G, S = GROUP.generatingset(parsed_args)
autS = GROUP.autS(parsed_args)
name = "$(name)_r$radius"
isdir(name) || mkdir(name)
logger = PropertyT.setup_logging(joinpath(name, "$(upper_bound)"))
info(logger, "Group: $name")
info(logger, "Iterations: $iterations")
info(logger, "Precision: $tol")
info(logger, "Upper bound: $upper_bound")
info(logger, "Threads: $(Threads.nthreads())")
info(logger, "Workers: $(workers())")
info(logger, G)
info(logger, "Symmetric generating set of size $(length(S))")
# info(logger, S)
solver = SCSSolver(eps=tol, max_iters=iterations, linearsolver=SCS.Direct, alpha=1.9, acceleration_lookback=1)
# solver = Mosek.MosekSolver(
# MSK_DPAR_INTPNT_CO_TOL_REL_GAP=tol,
# MSK_IPAR_INTPNT_MAX_ITERATIONS=iterations,
# QUIET=false)
# solver = CSDP.CSDPSolver(axtol=tol, atytol=tol, objtol=tol, minstepp=tol*10.0^-1, minstepd=tol*10.0^-1)
# solver = SDPA.SDPASolver(epsilonStar=tol, epsilonDash=tol)
sett = Settings(name, N, G, S, autS, radius, solver, upper_bound, tol, warm)
PropertyT.check_property_T(sett)
end

View File

@ -1,4 +1,10 @@
This repository contains code for computations in [Certifying Numerical Estimates of Spectral Gaps](https://arxiv.org/abs/1703.09680).
# DEPRECATED!
This repository has not been updated for a while!
If You are interested in replicating results for [1712.07167](https://arxiv.org/abs/1712.07167) please check [these instruction](https://kalmar.faculty.wmi.amu.edu.pl/post/1712.07176/)
Also [this notebook](https://nbviewer.jupyter.org/gist/kalmarek/03510181bc1e7c98615e86e1ec580b2a) could be of some help. If everything else fails the [zenodo dataset](https://zenodo.org/record/1133440) should contain the last-resort instructions.
This repository contains some legacy code for computations in [Certifying Numerical Estimates of Spectral Gaps](https://arxiv.org/abs/1703.09680).
# Installing
To run the code You need `julia-v0.5` (should work on `v0.6`, but with warnings).

View File

@ -1,80 +0,0 @@
module SAutFNs
using Nemo
using Groups
if VERSION >= v"0.6.0"
import Nemo.Generic.perm
end
###############################################################################
#
# Generating set
#
###############################################################################
function generatingset(N::Int)
SAutFN = AutGroup(FreeGroup(N), special=true)
S = gens(SAutFN);
S = [S; [inv(s) for s in S]]
return SAutFN, unique(S)
end
function generatingset(parsed_args)
N = parsed_args["N"]
return generatingset(N)
end
###############################################################################
#
# Action of WreathProductElems on AutGroupElem
#
###############################################################################
function AutFG_emb(A::AutGroup, g::WreathProductElem)
isa(A.objectGroup, FreeGroup) || throw("Not an Aut(Fₙ)")
parent(g).P.n == length(A.objectGroup.gens) || throw("No natural embedding of $(parent(g)) into $A")
powers = [(elt == parent(elt)() ? 0: 1) for elt in g.n.elts]
elt = reduce(*, [A(Groups.flip_autsymbol(i))^pow for (i,pow) in enumerate(powers)])
Groups.r_multiply!(elt, [Groups.perm_autsymbol(g.p)])
return elt
end
function AutFG_emb(A::AutGroup, p::perm)
isa(A.objectGroup, FreeGroup) || throw("Not an Aut(Fₙ)")
parent(p).n == length(A.objectGroup.gens) || throw("No natural embedding of $(parent(g)) into $A")
return A(Groups.perm_autsymbol(p))
end
function (g::WreathProductElem)(a::AutGroupElem)
g = AutFG_emb(parent(a),g)
return g*a*g^-1
end
function (p::perm)(a::AutGroupElem)
g = AutFG_emb(parent(a),p)
return g*a*g^-1
end
autS(N::Int) = WreathProduct(PermutationGroup(2), PermutationGroup(N))
function autS(parsed_args)
return autS(parsed_args["N"])
end
###############################################################################
#
# Misc
#
###############################################################################
function groupname(parsed_args)
N = parsed_args["N"]
return groupname(N), N
end
groupname(N::Int) = "oSAutF$(N)"
end # of module SAutFNs

151
SL.jl
View File

@ -1,151 +0,0 @@
using ArgParse
using Nemo
import SCS.SCSSolver
using PropertyT
###############################################################################
#
# Generating set
#
###############################################################################
function E(i::Int, j::Int, M::MatSpace, val=one(M.base_ring))
@assert i≠j
m = one(M)
m[i,j] = val
return m
end
function SLsize(n,p)
result = BigInt(1)
for k in 0:n-1
result *= p^n - p^k
end
return div(result, p-1)
end
function SL_generatingset(n::Int, X::Bool=false)
indexing = [(i,j) for i in 1:n for j in 1:n if i≠j]
G = MatrixSpace(ZZ, n, n)
if X
S = [E(i,j,G,v) for (i,j) in indexing for v in [1, 100]]
else
S = [E(i,j,G,v) for (i,j) in indexing for v in [1]]
end
S = vcat(S, [inv(x) for x in S])
return G, unique(S)
end
function SL_generatingset(n::Int, p::Int, X::Bool=false)
p == 0 && return SL_generatingset(n, X)
(p > 1 && n > 1) || throw("Both n and p should be positive integers!")
info("Size(SL($n,$p)) = $(SLsize(n,p))")
F = ResidueRing(ZZ, p)
G = MatrixSpace(F, n, n)
indexing = [(i,j) for i in 1:n for j in 1:n if i≠j]
S = [E(i, j, G) for (i,j) in indexing]
S = vcat(S, [inv(x) for x in S])
return G, unique(S)
end
###############################################################################
#
# Parsing command line
#
###############################################################################
function cpuinfo_physicalcores()
maxcore = -1
for line in eachline("/proc/cpuinfo")
if startswith(line, "core id")
maxcore = max(maxcore, parse(Int, split(line, ':')[2]))
end
end
maxcore < 0 && error("failure to read core ids from /proc/cpuinfo")
return maxcore + 1
end
function parse_commandline()
settings = ArgParseSettings()
@add_arg_table settings begin
"--tol"
help = "set numerical tolerance for the SDP solver"
arg_type = Float64
default = 1e-6
"--iterations"
help = "set maximal number of iterations for the SDP solver (default: 20000)"
arg_type = Int
default = 50000
"--upper-bound"
help = "Set an upper bound for the spectral gap"
arg_type = Float64
default = Inf
"--cpus"
help = "Set number of cpus used by solver"
arg_type = Int
required = false
"-N"
help = "Consider elementary matrices EL(N)"
arg_type = Int
default = 2
"-p"
help = "Matrices over field of p-elements (p=0 => over ZZ)"
arg_type = Int
default = 0
"--radius"
help = "Radius of ball B_r(e,S) to find solution over"
arg_type = Int
default = 2
end
return parse_args(settings)
end
function main()
parsed_args = parse_commandline()
if parsed_args["cpus"] nothing
if parsed_args["cpus"] > cpuinfo_physicalcores()
warn("Number of specified cores exceeds the physical core cound. Performance will suffer.")
end
BLAS.set_num_threads(parsed_args["cpus"])
end
N = parsed_args["N"]
p = parsed_args["p"]
if p == 0
dirname = "SL$(N)Z"
else
dirname = "SL$(N)_$p"
end
  radius = parsed_args["radius"]
tol = parsed_args["tol"]
iterations = parsed_args["iterations"]
upper_bound = parsed_args["upper-bound"]
dirname = "$(dirname)_$(upper_bound)_r=$radius"
logger = PropertyT.setup_logging(dirname)
info(logger, "Group: $dirname")
info(logger, "Iterations: $iterations")
info(logger, "Precision: $tol")
info(logger, "Upper bound: $upper_bound")
G, S = SL_generatingset(N, p)
info(logger, G)
info(logger, "Symmetric generating set of size $(length(S))")
Id = one(G)
solver = SCSSolver(eps=tol, max_iters=iterations, linearsolver=SCS.Direct)
@time PropertyT.check_property_T(dirname, S, Id, solver, upper_bound, tol, radius)
return 0
end
main()

124
SLNs.jl
View File

@ -1,124 +0,0 @@
module SLNs
using Nemo
using Groups
if VERSION >= v"0.6.0"
import Nemo.Generic.perm
end
###############################################################################
#
# Generating set
#
###############################################################################
function E(i::Int, j::Int, M::MatSpace, val=one(M.base_ring))
@assert i≠j
m = one(M)
m[i,j] = val
return m
end
function SLsize(n,p)
result = BigInt(1)
for k in 0:n-1
result *= p^n - p^k
end
return div(result, p-1)
end
function generatingset(n::Int, X::Bool=false)
indexing = [(i,j) for i in 1:n for j in 1:n if i≠j]
G = MatrixSpace(ZZ, n, n)
if X
S = [E(i,j,G,v) for (i,j) in indexing for v in [1, 100]]
else
S = [E(i,j,G,v) for (i,j) in indexing for v in [1]]
end
S = vcat(S, [inv(x) for x in S])
return G, unique(S)
end
function generatingset(n::Int, p::Int, X::Bool=false)
(p > 1 && n > 1) || throw("Both n and p should be positive integers!")
info("Size(SL($n,$p)) = $(SLsize(n,p))")
F = ResidueRing(ZZ, p)
G = MatrixSpace(F, n, n)
indexing = [(i,j) for i in 1:n for j in 1:n if i≠j]
S = [E(i, j, G) for (i,j) in indexing]
S = vcat(S, [inv(x) for x in S])
return G, unique(S)
end
function generatingset(parsed_args)
N = parsed_args["N"]
p = parsed_args["p"]
X = parsed_args["X"]
if p == 0
return generatingset(N, X)
else
return generatingset(N, p, X)
end
end
###############################################################################
#
# Action of WreathProductElems on Nemo.MatElem
#
###############################################################################
function matrix_emb(MM::MatSpace, g::WreathProductElem)
parent(g).P.n == MM.cols == MM.rows || throw("No natural embedding of $(parent(g)) in ")
powers = [(elt == parent(elt)() ? 0: 1) for elt in g.n.elts]
elt = diagm([(-1)^(elt == parent(elt)() ? 0: 1) for elt in g.n.elts])
return MM(elt)*MM(Nemo.matrix_repr(g.p)')
end
function (g::WreathProductElem)(A::MatElem)
G = matrix_emb(parent(A), g)
inv_G = matrix_emb(parent(A), inv(g))
return G*A*inv_G
end
function (p::perm)(A::MatElem)
length(p.d) == A.r == A.c || throw("Can't act via $p on matrix of size ($(A.r), $(A.c))")
R = parent(A)
return p*A*inv(p)
end
autS(N::Int) = WreathProduct(PermutationGroup(2), PermutationGroup(N))
function autS(parsed_args)
return autS(parsed_args["N"])
end
###############################################################################
#
# Misc
#
###############################################################################
function groupname(parsed_args)
N = parsed_args["N"]
p = parsed_args["p"]
X = parsed_args["X"]
return groupname(N, p, X), N
end
function groupname(N, p=0, X=false)
if p == 0
if X
name = "oSL$(N)Z⟨X⟩"
else
name = "oSL$(N)Z"
end
else
name = "oSL$(N)_$p"
end
return name
end
end # of module SLNs

View File

@ -1,61 +0,0 @@
using ArgParse
###############################################################################
#
# Parsing command line
#
###############################################################################
function parse_commandline()
settings = ArgParseSettings()
@add_arg_table settings begin
"--tol"
help = "set numerical tolerance for the SDP solver"
arg_type = Float64
default = 1e-14
"--iterations"
help = "set maximal number of iterations for the SDP solver (default: 20000)"
arg_type = Int
default = 60000
"--upper-bound"
help = "Set an upper bound for the spectral gap"
arg_type = Float64
default = Inf
"--cpus"
help = "Set number of cpus used by solver"
arg_type = Int
required = false
"-N"
help = "Consider elementary matrices EL(N)"
arg_type = Int
default = 2
"-p"
help = "Matrices over field of p-elements (p=0 => over ZZ)"
arg_type = Int
default = 0
"--radius"
help = "Radius of ball B_r(e,S) to find solution over"
arg_type = Int
default = 2
"-X"
help = "Consider EL(N, ZZ⟨X⟩)"
action = :store_true
"--warmstart"
help = "Use warmstart.jl as the initial guess for SCS"
action = :store_true
end
return parse_args(settings)
end
parsed_args = parse_commandline()
include("CPUselect.jl")
set_parallel_mthread(parsed_args, workers=true)
include("SLNs.jl")
include("Orbit.jl")
main(SLNs, parsed_args)

56
groups/Allgroups.jl Normal file
View File

@ -0,0 +1,56 @@
module PropertyTGroups
using PropertyT
using AbstractAlgebra
using Nemo
using Groups
using GroupRings
export PropertyTGroup, SymmetrizedGroup, GAPGroup,
SpecialLinearGroup,
SpecialAutomorphismGroup,
HigmanGroup,
CapraceGroup,
MappingClassGroup
export PropertyTGroup
abstract type PropertyTGroup end
abstract type SymmetrizedGroup <: PropertyTGroup end
abstract type GAPGroup <: PropertyTGroup end
function PropertyTGroup(args)
if haskey(args, "SL")
G = PropertyTGroups.SpecialLinearGroup(args)
elseif haskey(args, "SAut")
G = PropertyTGroups.SpecialAutomorphismGroup(args)
elseif haskey(args, "MCG")
G = PropertyTGroups.MappingClassGroup(args)
elseif haskey(args, "Higman")
G = PropertyTGroups.HigmanGroup(args)
elseif haskey(args, "Caprace")
G = PropertyTGroups.CapraceGroup(args)
else
throw("You must provide one of --SL, --SAut, --MCG, --Higman, --Caprace")
end
return G
end
include("autfreegroup.jl")
include("speciallinear.jl")
Comm(x,y) = x*y*x^-1*y^-1
function generatingset(G::GAPGroup)
S = gens(group(G))
return unique([S; inv.(S)])
end
include("mappingclassgroup.jl")
include("higman.jl")
include("caprace.jl")
include("actions.jl")
end # of module PropertyTGroups

92
groups/actions.jl Normal file
View File

@ -0,0 +1,92 @@
function (p::perm)(A::GroupRingElem)
RG = parent(A)
result = zero(RG, eltype(A.coeffs))
for (idx, c) in enumerate(A.coeffs)
if c!= zero(eltype(A.coeffs))
result[p(RG.basis[idx])] = c
end
end
return result
end
###############################################################################
#
# Action of WreathProductElems on Nemo.MatElem
#
###############################################################################
function matrix_emb(n::DirectProductGroupElem, p::perm)
Id = parent(n.elts[1])()
elt = diagm([(-1)^(el == Id ? 0 : 1) for el in n.elts])
return elt[:, p.d]
end
function (g::WreathProductElem)(A::MatElem)
g_inv = inv(g)
G = matrix_emb(g.n, g_inv.p)
G_inv = matrix_emb(g_inv.n, g.p)
M = parent(A)
return M(G)*A*M(G_inv)
end
import Base.*
doc"""
*(x::AbstractAlgebra.MatElem, P::Generic.perm)
> Apply the pemutation $P$ to the rows of the matrix $x$ and return the result.
"""
function *(x::AbstractAlgebra.MatElem, P::Generic.perm)
z = similar(x)
m = rows(x)
n = cols(x)
for i = 1:m
for j = 1:n
z[i, j] = x[i,P[j]]
end
end
return z
end
function (p::perm)(A::MatElem)
length(p.d) == A.r == A.c || throw("Can't act via $p on matrix of size ($(A.r), $(A.c))")
return p*A*inv(p)
end
###############################################################################
#
# Action of WreathProductElems on AutGroupElem
#
###############################################################################
function AutFG_emb(A::AutGroup, g::WreathProductElem)
isa(A.objectGroup, FreeGroup) || throw("Not an Aut(Fₙ)")
parent(g).P.n == length(A.objectGroup.gens) || throw("No natural embedding of $(parent(g)) into $A")
elt = A()
Id = parent(g.n.elts[1])()
flips = Groups.AutSymbol[Groups.flip_autsymbol(i) for i in 1:length(g.p.d) if g.n.elts[i] != Id]
Groups.r_multiply!(elt, flips, reduced=false)
Groups.r_multiply!(elt, [Groups.perm_autsymbol(g.p)])
return elt
end
function AutFG_emb(A::AutGroup, p::perm)
isa(A.objectGroup, FreeGroup) || throw("Not an Aut(Fₙ)")
parent(p).n == length(A.objectGroup.gens) || throw("No natural embedding of $(parent(p)) into $A")
return A(Groups.perm_autsymbol(p))
end
function (g::WreathProductElem)(a::Groups.Automorphism)
A = parent(a)
g = AutFG_emb(A,g)
res = A()
Groups.r_multiply!(res, g.symbols, reduced=false)
Groups.r_multiply!(res, a.symbols, reduced=false)
Groups.r_multiply!(res, [inv(s) for s in reverse!(g.symbols)])
return res
end
function (p::perm)(a::Groups.Automorphism)
g = AutFG_emb(parent(a),p)
return g*a*inv(g)
end

21
groups/autfreegroup.jl Normal file
View File

@ -0,0 +1,21 @@
struct SpecialAutomorphismGroup{N} <: SymmetrizedGroup
group::AutGroup
end
function SpecialAutomorphismGroup(args::Dict)
N = args["SAut"]
return SpecialAutomorphismGroup{N}(AutGroup(FreeGroup(N), special=true))
end
name(G::SpecialAutomorphismGroup{N}) where N = "SAutF$(N)"
group(G::SpecialAutomorphismGroup) = G.group
function generatingset(G::SpecialAutomorphismGroup)
S = gens(group(G));
return unique([S; inv.(S)])
end
function autS(G::SpecialAutomorphismGroup{N}) where N
return WreathProduct(PermutationGroup(2), PermutationGroup(N))
end

35
groups/caprace.jl Normal file
View File

@ -0,0 +1,35 @@
struct CapraceGroup <: GAPGroup end
name(G::CapraceGroup) = "CapraceGroup"
function group(G::CapraceGroup)
caprace_group = Groups.FPGroup(["x","y","z","t","r"])
x,y,z,t,r = gens(caprace_group)
relations = [
x^7,
y^7,
t^2,
r^73,
t*r*t*r,
Comm(x,y)*z^-1,
Comm(x,z),
Comm(y,z),
Comm(x^2*y*z^-1, t),
Comm(x*y*z^3, t*r),
Comm(x^3*y*z^2, t*r^17),
Comm(x, t*r^-34),
Comm(y, t*r^-32),
Comm(z, t*r^-29),
Comm(x^-2*y*z, t*r^-25),
Comm(x^-1*y*z^-3, t*r^-19),
Comm(x^-3*y*z^-2, t*r^-11)
];
relations = [relations; [inv(rel) for rel in relations]]
Groups.add_rels!(caprace_group, Dict(rel => caprace_group() for rel in relations))
return caprace_group
end

22
groups/higman.jl Normal file
View File

@ -0,0 +1,22 @@
struct HigmanGroup <: GAPGroup end
name(G::HigmanGroup) = "HigmanGroup"
function group(G::HigmanGroup)
higman_group = Groups.FPGroup(["a","b","c","d"]);
a,b,c,d = gens(higman_group)
relations = [
b*Comm(b,a),
c*Comm(c,b),
d*Comm(d,c),
a*Comm(a,d)
];
relations = [relations; [inv(rel) for rel in relations]]
Groups.add_rels!(higman_group, Dict(rel => higman_group() for rel in relations))
return higman_group
end

View File

@ -0,0 +1,83 @@
struct MappingClassGroup{N} <: GAPGroup end
MappingClassGroup(args::Dict) = MappingClassGroup{args["MCG"]}()
name(G::MappingClassGroup{N}) where N = "MCG(N)"
function group(G::MappingClassGroup{N}) where N
if N < 2
throw("Genus must be at least 2!")
elseif N == 2
MCGroup = Groups.FPGroup(["a1","a2","a3","a4","a5"]);
S = gens(MCGroup)
n = length(S)
A = prod(reverse(S))*prod(S)
relations = [
[Comm(S[i], S[j]) for i in 1:n for j in 1:n if abs(i-j) > 1]...,
[S[i]*S[i+1]*S[i]*inv(S[i+1]*S[i]*S[i+1]) for i in 1:G.n-1]...,
(S[1]*S[2]*S[3])^4*inv(S[5])^2,
Comm(A, S[1]),
A^2
]
relations = [relations; [inv(rel) for rel in relations]]
Groups.add_rels!(MCGroup, Dict(rel => MCGroup() for rel in relations))
return MCGroup
else
MCGroup = Groups.FPGroup(["a$i" for i in 0:2N])
S = gens(MCGroup)
a0 = S[1]
A = S[2:end]
k = length(A)
relations = [
[Comm(A[i], A[j]) for i in 1:k for j in 1:k if abs(i-j) > 1]...,
[Comm(a0, A[i]) for i in 1:k if i != 4]...,
[A[i]*A[(i+1)]*A[i]*inv(A[i+1]*A[i]*A[i+1]) for i in 1:k-1]...,
A[4]*a0*A[4]*inv(a0*A[4]*a0)
]
# 3-chain relation
c = prod(reverse(A[1:4]))*prod(A[1:4])
b0 = c*a0*inv(c)
push!(relations, (A[1]*A[2]*A[3])^4*inv(a0*b0))
# Lantern relation
b1 = inv(A[4]*A[5]*A[3]*A[4])*a0*(A[4]*A[5]*A[3]*A[4])
b2 = inv(A[2]*A[3]*A[1]*A[2])*b1*(A[2]*A[3]*A[1]*A[2])
u = inv(A[6]*A[5])*b1*(A[6]*A[5])
x = prod(reverse(A[2:6]))*u*prod(inv.(A[1:4]))
b3 = x*a0*inv(x)
push!(relations, a0*b2*b1*inv(A[1]*A[3]*A[5]*b3))
# Hyperelliptic relation
X = prod(reverse(A))*prod(A)
function n(i::Int, b=b0)
if i == 1
return A[1]
elseif i == 2
return b
else
return w(i-2)*n(i-2)*w(i-2)
end
end
function w(i::Int)
(A[2i+4]*A[2i+3]*A[2i+2]* n(i+1))*(A[2i+1]*A[2i] *A[2i+2]*A[2i+1])*
(A[2i+3]*A[2i+2]*A[2i+4]*A[2i+3])*( n(i+1)*A[2i+2]*A[2i+1]*A[2i] )
end
# push!(relations, X*n(N)*inv(n(N)*X))
relations = [relations; [inv(rel) for rel in relations]]
Groups.add_rels!(MCGroup, Dict(rel => MCGroup() for rel in relations))
return MCGroup
end
end

62
groups/speciallinear.jl Normal file
View File

@ -0,0 +1,62 @@
struct SpecialLinearGroup{N} <: SymmetrizedGroup
group::AbstractAlgebra.Group
p::Int
X::Bool
end
function SpecialLinearGroup(args::Dict)
N = args["SL"]
p = args["p"]
X = args["X"]
if p == 0
G = MatrixSpace(Nemo.ZZ, N, N)
else
R = Nemo.NmodRing(UInt(p))
G = MatrixSpace(R, N, N)
end
return SpecialLinearGroup{N}(G, p, X)
end
function name(G::SpecialLinearGroup{N}) where N
if G.p == 0
R = (G.X ? "Z[x]" : "Z")
else
R = "F$(G.p)"
end
return SL($(G.N),$R)
end
group(G::SpecialLinearGroup) = G.group
function generatingset(G::SpecialLinearGroup{N}) where N
G.p > 0 && G.X && throw("SL(n, F_p[x]) not implemented")
SL = group(G)
return generatingset(SL, G.X)
end
# r is the injectivity radius of
# SL(n, Z[X]) -> SL(n, Z) induced by X -> 100
function generatingset(SL::MatSpace, X::Bool=false, r=5)
n = SL.cols
indexing = [(i,j) for i in 1:n for j in 1:n if i≠j]
if !X
S = [E(idx[1],idx[2],SL) for idx in indexing]
else
S = [E(i,j,SL,v) for (i,j) in indexing for v in [1, 100*r]]
end
return unique([S; inv.(S)])
end
function E(i::Int, j::Int, M::MatSpace, val=one(M.base_ring))
@assert i≠j
m = one(M)
m[i,j] = val
return m
end
function autS(G::SpecialLinearGroup{N}) where N
return WreathProduct(PermutationGroup(2), PermutationGroup(N))
end

58
logging.jl Normal file
View File

@ -0,0 +1,58 @@
using Memento
function setup_logging(filename::String, handlername::Symbol=:log)
isdir(dirname(filename)) || mkdir(dirname(filename))
logger = Memento.config!("info", fmt="{date}| {msg}")
handler = DefaultHandler(filename, DefaultFormatter("{date}| {msg}"))
logger.handlers[String(handlername)] = handler
return logger
end
macro logtime(logger, ex)
quote
local stats = Base.gc_num()
local elapsedtime = Base.time_ns()
local val = $(esc(ex))
elapsedtime = Base.time_ns() - elapsedtime
local diff = Base.GC_Diff(Base.gc_num(), stats)
local ts = time_string(elapsedtime,
diff.allocd,
diff.total_time,
Base.gc_alloc_count(diff)
)
$(esc(info))($(esc(logger)), ts)
val
end
end
function time_string(elapsedtime, bytes, gctime, allocs)
str = @sprintf("%10.6f seconds", elapsedtime/1e9)
if bytes != 0 || allocs != 0
bytes, mb = Base.prettyprint_getunits(bytes, length(Base._mem_units), Int64(1024))
allocs, ma = Base.prettyprint_getunits(allocs, length(Base._cnt_units), Int64(1000))
if ma == 1
str*= @sprintf(" (%d%s allocation%s: ", allocs, Base._cnt_units[ma], allocs==1 ? "" : "s")
else
str*= @sprintf(" (%.2f%s allocations: ", allocs, Base._cnt_units[ma])
end
if mb == 1
str*= @sprintf("%d %s%s", bytes, Base._mem_units[mb], bytes==1 ? "" : "s")
else
str*= @sprintf("%.3f %s", bytes, Base._mem_units[mb])
end
if gctime > 0
str*= @sprintf(", %.2f%% gc time", 100*gctime/elapsedtime)
end
str*=")"
elseif gctime > 0
str*= @sprintf(", %.2f%% gc time", 100*gctime/elapsedtime)
end
return str
end
import Base: info, @time
Base.info(x) = info(getlogger(), x)
macro time(x)
return :(@logtime(getlogger(Main), $(esc(x))))
end

61
main.jl Normal file
View File

@ -0,0 +1,61 @@
using PropertyT
include("FPGroups_GAP.jl")
include("groups/Allgroups.jl")
using PropertyTGroups
import PropertyT.Settings
function summarize(sett::PropertyT.Settings)
info("Threads: $(Threads.nthreads())")
info("Workers: $(workers())")
info("GroupDir: $(PropertyT.prepath(sett))")
info(string(sett.G))
info("with generating set of size $(length(sett.S))")
info("Radius: $(sett.radius)")
info("Precision: $(sett.tol)")
info("Upper bound: $(sett.upper_bound)")
info("Solver: $(sett.solver)")
end
function Settings(Gr::PropertyTGroup, args, solver)
r = get(args, "radius", 2)
gr_name = PropertyTGroups.name(Gr)*"_r$r"
G = PropertyTGroups.group(Gr)
S = PropertyTGroups.generatingset(Gr)
sol = solver
ub = get(args,"upper-bound", Inf)
tol = get(args,"tol", 1e-10)
ws = get(args, "warmstart", false)
if get(args, "nosymmetry", false)
return PropertyT.Settings(gr_name, G, S, r, sol, ub, tol, ws)
else
autS = PropertyTGroups.autS(Gr)
return PropertyT.Settings(gr_name, G, S, r, sol, ub, tol, ws, autS)
end
end
function main(::PropertyTGroup, sett::PropertyT.Settings)
isdir(PropertyT.fullpath(sett)) || mkpath(PropertyT.fullpath(sett))
summarize(sett)
return PropertyT.check_property_T(sett)
end
function main(::GAPGroup, sett::PropertyT.Settings)
isdir(PropertyT.fullpath(sett)) || mkpath(PropertyT.fullpath(sett))
summarize(sett)
S = [s for s in sett.S if s.symbols[1].pow == 1]
relations = [k*inv(v) for (k,v) in sett.G.rels]
prepare_pm_delta(PropertyT.prepath(sett), GAP_groupcode(S, relations), sett.radius)
return PropertyT.check_property_T(sett)
end

View File

@ -0,0 +1,197 @@
using AbstractAlgebra
using Groups
using GroupRings
using PropertyT
using SCS
solver(tol, iterations) =
SCSSolver(linearsolver=SCS.Direct,
eps=tol, max_iters=iterations,
alpha=1.95, acceleration_lookback=1)
include("../main.jl")
using PropertyTGroups
args = Dict("SAut" => 5, "upper-bound" => 50.0, "radius" => 2, "nosymmetry"=>false, "tol"=>1e-12, "iterations" =>200000, "warmstart" => true)
Gr = PropertyTGroups.PropertyTGroup(args)
sett = PropertyT.Settings(Gr, args,
solver(args["tol"], args["iterations"]))
@show sett
fullpath = PropertyT.fullpath(sett)
isdir(fullpath) || mkpath(fullpath)
# setup_logging(PropertyT.filename(fullpath, :fulllog), :fulllog)
function small_generating_set(RG::GroupRing{AutGroup{N}}, n) where N
indexing = [(i,j) for i in 1:n for j in 1:n if i≠j]
rmuls = [Groups.rmul_autsymbol(i,j) for (i,j) in indexing]
lmuls = [Groups.lmul_autsymbol(i,j) for (i,j) in indexing]
gen_set = RG.group.([rmuls; lmuls])
return [gen_set; inv.(gen_set)]
end
function computeX(RG::GroupRing{AutGroup{N}}) where N
Tn = small_generating_set(RG, N-1)
= Int64
Δn = length(Tn)*one(RG, ) - RG(Tn, );
Alt_N = [g for g in elements(PermutationGroup(N)) if parity(g) == 0]
@time X = sum(σ(Δn)*sum(τ(Δn) for τ Alt_N if τ σ) for σ in Alt_N);
return X
end
function Sq(RG::GroupRing{AutGroup{N}}) where N
T2 = small_generating_set(RG, 2)
= Int64
Δ₂ = length(T2)*one(RG, ) - RG(T2, );
Alt_N = [g for g in elements(PermutationGroup(N)) if parity(g) == 0]
elt = sum(σ(Δ₂)^2 for σ in Alt_N)
return elt
end
function Adj(RG::GroupRing{AutGroup{N}}) where N
T2 = small_generating_set(RG, 2)
= Int64
Δ₂ = length(T2)*one(RG, ) - RG(T2, );
Alt_N = [g for g in elements(PermutationGroup(N)) if parity(g) == 0]
adj(σ::perm, τ::perm, i=1, j=2) = Set([σ[i], σ[j]]) Set([τ[i], τ[j]])
adj(σ::perm) = [τ for τ in Alt_N if length(adj(σ, τ)) == 1]
@time elt = sum(σ(Δ₂)*sum(τ(Δ₂) for τ in adj(σ)) for σ in Alt_N);
# return RG(elt.coeffs÷factorial(N-2)^2)
return elt
end
function Op(RG::GroupRing{AutGroup{N}}) where N
T2 = small_generating_set(RG, 2)
= Int64
Δ₂ = length(T2)*one(RG, ) - RG(T2, );
Alt_N = [g for g in elements(PermutationGroup(N)) if parity(g) == 0]
adj(σ::perm, τ::perm, i=1, j=2) = Set([σ[i], σ[j]]) Set([τ[i], τ[j]])
adj(σ::perm) = [τ for τ in Alt_N if length(adj(σ, τ)) == 0]
@time elt = sum(σ(Δ₂)*sum(τ(Δ₂) for τ in adj(σ)) for σ in Alt_N);
# return RG(elt.coeffs÷factorial(N-2)^2)
return elt
end
const ELT_FILE = joinpath(dirname(PropertyT.filename(sett, )), "SqAdjOp_coeffs.jld")
const WARMSTART_FILE = PropertyT.filename(sett, :warmstart)
if isfile(PropertyT.filename(sett,)) && isfile(ELT_FILE) &&
isfile(PropertyT.filename(sett, :OrbitData))
# cached
Δ = PropertyT.loadGRElem(PropertyT.filename(sett,), sett.G)
RG = parent(Δ)
orbit_data = load(PropertyT.filename(sett, :OrbitData), "OrbitData")
sq_c, adj_c, op_c = load(ELT_FILE, "Sq", "Adj", "Op")
# elt = ELT_FILE, sett.G)
sq = GroupRingElem(sq_c, RG)
adj = GroupRingElem(adj_c, RG)
op = GroupRingElem(op_c, RG);
else
info("Compute Laplacian")
Δ = PropertyT.Laplacian(sett.S, sett.radius)
RG = parent(Δ)
info("Compute Sq, Adj, Op")
@time sq, adj, op = Sq(RG), Adj(RG), Op(RG)
PropertyT.saveGRElem(PropertyT.filename(sett, ), Δ)
save(ELT_FILE, "Sq", sq.coeffs, "Adj", adj.coeffs, "Op", op.coeffs)
info("Compute OrbitData")
if !isfile(PropertyT.filename(sett, :OrbitData))
orbit_data = PropertyT.OrbitData(parent(Y), sett.autS)
save(PropertyT.filename(sett, :OrbitData), "OrbitData", orbit_data)
else
orbit_data = load(PropertyT.filename(sett, :OrbitData), "OrbitData")
end
end;
orbit_data = PropertyT.decimate(orbit_data);
elt = adj+2op;
const SOLUTION_FILE = PropertyT.filename(sett, :solution)
if !isfile(SOLUTION_FILE)
SDP_problem, varλ, varP = PropertyT.SOS_problem(elt, Δ, orbit_data; upper_bound=sett.upper_bound)
begin
using SCS
scs_solver = SCS.SCSSolver(linearsolver=SCS.Direct,
eps=sett.tol,
max_iters=args["iterations"],
alpha=1.95,
acceleration_lookback=1)
JuMP.setsolver(SDP_problem, scs_solver)
end
λ = Ps = nothing
ws = PropertyT.warmstart(sett)
# using ProgressMeter
# @showprogress "Running SDP optimization step... " for i in 1:args["repetitions"]
while true
λ, Ps, ws = PropertyT.solve(PropertyT.filename(sett, :solverlog),
SDP_problem, varλ, varP, ws);
if all((!isnan).(ws[1]))
save(WARMSTART_FILE, "warmstart", ws, "λ", λ, "Ps", Ps)
save(WARMSTART_FILE[1:end-4]*"_$(now()).jld", "warmstart", ws, "λ", λ, "Ps", Ps)
else
warn("No valid solution was saved!")
end
end
info("Reconstructing P...")
@time P = PropertyT.reconstruct(Ps, orbit_data);
save(SOLUTION_FILE, "λ", λ, "P", P)
end
P, λ = load(SOLUTION_FILE, "P", "λ")
@show λ;
@time const Q = real(sqrtm(P));
function SOS_residual(eoi::GroupRingElem, Q::Matrix)
RG = parent(eoi)
@time sos = PropertyT.compute_SOS(RG, Q);
return eoi - sos
end
info("Floating Point arithmetic:")
EOI = elt - λ*Δ
b = SOS_residual(EOI, Q)
@show norm(b, 1);
info("Interval arithmetic:")
using IntervalArithmetic
Qint = PropertyT.augIdproj(Q);
@assert all([zero(eltype(Q)) in sum(view(Qint, :, i)) for i in 1:size(Qint, 2)])
EOI_int = elt - @interval(λ)*Δ;
Q_int = PropertyT.augIdproj(Q);
@assert all([zero(eltype(Q)) in sum(view(Q_int, :, i)) for i in 1:size(Q_int, 2)])
b_int = SOS_residual(EOI_int, Q_int)
@show norm(b_int, 1);
info("λ is certified to be > ", (@interval(λ) - 2^2*norm(b_int,1)).lo)

108
run.jl Normal file
View File

@ -0,0 +1,108 @@
using ArgParse
###############################################################################
#
# Parsing command line
#
###############################################################################
function parse_commandline()
settings = ArgParseSettings()
@add_arg_table settings begin
"--tol"
help = "set numerical tolerance for the SDP solver"
arg_type = Float64
default = 1e-6
"--iterations"
help = "set maximal number of iterations for the SDP solver"
arg_type = Int
default = 50000
"--upper-bound"
help = "Set an upper bound for the spectral gap"
arg_type = Float64
default = Inf
"--cpus"
help = "Set number of cpus used by solver"
arg_type = Int
required = false
"--radius"
help = "Radius of ball B_r(e,S) to find solution over"
arg_type = Int
default = 2
"--warmstart"
help = "Use warmstart.jld as the initial guess for SCS"
action = :store_true
"--nosymmetry"
help = "Don't use symmetries of the Laplacian"
action = :store_true
"--SL "
help = "GROUP: the group generated by elementary matrices of size n by n"
arg_type = Int
required = false
"-p"
help = "Matrices over field of p-elements (p=0 => over ZZ) [only with --SL]"
arg_type = Int
default = 0
"-X"
help = "Consider EL(N, ZZ⟨X⟩) [only with --SL]"
action = :store_true
"--SAut"
help = "GROUP: the automorphisms group of the free group on N generators"
arg_type = Int
required = false
"--MCG"
help = "GROUP: mapping class group of surface of genus N"
arg_type = Int
required = false
"--Higman"
help = "GROUP: the Higman Group"
action = :store_true
"--Caprace"
help = "GROUP: for Caprace Group"
action = :store_true
end
return parse_args(settings)
end
const PARSEDARGS = parse_commandline()
set_parallel_mthread(PARSEDARGS, workers=false)
include("CPUselect.jl")
include("logging.jl")
include("main.jl")
using SCS.SCSSolver
# using Mosek
# using CSDP
# using SDPA
solver(tol, iterations) =
SCSSolver(linearsolver=SCS.Direct,
eps=tol, max_iters=iterations,
alpha=1.95, acceleration_lookback=1)
# Mosek.MosekSolver(
# MSK_DPAR_INTPNT_CO_TOL_REL_GAP=tol,
# MSK_IPAR_INTPNT_MAX_ITERATIONS=iterations,
# QUIET=false)
# CSDP.CSDPSolver(axtol=tol, atytol=tol, objtol=tol, minstepp=tol*10.0^-1, minstepd=tol*10.0^-1)
# SDPA.SDPASolver(epsilonStar=tol, epsilonDash=tol)
const Gr = PropertyTGroups.PropertyTGroup(PARSEDARGS)
const sett = PropertyT.Settings(Gr, PARSEDARGS,
solver(PARSEDARGS["tol"], PARSEDARGS["iterations"]))
fullpath = PropertyT.fullpath(sett)
isdir(fullpath) || mkpath(fullpath)
setup_logging(PropertyT.filename(fullpath, :fulllog), :fulllog)
main(Gr, sett)

222
runtests.jl Normal file
View File

@ -0,0 +1,222 @@
using Base.Test
include("main.jl")
testdir = "tests_"*string(now())
mkdir(testdir)
include("logging.jl")
logger=setup_logging(joinpath(testdir, "tests.log"))
info(testdir)
cd(testdir)
# groupname = name(G)
# ub = PARSEDARGS["upper-bound"]
#
# fullpath = joinpath(groupname, string(ub))
# isdir(fullpath) || mkpath(fullpath)
separator(n=60) = info("\n"*("\n"*"="^n*"\n"^3)*"\n")
function SL_tests(args)
args["SL"] = 2
args["p"] = 3
G = PropertyTGroup(args)
@test main(G) == true
separator()
let args = args
args["SL"] = 2
args["p"] = 5
G = PropertyTGroup(args)
@test main(G) == false
separator()
args["warmstart"] = true
G = PropertyTGroup(args)
@test main(G) == false
separator()
args["upper-bound"] = 0.1
G = PropertyTGroup(args)
@test main(G) == true
separator()
end
args["SL"] = 2
args["p"] = 7
G = PropertyTGroup(args)
@test main(G) == false
separator()
args["SL"] = 3
args["p"] = 7
G = PropertyTGroup(args)
@test main(G) == true
separator()
# begin
# args["iterations"] = 25000
# args["N"] = 3
# args["p"] = 0
# args["upper-bound"] = Inf
#
# G = PropertyTGroups.SpecialLinearGroup(args)
# @test main(G) == false
# separator()
#
# args["warmstart"] = false
# args["upper-bound"] = 0.27
# G = PropertyTGroups.SpecialLinearGroup(args)
# @test main(G) == false
# separator()
#
# args["warmstart"] = true
# G = PropertyTGroups.SpecialLinearGroup(args)
# @test main(G) == true
# separator()
# end
return 0
end
function SAut_tests(args)
G = PropertyTGroup(args)
@test main(G) == false
separator()
args["warmstart"] = true
G = PropertyTGroup(args)
@test main(G) == false
separator()
args["upper-bound"] = 0.1
G = PropertyTGroup(args)
@test main(G) == false
separator()
return 0
end
@testset "Groups with(out) (T)" begin
@testset "GAPGroups" begin
args = Dict(
"Higman" => true,
"iterations"=>5000,
"tol"=>1e-7,
"upper-bound"=>Inf,
"cpus"=>2,
"radius"=>2,
"warmstart"=>false,
"nosymmetry"=>true,
)
G = PropertyTGroup(args)
@test main(G) == false
args = Dict(
"Caprace" => true,
"iterations"=>5000,
"tol"=>1e-7,
"upper-bound"=>Inf,
"cpus"=>2,
"radius"=>2,
"warmstart"=>false,
"nosymmetry"=>true,
)
G = PropertyTGroup(args)
@test main(G) == false
args = Dict(
"MCG" => 3,
"iterations"=>5000,
"tol"=>1e-7,
"upper-bound"=>Inf,
"cpus"=>2,
"radius"=>2,
"warmstart"=>false,
"nosymmetry"=>true,
)
G = PropertyTGroup(args)
@test main(G) == false
end
@testset "SLn's" begin
@testset "Non-Symmetrized" begin
args = Dict(
"SL" => 2,
"p" => 3,
"X" => false,
"iterations"=>50000,
"tol"=>1e-7,
"upper-bound"=>Inf,
"cpus"=>2,
"radius"=>2,
"warmstart"=>false,
"nosymmetry"=>true,
)
SL_tests(args)
end
@testset "Symmetrized" begin
args = Dict(
"SL" => 2,
"p" => 3,
"X" => false,
"iterations"=>20000,
"tol"=>1e-7,
"upper-bound"=>Inf,
"cpus"=>2,
"radius"=>2,
"warmstart"=>false,
"nosymmetry"=>false,
)
SL_tests(args)
end
end
@testset "SAutF_n's" begin
@testset "Non-Symmetrized" begin
args = Dict(
"SAut" => 2,
"iterations"=>5000,
"tol"=>1e-7,
"upper-bound"=>Inf,
"cpus"=>2,
"radius"=>2,
"warmstart"=>false,
"nosymmetry"=>true,
)
SAut_tests(args)
end
@testset "Symmetrized" begin
args = Dict(
"SAut" => 3,
"iterations"=>500,
"tol"=>1e-7,
"upper-bound"=>Inf,
"cpus"=>2,
"radius"=>2,
"warmstart"=>false,
"nosymmetry"=>false,
)
SAut_tests(args)
end
end
end