1
0
mirror of https://github.com/kalmarek/SmallHyperbolic synced 2024-10-06 13:05:36 +02:00
SmallHyperbolic/src/FPGroups_GAP.jl

216 lines
6.1 KiB
Julia

using DelimitedFiles
abstract type WordReduction end
struct KnuthBendix <: WordReduction end
struct AutomaticStructure <: WordReduction end
const GAP_EXECUTABLE = get(ENV, "GAP_EXECUTABLE", "gap")
const PRODUCT_MATRIX_FUNCTIONS = """
MetricBalls := function(rws, radius)
local l, basis, sizes, i;
l := EnumerateReducedWords(rws, 0, radius);;
SortBy(l, Length);
sizes := [1..radius];
Apply(sizes, i -> Number(l, w -> Length(w) <= i));
return [l, sizes];
end;;
MetricBalls := function(rws, gens, radius)
local elts, sgens, sizes, r, RwsReducedProduct;
RwsReducedProduct := function(x, y) return ReducedForm(rws, x*y); end;
elts := Union([Identity(gens[1])], gens);
sgens := elts;
sizes := [1, Length(elts)];
for r in [2..radius] do;
elts := SetX(elts, sgens, RwsReducedProduct);
Add(sizes, Length(elts));
if sizes[Length(sizes)] = sizes[Length(sizes)-1] then
break;
fi;
od;
return [elts, sizes{[2..Length(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;;
"""
function product_matrix_GAP_code(reduction::Type{<:WordReduction},
G::FPGroup, dir, halfradius; maxeqns=100_000, infolevel=1)
code = """
LogTo("$(dir)/GAP.log");
RequirePackage("kbmag");
SetInfoLevel(InfoRWS, $infolevel);
$PRODUCT_MATRIX_FUNCTIONS
$(GAP_code(G))
# G:= SimplifiedFpGroup(G);;
rws := KBMAGRewritingSystem(G);
# ResetRewritingSystem(rws);;
O:=OptionsRecordOfKBMAGRewritingSystem(rws);;
O.maxeqns := $maxeqns;;
O.maxstates := 1000*$maxeqns;;
#O.maxstoredlen := [100,100];;
before := Runtimes();;
$reduction(rws);
KnuthBendix(rws);
after := Runtimes();;
delta := after.user_time_children - before.user_time_children;;
Print("$reduction time: \t", StringTime(delta), "\\n");
t := Runtime();;
A := MetricBalls(rws, [a,b,a^-1,b^-1], 10);;
B := MetricBalls(rws, [b,c,b^-1,c^-1], 10);;
C := MetricBalls(rws, [c,a,c^-1,a^-1], 10);;
S := Union(A[1], B[1], C[1]);;
S := Difference(S, [Identity(F)]);;
Print("Sizes of generated subgroups: \\n", [A[2], B[2], C[2]], "\\n");
res := MetricBalls(rws, S, $(2halfradius));;
Print("Metric-Balls generation: \t", StringTime(Runtime()-t), "\\n");
B := res[1];;
sizes := res[2];;
Print("Sizes of generated Balls: \t", sizes, "\\n");
SortBy(B,
function(x)
if x = Identity(F) then;
return 0;
elif x in S then;
return Position(S,x);
fi;
return Length(S)+Length(x);
end);;
t := Runtime();;
pm := ProductMatrix(rws, B, sizes[$halfradius]);;
Print("Computing ProductMatrix: \t", StringTime(Runtime()-t), "\\n");
# S := EnumerateReducedWords(rws, 1, 1);
S_positions := List(S, s -> Position(B,s));
SaveCSV("$(dir)/pm.csv", pm);
SaveCSV("$(dir)/S.csv", [S_positions]);
SaveCSV("$(dir)/sizes.csv", [sizes]);
SaveCSV("$(dir)/B_$(2halfradius).csv", [B]);
Print("DONE!\\n");
quit;""";
return code
end
function GAP_code(G::FPGroup)
S = gens(G);
rels = [k*inv(v) for (k,v) in G.rels]
F = "FreeGroup("*join(["\"$v\"" for v in S], ", ") *");"
m = match(r".*(\[.*\])$", string(rels))
rels_gap = replace(m.captures[1], " "=>"\n")
gap_code = """
F := $F;
AssignGeneratorVariables(F);;
relations := $rels_gap;;
G := F/relations;
"""
return gap_code
end
function GAP_execute(gap_code, dir)
isdir(dir) || mkpath(dir)
GAP_file = joinpath(dir, "GAP_code.g")
@info "Writing GAP code to $GAP_file"
open(GAP_file, "w") do io
write(io, gap_code)
end
run(pipeline(`cat $(GAP_file)`, `$GAP_EXECUTABLE -q`))
end
function prepare_pm_delta_csv(reduction::Type{<:WordReduction},
G::FPGroup, name::AbstractString, halfradius::Integer; kwargs...)
@info "Preparing multiplication table using GAP (via kbmag)"
gap_code = product_matrix_GAP_code(reduction, G, name, halfradius; kwargs...)
return GAP_execute(gap_code, name)
end
function prepare_pm_delta(reduction::Type{<:WordReduction},
G::FPGroup, name::AbstractString, halfradius::Integer; kwargs...)
pm_fname = joinpath(name, "pm.csv")
S_fname = joinpath(name, "S.csv")
sizes_fname = joinpath(name, "sizes.csv")
delta_fname = joinpath(name, "delta.jld")
csv_files_exist = isfile(pm_fname) && isfile(S_fname) && isfile(sizes_fname)
if !csv_files_exist
@info "Creating csv files"
prepare_pm_delta_csv(reduction, G, name, halfradius; kwargs...)
elseif isfile(sizes_fname)
sizes = readdlm(sizes_fname, ',')[1,:]
if 2halfradius > length(sizes)
prepare_pm_delta_csv(reduction, G, name, halfradius; kwargs...)
end
end
@assert isfile(pm_fname) && isfile(S_fname) && isfile(sizes_fname)
@info "Reading csv files"
pm = Int.(readdlm(pm_fname, ','))
S = Int.(readdlm(S_fname, ',')[1,:])
sizes = Int.(readdlm(sizes_fname, ',')[1,:])
Δ = spzeros(Int, sizes[2halfradius])
Δ[S] .= -1
Δ[1] = length(S)
pm = pm[1:sizes[halfradius], 1:sizes[halfradius]]
@info "Writing delta.jld for radius $(2halfradius)"
save(joinpath(name, "delta.jld"), "coeffs", Δ, "pm", pm)
end
function prepare_pm_delta(G::FPGroup, dir, halfradius; kwargs...)
return prepare_pm_delta(KnuthBendix, G, dir, halfradius; kwargs... )
end