Table of Contents

PermutationGroup


Basics

def gen1 = [[1, 4, 5]].p
def gen2 = [[2, 3]].p
def group = Group(gen1, gen2)
//print all group elements
group.each { println it }
   > +[]
   > +[[2, 3]]
   > +[[1, 5, 4]]
   > +[[1, 5, 4], [2, 3]]
   > +[[1, 4, 5]]
   > +[[1, 4, 5], [2, 3]]

def sym69 = SymmetricGroup(69)
println sym69.order()
   > 17112245242814131137246833888127283909227054489352036939364804092325
        7279754140647424000000000000000
def alt54 = AlternatingGroup(54)
println alt54.order()
   > 115421848669620690236046371341513790541639282285903970566144000000000000

Examples

Let us consider some common features of PermutationGroup by a particular examples. Consider the following permutation group:

//first generator in cycle notation
def perm1 = [[1,28,5,3,13,25,8,4,17,11,29,7,2,21,23,10,6],
              [9,16,22,30,18,15,20,19,14]].p
//second generator in cycle notation
def perm2 = [[1, 4, 27], [2, 5, 3]].p
//create group 
def group = Group(perm1, perm2)


println group.order()
   > 28810681675776000


println group.orbit(20)
   > [9, 16, 22, 30, 18, 15, 20, 19, 14]


def pStabilizer =  group.pointwiseStabilizer(3, 25, 27)
println pStabilizer
   > Group( +[[9, 16, 22, 30, 18, 15, 20, 19, 14]],
            +[[1, 17, 11, 21, 23, 28, 5, 29, 7, 2, 13, 8, 4, 10, 6]],
            +[[1, 29, 2, 17, 7, 6, 23, 8, 21, 13, 11], [4, 10, 28]])
println pStabilizer.order()
   > 5884534656000


def p = [[1, 28, 2, 8, 27, 10, 11], [21, 29, 23]].p
println group.membershipTest(p)
   > true
def n = [[7, 19, 5, 17, 3, 4, 21, 25]].p
println group.membershipTest(n)
   > false


def sStabilizer = group.setwiseStabilizer(3, 27, 25)
println sStabilizer 
   > Group( +[[9, 16, 22, 30, 18, 15, 20, 19, 14]],
               +[[1, 17, 11, 21, 23, 28, 5, 29, 7, 2, 13, 8, 4, 10, 6]],
               +[[1, 29, 2, 17, 7, 6, 23, 8, 21, 13, 11], [4, 10, 28]],
               +[[1, 4], [25, 27]], +[[1, 4], [3, 25]] )
println sStabilizer.order()
   > 35307207936000


def dSubgroup = group.derivedSubgroup()
println dSubgroup.order()
 
   > 3201186852864000


def oth = Group([[1, 17, 12, 19], [23, 25]].p, [[4, 14, 26]].p)
println group.containsSubgroup(oth)
 
   > false


def union = group.union(oth)
assert union.containsSubgroup(group)
assert union.containsSubgroup(oth)

println union.order()
 
   > 4420880996869850977271808000000


def intersection = union.intersection(group)
assert intersection == group
 


println sStabilizer.leftCosetRepresentatives(pStabilizer)
 
   > [+[],
          +[[1, 4], [25, 27]],
          +[[1, 4], [3, 25]],
          +[[3, 25, 27]],
          +[[3, 27, 25]],
          +[[1, 4], [3, 27]]]


println group.centralizerOf(sStabilizer)
 
   > Group( +[[9, 14, 19, 20, 15, 18, 30, 22, 16]] )

Features

General features

The following table lists relevant features of PermutationGroup:

.center() center of group.
.centralizerOf(perm) centralizer of permutation perm.
.centralizerOf(group). centralizer of subgroup group.
.commutator(oth) commutator with oth group.
.conjugate(perm) conjugation of group with specified permutation perm( i.e. $p^{-1} G p$, where $p$ is perm and $G$ is a group).
.containsSubgroup(oth) returns whether group contains subgroup oth.
.degree() natural degree of group, i.e. largest moved point plus one.
.derivedSubgroup() derived subgroup, i.e. commutator subgroup of group with itself.
. directProduct(oth) direct product of group oth group. This product is organized as follows: the initial segment of each permutation is equal to permutation taken from group, while the rest is taken from permutation of oth.
. equals(oth) returns whether group is equal to oth in mathematical sense.
.generators() a list of group generators.
. base base of group.
. getBSGS() low level representation of base and strong generating set.
. intersection(oth) intersection of group with oth.
. isAbelian() returns whether group is abelian.
. isAlternating() returns whether group is alternating.
. isRegular() returns whether group is regular (i.e. transitive and its order equals to degree).
. isSymmetric() returns whether group is symmetric.
. isTransitive() returns whether group is transitive.
. isTransitive(from, to) returns whether group acts transitively on the the range from (inclusive) to to (exclusive).
. isTrivial() returns whether group is trivial (contains only identity element).
. leftCosetRepresentatives(subgroup) array of left coset representatives of a subgroup.
. leftTransversalOf(subgroup, perm) a unique left coset representative of perm.
. mapping(from, to) iterator over permutations that maps from points on to.
. membershipTest(perm) returns whether perm belongs to group.
. normalClosureOf(subgroup) normal closure of specified subgroup.
. orbit(points) orbit of points.
.order() group order (i.e. number of all elements).
. pointwiseStabilizer(set) pointwise stabilizer of specified set of points.
. randomElement() uniformly distributed random element from group.
. rightCosetRepresentatives(subgroup) array of right coset representatives of a subgroup.
. setwiseStabilizer(set) pointwise stabilizer of specified set of points.
. union(oth) union of group and specified group (or set of generators), i.e. group which is generated by union of generators of group and oth.

Orbits

One can take an orbit of some point using method .orbit(point):

def perm1 = [[0, 7, 4, 2, 10], [8, 5, 9]].p
def perm2 = [[0, 3, 6], [1, 4, 2]].p
def group = Group(perm1, perm2)

println group.orbit(2)
   > [0, 7, 3, 4, 6, 2, 10, 1]
println group.orbit(5)
   > [5, 9, 8]
In order to take a list of all orbits one can do
def orbits = group.orbits()
println orbits
   > [[0, 7, 3, 4, 6, 2, 10, 1], [5, 9, 8]]
Given a list of all orbits, one can take an orbit of specified point by using array positionsInOrbits: an i-th element in this array is an index of orbit of i-th point in array of orbits:
def poss = group.positionsInOrbits
println poss
   > [0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0]
//orbit of 2
println orbits[poss[2]]
   > [0, 7, 3, 4, 6, 2, 10, 1]
//orbit of 5
println orbits[poss[5]]
   > [5, 9, 8]
//check
for (int i = 0; i < group.degree(); ++i)
    assert group.orbit(i) == orbits[poss[i]]

Details

The implementation of most PermutationGroup methods relies on base and strong generating set (BSGS). The algorithms such as used in calculation of groups intersections, centralizers etc. uses backtracking techniques.

Consider for example the stabilizers chain of Rubik group:

def rot1 = [[0, 2, 7, 5], [1, 4, 6, 3],
            [8, 47, 14, 11], [9, 46, 15, 12],
            [10, 45, 16, 13]].p
def rot2 = [[5, 14, 34, 25], [6, 21, 33, 18],
            [7, 29, 32, 10], [11, 13, 28, 26],
            [12, 20, 27, 19]].p
def rot3 = [[0, 11, 32, 40], [3, 19, 35, 43],
            [5, 26, 37, 45], [8, 10, 25, 23],
            [9, 18, 24, 17]].p
def rot4 = [[0, 23, 39, 16], [1, 17, 38, 22],
            [2, 8, 37, 31], [40, 42, 47, 45],
            [41, 44, 46, 43]].p
def rot5 = [[2, 42, 34, 13], [4, 44, 36, 20],
            [7, 47, 39, 28], [14, 16, 31, 29],
            [15, 22, 30, 21]].p
def rot6 = [[23, 26, 29, 42], [24, 27, 30, 41],
            [25, 28, 31, 40], [32, 34, 39, 37],
            [33, 36, 38, 35]].p

def RubikGroup = Group(rot1, rot2, rot3, rot4, rot5, rot6)
//order of Rubik group
println RubikGroup.order()
   > 43252003274489856000
Get base of group:
//get base of group
def base = RubikGroup.base
println base
   > [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23,22,24,4,
          30,17,3,1]
Let's print order of each stabilizer in stabilizers chain:
//print stabilizers chain
for (int i = 0; i < base.length; ++i) {
    //partial base
    def pBase = base[0..i] as int[]
    print('stab of ' + pBase + ' -> ')
    print(RubikGroup.pointwiseStabilizer(pBase).order())
    println ''
}
   > stab of [0] -> 1802166803103744000
   > stab of [0,5] -> 85817466814464000
   > stab of [0,5,6] -> 3575727783936000
   > stab of [0,5,6,7] -> 198651543552000
   > stab of [0,5,6,7,10] -> 198651543552000
   > stab of [0,5,6,7,10,11] -> 198651543552000
   > stab of [0,5,6,7,10,11,12] -> 198651543552000
   > stab of [0,5,6,7,10,11,12,13] -> 198651543552000
   > stab of [0,5,6,7,10,11,12,13,14] -> 198651543552000
   > stab of [0,5,6,7,10,11,12,13,14,18] -> 9029615616000
   > stab of [0,5,6,7,10,11,12,13,14,18,19] -> 9029615616000
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20] -> 451480780800
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21] -> 451480780800
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25] -> 30098718720
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26] -> 30098718720
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27] -> 1672151040
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28] -> 139345920
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29] -> 139345920
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32] -> 139345920
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33] ->  139345920
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34] -> 139345920
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2] -> 15482880
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23] -> 2580480
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23,22] -> 161280
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23,22,24] -> 11520
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23,22,24,4] -> 960
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23,22,24,4,30] -> 96
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23,22,24,4,30,17] -> 12
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23,22,24,4,30,17,3] -> 2
   > stab of [0,5,6,7,10,11,12,13,14,18,19,20,21,25,26,27,28,29,32,33,34,2,23,22,24,4,30,17,3,1] -> 1

The algorithms used in Redberry are described in:

The details on the internal implementation can be found in API.

See also