Search Reduction
Last chapter, we reached the following question: given two groups \(G\) and \(H\), is there any permutation \(p \in G \cap H\) where \(1^p = i\)? To help answer it, we need another use of stabiliser chains.
Representative action
Consider the following problem.
Given a permutation group \(G\) and two lists of integers \(X = [x_1,\ldots,x_n]\) and \(Y = [y_1,\ldots,y_n]\), find a permutation \(p \in G\) such that \(x_i^p = y_i\) for all \(i\) (or prove none exists).
This problem can be answered using stabiliser chains, and in GAP is implemented by the function RepresentativeAction. Here are some examples.
RepresentativeAction(G, [1], [3], OnTuples);
# (1,3,2)
RepresentativeAction(G, [1,2], [2,3], OnTuples);
# (1,2,3)
RepresentativeAction(H, [1,2], [2,3], OnTuples);
# (1,2,3,4,6,5)
RepresentativeAction(G, [1], [2], OnTuples);
# (1,2)
RepresentativeAction(H, [1], [2], OnTuples);
# (1,2)(3,4)(5,6)
RepresentativeAction(G, [1,2], [2,6], OnTuples);
# fail
RepresentativeAction(H, [1,2], [2,6], OnTuples);
# (1,2,6,5,3,4)What can we tell from these? As \(G\) does not contain a permutation which maps \([1,2]\) to \([2,6]\), then \(G \cap H\) certainly cannot contain one either.
Does \(G \cap H\) contain a permutation which maps \([1]\) to \([2]\)? We don’t know yet. Just because \(G\) and \(H\) both contain such permutations doesn’t mean they share any.
This pattern will recur throughout backtrack search: we try to prove a subproblem has an answer or not, and get back yes, no, or don’t know.
So how can we find the elements of \(G \cap H\) where \(1^p = 2\)? More splitting. If \(G \cap H\) contains a permutation such that \(1^p = 2\), we consider where that permutation maps \(2\), splitting our search into another \(n-1\) pieces:
- Find \(p \in G \cap H\) where \(1^p = 2\), \(2^p = 1\).
- Find \(p \in G \cap H\) where \(1^p = 2\), \(2^p = 3\).
- Find \(p \in G \cap H\) where \(1^p = 2\), \(2^p = 4\).
- \(\ldots\)
There are \(n-1\) pieces, because there can’t be a permutation \(p\) where \(1^p = 2\) and \(2^p = 2\).
So, what do we find here? \(G\) maps \([1,2]\) only to \([2,1]\) and \([2,3]\), while \(H\) maps \([1,2]\) to \([2,1]\), \([2,3]\), and \([2,6]\). So any element of \(G \cap H\) which maps \(1\) to \(2\) must map \([1,2]\) to \([2,1]\).
Continuing: mapping \([1,2,3]\) to \([2,1,x]\) for different \(x\)? It turns out \(G\) only maps to \([2,1,3]\), while \(H\) maps to \([2,1,4]\) and \([2,1,5]\). So there is no element of \(G \cap H\) which maps \(1\) to \(2\).
Let’s encapsulate this as a GAP function. It is fairly long, but we’ll work through it.
Aside: Info
The Info function lets us print information about what our algorithm is doing. It takes a name for the type of info (we use InfoGroup, for group algorithms), the importance (lower numbers are more important), and the information to print. To see messages, run SetInfoLevel(InfoGroup, 1);, which prints all messages of level 1 or less.
Algorithm
FindExtendingElementGH takes two groups, a bound maxpnt, and an array \(\mathit{Array} = [x_1,\dots,x_n]\). It searches for a permutation \(p \in G \cap H\) such that \(i^p = \mathit{Array}[i]\) for all \(i \in \{1,\ldots,n\}\).
## Given two groups G and H and an array Array,
## find a permutation p in G and H such that i^p = Array[i]
## for all i in [1..Length(Array)],
## where G and H are subgroups of SymmetricGroup(maxpnt).
## Returns p, or fail if no such permutation exists
FindExtendingElementGH := function(G, H, maxpnt, Array)
local pg, ph, retperm, n, i, newarray;
n := Length(Array);
# First we look for permutations which map [1..n] to Array
# if either returns fail, then return fail
if RepresentativeAction(G, [1..n], Array, OnTuples) = fail then
Info(InfoGroup, 3, "FEE: No permutation in G for ", Array);
return fail;
fi;
if RepresentativeAction(H, [1..n], Array, OnTuples) = fail then
Info(InfoGroup, 3, "FEE: No permutation in H for ", Array);
return fail;
fi;
# Check if we have assigned all points
# PermList turns a list into a GAP permutation
if n = maxpnt then
Info(InfoGroup, 3, "FEE: Found ", PermList(Array));
return PermList(Array);
fi;
# In this case, we need to recursively search.
# Let's try adding a new member to our array.
# We don't bother skipping the case where we would
# build non-permutations; they will just fail.
Info(InfoGroup, 3, "FEE: Extending ", Array, " with another point");
for i in [1..maxpnt] do
newarray := Concatenation(Array, [i]);
retperm := FindExtendingElementGH(G, H, maxpnt, newarray);
if retperm <> fail then
return retperm;
fi;
od;
return fail;
end;Let’s try running it:
SetInfoLevel(InfoGroup, 3);
FindExtendingElementGH(G, H, 6, [3]);
#I FEE: Extending [ 3 ] with another point
#I FEE: No permutation in H for [ 3, 1 ]
#I FEE: Extending [ 3, 2 ] with another point
#I FEE: Extending [ 3, 2, 1 ] with another point
#I FEE: No permutation in G for [ 3, 2, 1, 1 ]
#I FEE: No permutation in G for [ 3, 2, 1, 2 ]
#I FEE: No permutation in G for [ 3, 2, 1, 3 ]
#I FEE: Extending [ 3, 2, 1, 4 ] with another point
#I FEE: No permutation in G for [ 3, 2, 1, 4, 1 ]
#I FEE: No permutation in G for [ 3, 2, 1, 4, 2 ]
#I FEE: No permutation in G for [ 3, 2, 1, 4, 3 ]
#I FEE: No permutation in G for [ 3, 2, 1, 4, 4 ]
#I FEE: Extending [ 3, 2, 1, 4, 5 ] with another point
#I FEE: No permutation in G for [ 3, 2, 1, 4, 5, 1 ]
#I FEE: No permutation in G for [ 3, 2, 1, 4, 5, 2 ]
#I FEE: No permutation in G for [ 3, 2, 1, 4, 5, 3 ]
#I FEE: No permutation in G for [ 3, 2, 1, 4, 5, 4 ]
#I FEE: No permutation in G for [ 3, 2, 1, 4, 5, 5 ]
#I FEE: Found (1,3)
(1,3)Aside: LargestMovedPoint
We assume our permutation groups act on a finite set \(\{1, \ldots, n\}\). But how do we find \(n\)? GAP does not store this value — it acts as if all groups act on \(\mathbb{N}\). We could pass \(n\) around in all our functions, but instead we use LargestMovedPoint(G). This gives the largest integer \(n\) moved by any member of \(G\); we can ignore any larger points when doing intersections.
# Find the intersection of two permutation groups G and H on [1..n],
# assuming that G and H both fix [1..pnt]
# (pnt might be 0, then G and H may fix nothing)
BasicIntersectionLoop := function(G, H, n, pnt)
local loopgroup, loopG, loopH, i, cosetreps, rep;
# Base case: if either G or H is the identity group,
# the intersection is the identity group.
if G = Group(()) or H = Group(()) then
Info(InfoGroup, 1, "Reached base case");
return [()];
fi;
# Recursive call for intersection of point
# stabiliser of pnt + 1
loopG := Stabilizer(G, pnt + 1);
loopH := Stabilizer(H, pnt + 1);
loopgroup := BasicIntersectionLoop(loopG, loopH, n, pnt + 1);
# Now look for coset representatives
cosetreps := [];
for i in [pnt + 2..n] do
rep := FindExtendingElementGH(G,H, n,
Concatenation([1..pnt], [i]));
if rep <> fail then
Add(cosetreps, rep);
fi;
od;
return Concatenation(loopgroup, cosetreps);
end;
# Set up our recursive loop, finding the set which G
# and H act on (using LargestMovedPoint).
BasicIntersection := function(G, H)
local lmp;
lmp := Minimum(LargestMovedPoint(G), LargestMovedPoint(H));
return Group(BasicIntersectionLoop(G, H, lmp, 0));
end;Now we have a basic intersection algorithm which produces a set of generators. How can we improve this search? The next chapter begins to answer that.