def test_member_of(self): # Permutation group D_4: symmetries of a rectangle # 0 -- 1 <- trivial permutation # | | # 3 -- 2 # Has generators p = (0123) rotation, and q = (03)(12) reflection across horizontal plane D4_members = { 'e': [[]], 'p': [[0, 1, 2, 3]], 'pp': [[0, 2], [1, 3]], 'ppp': [[0, 3, 2, 1]], 'q': [[0, 3], [1, 2]], 'qp': [[1, 3]], 'qpp': [[0, 1], [2, 3]], 'qppp': [[0, 2]] } p = Permutation(4, cycles=D4_members['p']) q = Permutation(4, cycles=D4_members['q']) D4 = [p, q, Permutation(4)] for element in D4_members.values(): f = Permutation(4, cycles=element) self.assertTrue(member_of(f, D4)) # (3210) should not be an element f = Permutation(4, cycles=[[1, 2]]) self.assertFalse(member_of(f, D4))
def test_order_computation( self): # TODO: to a testclass of basicpermutations g = Graph(directed=False, n=6, name='g') h = Graph(directed=False, n=6, name='h') vg_0, vg_1, vg_2, vg_3, vg_4, vg_5 = g.vertices vh_0, vh_1, vh_2, vh_3, vh_4, vh_5 = h.vertices # mapping only to itself coloring_p = Coloring() coloring_p.add([vg_0, vh_0]) coloring_p.add([vg_1, vh_1]) p = Permutation(len(g.vertices), coloring=coloring_p) H = [p] self.assertEqual(1, order_computation(H)) # mapping to itself and 1 other node coloring_p = Coloring() coloring_p.add([vg_0, vh_1]) coloring_p.add([vg_1, vh_0]) p = Permutation(len(g.vertices), coloring=coloring_p) H = [p] self.assertEqual(2, order_computation(H)) # this is the permutation example of the lecture coloring_p = Coloring() coloring_p.add([vg_0, vh_1]) coloring_p.add([vg_1, vh_2]) coloring_p.add([vg_2, vh_0]) coloring_p.add([vg_4, vh_5]) coloring_p.add([vg_5, vh_4]) p = Permutation(6, coloring=coloring_p) coloring_q = Coloring() coloring_q.add([vg_2, vh_3]) coloring_q.add([vg_3, vh_2]) q = Permutation(6, coloring=coloring_q) H = [p, q] self.assertEqual(48, order_computation(H))
def test_compute_orbit(self): # H = <p,q> with p = (0,1,2)(4,5) and q = (2,3). Let alpha = 0. p = Permutation(6, cycles=[[0, 1, 2], [4, 5]]) q = Permutation(6, cycles=[[2, 3]]) orbit, transversal = compute_orbit([p, q], 0, return_transversal=True) expected_orbit = [0, 1, 2, 3] expected_transversal = [ Permutation(6), Permutation(6, cycles=[[0, 1, 2], [4, 5]]), Permutation(6, cycles=[[0, 2, 1]]), Permutation(6, cycles=[[0, 3, 2, 1]]) ] self.assertListEqual(expected_orbit, orbit) self.assertListEqual(expected_transversal, transversal) # Wolfram alpha example: http://mathworld.wolfram.com/GroupOrbit.html # H = {(0123),(1023),(0132),(1032)} h = [ Permutation(4, mapping=[0, 1, 2, 3]), Permutation(4, mapping=[1, 0, 2, 3]), Permutation(4, mapping=[0, 1, 3, 2]), Permutation(4, mapping=[1, 0, 3, 2]) ] self.assertTrue(compare([0, 1], compute_orbit(h, 0))) self.assertTrue(compare([0, 1], compute_orbit(h, 1))) self.assertTrue(compare([2, 3], compute_orbit(h, 2))) self.assertTrue(compare([2, 3], compute_orbit(h, 3)))
def test_stabilizer(self): # Wolfram alpha example: http://mathworld.wolfram.com/GroupOrbit.html # H = {(0123),(1023),(0132),(1032)} h = [ Permutation(4, mapping=[0, 1, 2, 3]), Permutation(4, mapping=[1, 0, 2, 3]), Permutation(4, mapping=[0, 1, 3, 2]), Permutation(4, mapping=[1, 0, 3, 2]) ] # Note: it the trivial permutation is ignored self.assertTrue(compare([h[2]], stabilizer(h, 0)), 'expected [[0,1,3,2]] got' + str(stabilizer(h, 0))) self.assertTrue(compare([h[2]], stabilizer(h, 1)), 'expected [[0,1,3,2]] got' + str(stabilizer(h, 0))) self.assertTrue(compare([h[1]], stabilizer(h, 2)), 'expected [[1,0,2,3]] got' + str(stabilizer(h, 0)))
def test_member_of2(self): # Another is_member test # Use example of sheets # H = <p,q> with p = (0,1,2)(4,5) and q = (2,3) p = Permutation(6, cycles=[[0, 1, 2], [4, 5]]) q = Permutation(6, cycles=[[2, 3]]) H = [p, q] f = Permutation(6, cycles=[[0, 2]]) # f = p q p^2 q p q p^2 # Check that f = p q p^2 q p q p^2 self.assertEqual(f, p * q * p**2 * q * p * q * p**2) # Check that trivial permutation is part of H self.assertTrue(Permutation(6), H) factors_of_f = [p, q, p**2, q, p, q, p**2] element = Permutation(6) for elem in factors_of_f: element *= elem self.assertTrue(member_of(element, H))
def test_permutation_coloring(self): g = Graph(directed=False, n=5) h = Graph(directed=False, n=5) vg_0, vg_1, vg_2, vg_3, vg_4 = g.vertices vh_0, vh_1, vh_2, vh_3, vh_4 = h.vertices coloring_p = Coloring() p = Permutation(0, coloring=coloring_p, g=g) self.assertEqual(0, len(p)) coloring_p.add([vg_0, vh_0]) coloring_p.add([vg_1, vh_1]) p = Permutation(2, coloring=coloring_p, g=g) self.assertEqual(0, p.P[0]) self.assertEqual(1, p.P[1]) coloring_p = Coloring() coloring_p.add([vg_0, vh_1]) coloring_p.add([vg_1, vh_0]) p = Permutation(2, coloring=coloring_p, g=g) self.assertEqual(1, p.P[0]) self.assertEqual(0, p.P[1]) # TODO: deze test gaat nog mis... coloring_p = Coloring() coloring_p.add([vg_0, vh_1]) coloring_p.add([vg_1, vh_2]) coloring_p.add([vg_2, vh_3]) coloring_p.add([vg_3, vh_4]) coloring_p.add([vg_4, vh_0]) p = Permutation(5, coloring=coloring_p, g=g) self.assertEqual(1, p.P[0]) self.assertEqual(2, p.P[1]) self.assertEqual(3, p.P[2]) self.assertEqual(4, p.P[3]) self.assertEqual(0, p.P[4])
def test_member_of3(self): # trivial permuatiation H = Permutation(n=5) f = Permutation(n=5) f2 = Permutation(n=5, cycles=[[0, 1]]) self.assertTrue(member_of(f, [H])) self.assertFalse(member_of(f2, [H])) H1 = Permutation(n=6, cycles=[[0, 1, 2], [4, 5]]) H2 = Permutation(n=6, cycles=[[2, 3]]) H = [H1, H2] f = Permutation(n=6, cycles=[[0, 2]]) self.assertTrue(f, H)
def compute_generators( g: Graph, h: Graph, start_coloring: Coloring, generators: list() = [], lastvisited: list() = list()) -> (list(), list()): """ Computes a set of generators of the mapping from graph g to graph h (Implements the algorithm of lecture 4) The coloring is refined using the fast_color-refine-algorithm. If the coloring then defines a bijection, it is checked whether this mapping is already in the set of generators. If not, the permutation is added to the set. In both cases, the coloring is put back to the last visited trivial mapping. When the coloring is undecided, the coloring branches. The first pick is the trivial mapping (if possible) and the generating set is computed recursively. Thereafter, the non-trivial mapping is computed recursively. :param Graph g: graph to determine the generators from :param Graph h: graph to be mapped to :param Coloring start_coloring: an unstable coloring :param set generators: list of generators :param DoubleLinkedList lastvisited: list of lastvisited trivial mappings :return (list, [Coloring]): a list of generators of the mapping from graph g to h """ # Do colorrefinement -> returns stable or unbalanced coloring is_previous_node_trivial = start_coloring in lastvisited new_coloring = fast_color_refine(start_coloring) coloring_status = new_coloring.status(g, h) # # No automorphism with given coloring if coloring_status == "Unbalanced": return generators, lastvisited # Unique automorphism elif coloring_status == "Bijection": perm_f = Permutation(len(g.vertices), coloring=new_coloring, g=g) # is this coloring already in the set of colorings? if len(generators) == 0 or not member_of(perm_f, generators): # put f in the set and return to last visited node generators.append(perm_f) return generators, lastvisited # Undecided else: # choose branching vertex x and cell C chosen_vertex_g, vertices = choose_color_trivial(new_coloring, g) if chosen_vertex_g is None: vertices = choose_color(new_coloring) chosen_vertex_g = choose_vertex(vertices, g) vertices_in_h = [v for v in vertices if v.in_graph(h)] trivial_mapping, non_trivial_mapping = get_mappings( chosen_vertex_g, vertices_in_h) # add this coloring to a map # if this coloring is not trivial: only do left branch (if there is a trivial, do trivial, else, do one of non_trivial) if not is_previous_node_trivial: if trivial_mapping is not None: # lastvisited[coloring] = is_trivial trivial_coloring = create_new_color_class( new_coloring, chosen_vertex_g, trivial_mapping) generators, lastvisited = compute_generators( g, h, trivial_coloring, generators=generators, lastvisited=lastvisited) else: # lastvisited[coloring] = is_trivial adapted_coloring = create_new_color_class( new_coloring, chosen_vertex_g, non_trivial_mapping[0]) generators, lastvisited = compute_generators( g, h, adapted_coloring, generators=generators, lastvisited=lastvisited) # if coloring is trivial: do all branches else: if trivial_mapping is not None: # lastvisited[coloring] = True trivial_coloring = create_new_color_class( new_coloring, chosen_vertex_g, trivial_mapping) lastvisited.append(trivial_coloring) generators, lastvisited = compute_generators( g, h, trivial_coloring, generators=generators, lastvisited=lastvisited) # lastvisited[coloring] = False for second_vertex in non_trivial_mapping: adapted_coloring = create_new_color_class( new_coloring, chosen_vertex_g, second_vertex) generators, lastvisited = compute_generators( g, h, adapted_coloring, generators=generators, lastvisited=lastvisited) return generators, lastvisited