-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathOrbitalGraphs.gi
353 lines (292 loc) · 10.5 KB
/
OrbitalGraphs.gi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
# OrbitalGraphs: Computations with orbital graphs in GAP
# A GAP package by Paula Hähndel, Markus Pfeiffer, and Wilf A. Wilson.
#
# SPDX-License-Identifier: MPL-2.0
#
# Implementations
## Constructing orbital graphs
# Permutation groups
InstallMethod(OrbitalGraphs, "for a permutation group", [IsPermGroup],
{G} -> OrbitalGraphs(G, MovedPoints(G)));
InstallMethod(OrbitalGraphs, "for a permutation group and an int",
[IsPermGroup, IsInt],
function(G, n)
if n < 0 then
ErrorNoReturn("the second argument <n> must be a nonnegative integer");
fi;
return OrbitalGraphs(G, [1 .. n]);
end);
# The code below is essentially stolen from ferret; do we want to give:
# * a naive version that just computes all orbital graphs
# * a version one that only gives a representative in the isomorphism class
# * a version that gives the ones actually used in backtrack?
#
InstallMethod(OrbitalGraphs,
"for a permutation group and a homogeneous list (super basic)",
[IsPermGroup, IsHomogeneousList],
function(G, points)
local graphs, stab, innerorbits, orb, iorb;
# Commented out lines give the behaviour of the original OrbitalGraphs method
graphs := [];
for orb in Orbits(G, points) do
stab := Stabilizer(G, orb[1]);
innerorbits := Orbits(stab, MovedPoints(stab));
#innerorbits := Orbits(stab, Difference(points, [orb[1]]));
for iorb in innerorbits do
AddSet(graphs, EdgeOrbitsDigraph(G, [orb[1], iorb[1]]));
od;
od;
return graphs;
end);
# TODO decide what to do about non-moved points
InstallMethod(OrbitalGraphs, "for a permutation group and a homogeneous list",
[IsPermGroup, IsHomogeneousList],
function(G, points)
local orb, orbitsG, iorb, graph, graphlist, val, p, i, orbsizes, D,
orbpos, innerorblist, orbitsizes, orbreps, fillRepElts, maxval, moved;
if IsEmpty(points) then
return [];
elif not ForAll(points, IsPosInt) then
ErrorNoReturn("the second argument <points> must be a list of ",
"positive integers");
fi;
points := Set(points);
maxval := Maximum(points);
if not (maxval >= LargestMovedPoint(G) or
ForAll(GeneratorsOfGroup(G), g -> OnSets(points, g) = points)) then
ErrorNoReturn("the second argument <points> must be fixed setwise ",
"by the first argument <G>");
fi;
moved := Intersection(points, MovedPoints(G));
fillRepElts := function(G, orb)
local val, g, reps, buildorb, gens;
reps := [];
reps[orb[1]] := ();
buildorb := [orb[1]];
gens := GeneratorsOfGroup(G);
for val in buildorb do
for g in gens do
if not IsBound(reps[val ^ g]) then
reps[val ^ g] := reps[val] * g;
Add(buildorb, val ^ g);
fi;
od;
od;
return reps;
end;
orbitsG := Orbits(G, moved);
# FIXME: Currently unused
orbsizes := [];
# FIXME: Currently unused
orbpos := [];
# Efficently store size of orbits of values
for orb in [1 .. Length(orbitsG)] do
for i in orbitsG[orb] do
orbsizes[i] := Length(orbitsG[orb]);
orbpos[i] := orb;
od;
od;
innerorblist := List(orbitsG, o -> Orbits(Stabilizer(G, o[1]), moved));
# FIXME: Currently unused
orbitsizes := List([1 .. Length(orbitsG)],
x -> List(innerorblist[x], y -> Length(orbitsG[x]) * Length(y)));
graphlist := [];
for i in [1 .. Length(orbitsG)] do
orb := orbitsG[i];
orbreps := [];
for iorb in innerorblist[i] do
if not (Size(iorb) = 1 and orb[1] = iorb[1]) # No loopy orbitals
then
graph := List([1..maxval], x -> []);
if IsEmpty(orbreps) then
orbreps := fillRepElts(G, orb);
fi;
for val in orb do
p := orbreps[val];
graph[val] := List(iorb, x -> x^p);
od;
D := Digraph(graph);
SetUnderlyingGroup(D, G);
AddSet(graphlist, D);
fi;
od;
od;
Perform(graphlist, function(x) SetFilterObj(x, IsOrbitalGraphOfGroup); end);
return graphlist;
end);
InstallMethod(OrbitalGraphs,
"for a permutation group an a homogeneous list (no stab chain)",
[IsPermGroup, IsHomogeneousList],
-1, # TEMPORARY so that it doesn't get called
function(G, points)
local n, gens, seen, reps, graphs, root, D, orbit, schreier_gens, b, schreier_gen, innerorbits, a, i, inner;
# FIXME: Currently ignores points
n := LargestMovedPoint(G);
gens := GeneratorsOfGroup(G);
seen := BlistList([1 .. n], []);
reps := [];
graphs := [];
repeat
root := First([1 .. n], x -> not seen[x]);
seen[root] := true;
reps[root] := ();
orbit := [root];
schreier_gens := [];
# Construct a basic Schreier tree for <root> and Schreier generators for stabiliser of <root>
for a in orbit do
for i in [1 .. Length(gens)] do
b := a ^ gens[i];
if not seen[b] then
reps[b] := reps[a] * gens[i];
seen[b] := true;
Add(orbit, b);
else
schreier_gen := reps[a] * gens[i] * reps[b] ^ -1;
Add(schreier_gens, schreier_gen);
fi;
od;
od;
# Compute the orbits of the stabilizer of <root>, and hence the orbital graphs
if not IsTrivial(orbit) then
innerorbits := Orbits(Group(schreier_gens));
for inner in innerorbits do
AddSet(graphs, EdgeOrbitsDigraph(G, [orbit[1], inner[1]]));
od;
fi;
until SizeBlist(seen) = n;
return graphs;
end);
# Individual orbital graphs
InstallMethod(OrbitalGraph,
"for a permutation group, homogeneous list, and pos int",
[IsPermGroup, IsHomogeneousList, IsPosInt],
function(G, basepair, k)
local D;
if not (Length(basepair) = 2 and ForAll(basepair, IsPosInt)) then
ErrorNoReturn("the second argument <basepair> must be a pair of ",
"positive integers");
elif basepair[1] > k or basepair[2] > k or
ForAny(GeneratorsOfGroup(G), g -> ForAny([1 .. k], i -> i ^ g > k)) then
ErrorNoReturn("the third argument <k> must be such that [1..k] ",
"contains the entries of <basepair> and is preserved ",
"by G");
fi;
D := EdgeOrbitsDigraph(G, basepair, k);
SetFilterObj(D, IsOrbitalGraphOfGroup);
SetUnderlyingGroup(D, G);
SetBasePair(D, basepair);
return D;
end);
# Transformation semigroups
InstallMethod(OrbitalGraphs, "for a transformation semigroup",
[IsTransformationSemigroup],
function(S)
# FIXME This is currently super-naive
local bpts, out, D, x;
bpts := Arrangements([1..LargestMovedPoint(S)], 2);
out := [];
for x in bpts do
D := DigraphByEdges(AsList(Enumerate(Orb(S, x, OnTuples))));
SetFilterObj(D, IsOrbitalGraphOfSemigroup);
SetUnderlyingSemigroup(D, S);
SetBasePair(D, x);
AddSet(out, D);
od;
return out;
end);
## Information stored about orbital graphs at creation
InstallMethod(BasePair, "for an orbital graph of a group",
[IsOrbitalGraphOfGroup],
function(D)
local i, j, nbs;
nbs := OutNeighbours(D);
i := First(DigraphVertices(D), x -> not IsEmpty(nbs[x]));
j := Minimum(nbs[i]);
return [i, j];
end);
## Values computed from the orbital graphs of a group
InstallMethod(OrbitalClosure, "for a permutation group",
[IsPermGroup],
function(G)
if IsTrivial(G) then
return G;
fi;
return Intersection(List(OrbitalGraphs(G), AutomorphismGroup));
end);
InstallMethod(OrbitalIndex, "for a permutation group", [IsPermGroup],
{G} -> Index(OrbitalClosure(G), G));
## Recognising a group from its orbital graphs
InstallTrueMethod(IsOGR, IsStronglyOGR);
InstallMethod(IsOGR, "for a permutation group", [IsPermGroup],
function(G)
if IsTransitive(G) and Transitivity(G) > 1 then
return IsNaturalSymmetricGroup(G);
fi;
# it holds that G <= OrbitalClosure(G), so testing for size is sufficient
return Size(G) = Size(OrbitalClosure(G));
end);
InstallTrueMethod(IsStronglyOGR, IsAbsolutelyOGR);
InstallMethod(IsStronglyOGR, "for a permutation group", [IsPermGroup],
function(G)
if IsTransitive(G) and Transitivity(G) > 1 then
return IsNaturalSymmetricGroup(G);
fi;
return IsTrivial(G) or
ForAny(OrbitalGraphs(G), x -> Size(G) = Size(AutomorphismGroup(x)));
end);
InstallTrueMethod(IsAbsolutelyOGR, IsPermGroup and IsNaturalSymmetricGroup);
InstallTrueMethod(IsAbsolutelyOGR, IsPermGroup and IsTrivial);
InstallMethod(IsAbsolutelyOGR, "for a permutation group", [IsPermGroup],
function(G)
if IsTransitive(G) and Transitivity(G) > 1 then
return IsNaturalSymmetricGroup(G);
fi;
return ForAll(OrbitalGraphs(G), x -> Size(G) = Size(AutomorphismGroup(x)));
end);
## Attributes and properties of individual orbital graphs
# TODO Is it possible to cleverly determine this at creation?
# I guess we can set it to be false if we know that the points of the base pair
# are in different orbits?
InstallMethod(IsSymmetricDigraph, "for an orbital graph of a group",
[IsOrbitalGraphOfGroup],
{D} -> IsDigraphEdge(D, Reversed(BasePair(D))));
InstallMethod(IsSelfPaired, "for an orbital graph",
[IsOrbitalGraph], IsSymmetricDigraph);
# TODO Make sure that I am doing the appropriate GAP-like thing
InstallMethod(ViewString, "for an orbital graph",
[IsOrbitalGraph],
function(D)
local G, list_vertices, str;
list_vertices := IsRange(DigraphVertices(D)) or DigraphNrVertices(D) < 10;
if IsOrbitalGraphOfGroup(D) then
G := UnderlyingGroup(D);
elif IsOrbitalGraphOfSemigroup(D) then
G := UnderlyingSemigroup(D);
else
ErrorNoReturn("Unknown kind of orbital graph! ",
"No known underlying group or semigroup");
fi;
str := "<";
if IsSelfPaired(D) then
Append(str, "self-paired ");
fi;
Append(str, "orbital graph \>of ");
if HasName(G) then
Append(str, Name(G));
elif HasStructureDescription(G) then
Append(str, StructureDescription(G));
else
Append(str, ViewString(G));
fi;
Append(str, "\< on \>");
Append(str, String(DigraphNrVertices(D)));
Append(str, " vertices\<");
Append(str, " \>with \>base-pair \>(");
Append(str, String(BasePair(D)[1]));
Append(str, ",");
Append(str, String(BasePair(D)[2]));
Append(str, "),\<\< ");
Append(str, PrintString(DigraphNrEdges(D)));
Append(str, " arcs>\<");
return str;
end);