30
30
InstallMethod(OrbitalGraphs, " for a permutation group and a homogeneous list" ,
31
31
[ IsPermGroup, IsHomogeneousList] ,
32
32
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,
34
35
orbpos, innerorblist, orbitsizes, orbreps, fillRepElts, maxval, moved;
35
36
36
37
if IsEmpty(points) then
@@ -48,6 +49,15 @@ function(G, points)
48
49
fi ;
49
50
moved := Intersection(points, MovedPoints(G));
50
51
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
+
51
61
fillRepElts := function (G, orb )
52
62
local val, g, reps, buildorb, gens;
53
63
reps := [] ;
@@ -65,49 +75,98 @@ function(G, points)
65
75
return reps;
66
76
end ;
67
77
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 ;
69
86
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);
74
109
75
110
# Efficently store size of orbits of values
111
+ orbsizes := [] ;
112
+ orbpos := [] ;
76
113
for orb in [ 1 .. Length(orbitsG)] do
77
114
for i in orbitsG[ orb] do
78
115
orbsizes[ i] := Length(orbitsG[ orb] );
79
116
orbpos[ i] := orb;
80
117
od ;
81
118
od ;
82
119
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));
85
123
orbitsizes := List([ 1 .. Length(orbitsG)] ,
86
124
x -> List(innerorblist[ x] , y -> Length(orbitsG[ x] ) * Length(y)));
125
+ biggestOrbit := Maximum(List(orbitsizes, Maximum));
87
126
88
127
graphlist := [] ;
89
128
for i in [ 1 .. Length(orbitsG)] do
90
129
orb := orbitsG[ i] ;
91
130
orbreps := [] ;
92
-
93
131
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 ;
107
152
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);
108
167
od ;
109
168
od ;
110
- Perform( graphlist, function ( x ) SetFilterObj(x, IsOrbitalGraphOfGroup); end );
169
+ # Note: ` graphlist` should already be stably ordered because of the uses of `Set`
111
170
return graphlist;
112
171
end );
113
172
0 commit comments