Skip to content

Commit 0709bc7

Browse files
committed
Unify OrbitalGraphs with _BTKit.getOrbitalList
This way, _BTKit.getOrbitalList can be removed from the BacktrackKit package, and BacktrackKit can rely on the OrbitalGraphs package instead. This reduces code duplication and gives further motivation to improve the OrbitalGraphs package.
1 parent e19b02f commit 0709bc7

File tree

4 files changed

+103
-30
lines changed

4 files changed

+103
-30
lines changed

gap/OrbitalGraphs.gd

+2-2
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,9 @@ DeclareOperation("OrbitalGraphs", [IsPermGroup, IsHomogeneousList]);
6767
#! gap> D8 := Group([ (1,2,3,4), (2,4) ]);; StructureDescription(D8);
6868
#! "D8"
6969
#! gap> OrbitalGraphs(D8);
70-
#! [ <self-paired orbital graph of D8 on 4 vertices with base-pair (1,3), 4 arcs>
70+
#! [ <self-paired orbital graph of D8 on 4 vertices with base-pair (1,2), 8 arcs>
7171
#! , <self-paired orbital graph of D8 on 4 vertices
72-
#! with base-pair (1,2), 8 arcs> ]
72+
#! with base-pair (1,3), 4 arcs> ]
7373
#! @EndExampleSession
7474
DeclareOperation("OrbitalGraphs", [IsPermGroup, IsInt]);
7575

gap/OrbitalGraphs.gi

+82-23
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ end);
3030
InstallMethod(OrbitalGraphs, "for a permutation group and a homogeneous list",
3131
[IsPermGroup, IsHomogeneousList],
3232
function(G, points)
33-
local orb, orbitsG, iorb, graph, graphlist, val, p, i, orbsizes, D,
33+
local orb, orbitsG, iorb, graph, graphlist, val, p, i, orbsizes, D, cutoff,
34+
biggestOrbit, shouldSkipOneLargeOrbit, allowLoopyGraphs, search,
3435
orbpos, innerorblist, orbitsizes, orbreps, fillRepElts, maxval, moved;
3536

3637
if IsEmpty(points) then
@@ -48,6 +49,15 @@ function(G, points)
4849
fi;
4950
moved := Intersection(points, MovedPoints(G));
5051

52+
# Optimisation from BacktrackKit
53+
# Warning: in the future, if we wish to allow supporting orbital graphs
54+
# with loops, and choosing base points that are allowed to be fixed, then
55+
# this would mean that a trivial group could have some orbital graphs to
56+
# return, and so this optimisation would be invalid.
57+
if IsTrivial(G) then
58+
return [];
59+
fi;
60+
5161
fillRepElts := function(G, orb)
5262
local val, g, reps, buildorb, gens;
5363
reps := [];
@@ -65,49 +75,98 @@ function(G, points)
6575
return reps;
6676
end;
6777

68-
orbitsG := Orbits(G, moved);
78+
# Option `cutoff`:
79+
# Have a limit for the number of edges that an orbital graph that this
80+
# function will create. The default is infinity.
81+
if IsPosInt(ValueOption("cutoff")) then
82+
cutoff := ValueOption("cutoff");
83+
else
84+
cutoff := infinity;
85+
fi;
6986

70-
# FIXME: Currently unused
71-
orbsizes := [];
72-
# FIXME: Currently unused
73-
orbpos := [];
87+
# TODO: Implement this
88+
# Option `loops`:
89+
# Create OrbitalGraphs with base pair is a loop (i.e. a repeated vertex).
90+
# People do not normally want these kinds of orbital graphs, since they
91+
# only tell you about the orbit of that point.
92+
#allowLoopyGraphs := ValueOption("loops") = true;
93+
94+
# Option `search`:
95+
# If `true`, then OrbitalGraphs will not create some orbital graphs
96+
# that are useless from the point of view of a backtrack search algorithm.
97+
search := ValueOption("search") = true;
98+
99+
# Option `skipone`
100+
# If `true`, then OrbitalGraphs will not create one of the orbital graphs
101+
# that has the largest possible number of edges. In a backtrack search,
102+
# one such orbital graph can be ignored without losing anything.
103+
shouldSkipOneLargeOrbit := search or ValueOption("skipone") = true;
104+
105+
# Make sure that the orbits of G are stably sorted, so that the resulting
106+
# list of orbital graphs for a group always comes in the same order,
107+
# with the same base pairs.
108+
orbitsG := Set(Orbits(G, moved), Set);
74109

75110
# Efficently store size of orbits of values
111+
orbsizes := [];
112+
orbpos := [];
76113
for orb in [1 .. Length(orbitsG)] do
77114
for i in orbitsG[orb] do
78115
orbsizes[i] := Length(orbitsG[orb]);
79116
orbpos[i] := orb;
80117
od;
81118
od;
82119

83-
innerorblist := List(orbitsG, o -> Orbits(Stabilizer(G, o[1]), moved));
84-
# FIXME: Currently unused
120+
# FIXME: In the following line, BacktrackKit uses [1..LargestMovedPoint(G)]
121+
# instead of moved (which omits non-moved points). Is this a problem?
122+
innerorblist := List(orbitsG, o -> Set(Orbits(Stabilizer(G, o[1]), moved), Set));
85123
orbitsizes := List([1 .. Length(orbitsG)],
86124
x -> List(innerorblist[x], y -> Length(orbitsG[x]) * Length(y)));
125+
biggestOrbit := Maximum(List(orbitsizes, Maximum));
87126

88127
graphlist := [];
89128
for i in [1 .. Length(orbitsG)] do
90129
orb := orbitsG[i];
91130
orbreps := [];
92-
93131
for iorb in innerorblist[i] do
94-
if not (Size(iorb) = 1 and orb[1] = iorb[1]) # No loopy orbitals
95-
then
96-
graph := List([1..maxval], x -> []);
97-
if IsEmpty(orbreps) then
98-
orbreps := fillRepElts(G, orb);
99-
fi;
100-
for val in orb do
101-
p := orbreps[val];
102-
graph[val] := List(iorb, x -> x^p);
103-
od;
104-
D := Digraph(graph);
105-
SetUnderlyingGroup(D, G);
106-
AddSet(graphlist, D);
132+
# Find reasons to not construct this orbital graph...
133+
# (These conditions are split to allow for future Info statements
134+
# explaining what is happening, and also to make sure we have
135+
# good code coverage, and therefore that we have good tests.)
136+
if Size(orb) * Size(iorb) > cutoff then
137+
# orbital graph is too big
138+
continue;
139+
elif search and Size(iorb) = orbsizes[iorb[1]] then
140+
# orbit size unchanged
141+
continue;
142+
elif search and orbpos[orb[1]] = orbpos[iorb[1]] and Size(iorb) + 1 = orbsizes[iorb[1]] then
143+
# orbit size only removed one point
144+
continue;
145+
elif Length(iorb) = 1 and orb[1] = iorb[1] then
146+
# don't want to take the fixed point orbit
147+
continue;
148+
elif shouldSkipOneLargeOrbit and Size(orb) * Size(iorb) = biggestOrbit then
149+
# largest possible; skip
150+
shouldSkipOneLargeOrbit := false;
151+
continue;
107152
fi;
153+
154+
# Construct the orbital graph as a Digraphs package object
155+
if IsEmpty(orbreps) then
156+
orbreps := fillRepElts(G, orb);
157+
fi;
158+
graph := List([1 .. maxval], x -> []);
159+
for val in orb do
160+
p := orbreps[val];
161+
graph[val] := List(iorb, x -> x ^ p);
162+
od;
163+
D := Digraph(graph);
164+
SetFilterObj(D, IsOrbitalGraphOfGroup);
165+
SetUnderlyingGroup(D, G);
166+
Add(graphlist, D);
108167
od;
109168
od;
110-
Perform(graphlist, function(x) SetFilterObj(x, IsOrbitalGraphOfGroup); end);
169+
# Note: `graphlist` should already be stably ordered because of the uses of `Set`
111170
return graphlist;
112171
end);
113172

tst/orbitalgraphs01.tst

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ gap> START_TEST("orbitalgraphs01.tst");
1414
gap> D8 := Group([ (1,2,3,4), (2,4) ]);; StructureDescription(D8);
1515
"D8"
1616
gap> OrbitalGraphs(D8);
17-
[ <self-paired orbital graph of D8 on 4 vertices with base-pair (1,3), 4 arcs>
17+
[ <self-paired orbital graph of D8 on 4 vertices with base-pair (1,2), 8 arcs>
1818
, <self-paired orbital graph of D8 on 4 vertices
19-
with base-pair (1,2), 8 arcs> ]
19+
with base-pair (1,3), 4 arcs> ]
2020

2121
# doc/_Chapter_Orbital_graphs.xml:101-114
2222
gap> D8 := DihedralGroup(IsPermGroup, 8);

tst/orbitals.tst

+17-3
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ gap> Length(orbitals);
2727
gap> ForAll(orbitals, D -> IsDigraph(D) and DigraphNrVertices(D) = 114);
2828
true
2929
gap> List(orbitals, DigraphNrEdges);
30-
[ 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 8, 8, 8, 8, 8, 8, 8, 8,
31-
8, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100,
32-
100, 100, 200, 200, 200, 200, 2450, 2450, 2500, 2500 ]
30+
[ 2450, 100, 100, 2500, 100, 100, 200, 100, 2, 4, 100, 4, 4, 8, 100, 4, 2,
31+
100, 4, 4, 8, 2500, 100, 100, 2450, 100, 100, 200, 100, 4, 4, 100, 2, 4, 8,
32+
100, 4, 4, 100, 4, 2, 8, 200, 8, 8, 200, 8, 8, 8, 4 ]
3333
gap> Number(orbitals, IsSymmetricDigraph);
3434
8
3535
gap> Set(orbitals, x -> Minimum(DigraphEdges(x)));
@@ -107,5 +107,19 @@ gap> OrbitalGraphs(G) =
107107
> x -> OrbitalGraph(G, BasePair(x), LargestMovedPoint(G)));
108108
true
109109

110+
# OrbitalGraphs: `skipone` option
111+
gap> IsEmpty(OrbitalGraphs(SymmetricGroup(5) : skipone));
112+
true
113+
gap> Length(OrbitalGraphs(Group((1,2,3,4))));
114+
3
115+
gap> Length(OrbitalGraphs(Group((1,2,3,4)) : skipone));
116+
2
117+
118+
# OrbitalGraphs: `cutoff` option
119+
gap> Length(OrbitalGraphs(DihedralGroup(IsPermGroup, 8)));
120+
2
121+
gap> Length(OrbitalGraphs(DihedralGroup(IsPermGroup, 8) : cutoff := 4));
122+
1
123+
110124
#
111125
gap> STOP_TEST("OrbitalGraphs package: orbitalgraphs.tst", 0);

0 commit comments

Comments
 (0)