def generate_dimino(self, af=False): """ yield group elements using Dimino's algorithm If af == True it yields the array form of the permutations Reference: [1] The implementation of various algorithms for Permutation Groups in the Computer Algebra System: AXIOM, N.J. Doye, M.Sc. Thesis Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([0, 2, 1, 3]) >>> b = Permutation([0, 2, 3, 1]) >>> g = PermutationGroup([a, b]) >>> list(g.generate_dimino(af=True)) [[0, 1, 2, 3], [0, 2, 1, 3], [0, 2, 3, 1], [0, 1, 3, 2], [0, 3, 2, 1], [0, 3, 1, 2]] """ idn = range(self.degree) order = 0 element_list = [idn] set_element_list = set([tuple(idn)]) if af: yield idn else: yield _new_from_array_form(idn) gens = [p.array_form for p in self.generators] for i in xrange(len(gens)): # D elements of the subgroup G_i generated by gens[:i] D = element_list[:] N = [idn] while N: A = N N = [] for a in A: for g in gens[:i + 1]: ag = perm_af_mul(a, g) if tuple(ag) not in set_element_list: # produce G_i*g for d in D: order += 1 ap = perm_af_mul(d, ag) if af: yield ap else: p = _new_from_array_form(ap) yield p element_list.append(ap) set_element_list.add(tuple(ap)) N.append(ap) self._order = len(element_list)
def generate_dimino(self, af=False): """ yield group elements using Dimino's algorithm If af == True it yields the array form of the permutations Reference: [1] The implementation of various algorithms for Permutation Groups in the Computer Algebra System: AXIOM, N.J. Doye, M.Sc. Thesis Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([0, 2, 1, 3]) >>> b = Permutation([0, 2, 3, 1]) >>> g = PermutationGroup([a, b]) >>> list(g.generate_dimino(af=True)) [[0, 1, 2, 3], [0, 2, 1, 3], [0, 2, 3, 1], [0, 1, 3, 2], [0, 3, 2, 1], [0, 3, 1, 2]] """ idn = range(self.degree) order = 0 element_list = [idn] set_element_list = set([tuple(idn)]) if af: yield idn else: yield _new_from_array_form(idn) gens = [p.array_form for p in self.generators] for i in xrange(len(gens)): # D elements of the subgroup G_i generated by gens[:i] D = element_list[:] N = [idn] while N: A = N N = [] for a in A: for g in gens[:i+1]: ag = perm_af_mul(a, g) if tuple(ag) not in set_element_list: # produce G_i*g for d in D: order += 1 ap = perm_af_mul(d, ag) if af: yield ap else: p = _new_from_array_form(ap) yield p element_list.append(ap) set_element_list.add(tuple(ap)) N.append(ap) self._order = len(element_list)
def coset_rank(self, g): """ rank using Schreier-Sims representation The coset rank of `g` is the ordering number in which it appears in the lexicographic listing according to the coset decomposition, see coset_decomposition; the ordering is the same as in G.generate(method='coset'). If `g` does not belong to the group it returns None Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([[0, 1, 3, 7, 6, 4], [2, 5]]) >>> b = Permutation([[0, 1, 3, 2], [4, 5, 7, 6]]) >>> G = PermutationGroup([a, b]) >>> c = Permutation([[0, 1, 2, 3, 4], [5, 6, 7]]) >>> G.coset_rank(c) >>> c = Permutation([[0, 6], [1, 7], [2, 4], [3, 5]]) >>> G.coset_rank(c) 40 >>> G.coset_unrank(40, af=True) [6, 7, 4, 5, 2, 3, 0, 1] """ u = self.coset_repr() if isinstance(g, Permutation): g = g.array_form g1 = g m = len(u) a = [] un = self._coset_repr_n n = self.degree rank = 0 base = [1] for i in un[m:0:-1]: base.append(base[-1]*i) base.reverse() a1 = [0]*m i1 = -1 for i in self._base: i1 += 1 x = g1[i] for j, h in enumerate(u[i]): if h[i] == x: a.append(h) a1[i] = j rank += j*base[i1] p2 = perm_af_invert(h) g1 = perm_af_mul(p2, g1) break else: return None if perm_af_muln(*a) == g: return rank return None
def coset_rank(self, g): """ rank using Schreier-Sims representation The coset rank of `g` is the ordering number in which it appears in the lexicographic listing according to the coset decomposition, see coset_decomposition; the ordering is the same as in G.generate(method='coset'). If `g` does not belong to the group it returns None Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([[0, 1, 3, 7, 6, 4], [2, 5]]) >>> b = Permutation([[0, 1, 3, 2], [4, 5, 7, 6]]) >>> G = PermutationGroup([a, b]) >>> c = Permutation([[0, 1, 2, 3, 4], [5, 6, 7]]) >>> G.coset_rank(c) >>> c = Permutation([[0, 6], [1, 7], [2, 4], [3, 5]]) >>> G.coset_rank(c) 40 >>> G.coset_unrank(40, af=True) [6, 7, 4, 5, 2, 3, 0, 1] """ u = self.coset_repr() if isinstance(g, Permutation): g = g.array_form g1 = g m = len(u) a = [] un = self._coset_repr_n n = self.degree rank = 0 base = [1] for i in un[m:0:-1]: base.append(base[-1] * i) base.reverse() a1 = [0] * m i1 = -1 for i in self._base: i1 += 1 x = g1[i] for j, h in enumerate(u[i]): if h[i] == x: a.append(h) a1[i] = j rank += j * base[i1] p2 = perm_af_invert(h) g1 = perm_af_mul(p2, g1) break else: return None if perm_af_muln(*a) == g: return rank return None
def test_muln(): n = 6 m = 8 a = [Permutation.unrank_nonlex(n, i).array_form for i in range(m)] h = range(n) for i in range(m): h = perm_af_mul(h, a[i]) h2 = perm_af_muln(*a[:i+1]) assert h == h2
def test_muln(): n = 6 m = 8 a = [Permutation.unrank_nonlex(n, i).array_form for i in range(m)] h = range(n) for i in range(m): h = perm_af_mul(h, a[i]) h2 = perm_af_muln(*a[:i + 1]) assert h == h2
def generate_schreier_sims(self, af=False): """ yield group elements using the Schreier-Sims representation If af = True it yields the array form of the permutations Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([0, 2, 1, 3]) >>> b = Permutation([0, 2, 3, 1]) >>> g = PermutationGroup([a, b]) >>> list(g.generate_schreier_sims(af=True)) [[0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 3, 1], [0, 2, 1, 3], [0, 3, 2, 1], [0, 3, 1, 2]] """ def get1(posmax): n = len(posmax) - 1 for i in range(n,-1,-1): if posmax[i] != 1: return i + 1 n = self.degree u = self.coset_repr() # stg stack of group elements stg = [range(n)] # posmax[i] = len(u[i]) posmax = [len(x) for x in u] n1 = get1(posmax) pos = [0]*n1 posmax = posmax[:n1] h = 0 while 1: # backtrack when finished iterating over coset if pos[h] >= posmax[h]: if h == 0: raise StopIteration pos[h] = 0 h -= 1 stg.pop() continue p = perm_af_mul(stg[-1], u[h][pos[h]]) pos[h] += 1 stg.append(p) h += 1 if h == n1: if af: yield p else: p1 = _new_from_array_form(p) yield p1 stg.pop() h -= 1
def orbit_traversal(self, alpha, af=False): """ compute the orbit traversal Output: list of group elements; applying each to alpha one gets the orbit of alpha Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([0, 2, 1]) >>> b = Permutation([1, 0, 2]) >>> G = PermutationGroup([a, b]) >>> G.orbit_traversal(1, af=True) [[1, 0, 2], [0, 1, 2], [0, 2, 1]] """ genv = [p.array_form for p in self.generators] n = self._degree coset_repr = [None] * n if af: coset_repr[alpha] = range(n) else: coset_repr[alpha] = Permutation(range(n)) h = 0 r = len(genv) stg = [range(n)] sta = [alpha] pos = [0] * n while 1: # backtrack when finished iterating over generators if pos[h] >= r: if h == 0: return coset_repr pos[h] = 0 h -= 1 sta.pop() stg.pop() continue g = genv[pos[h]] pos[h] += 1 alpha = sta[-1] ag = g[alpha] if coset_repr[ag] == None: gen1 = perm_af_mul(g, stg[-1]) if af: coset_repr[ag] = gen1 else: coset_repr[ag] = Permutation(gen1) sta.append(ag) stg.append(gen1) h += 1
def generate_schreier_sims(self, af=False): """ yield group elements using the Schreier-Sims representation If af = True it yields the array form of the permutations Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([0, 2, 1, 3]) >>> b = Permutation([0, 2, 3, 1]) >>> g = PermutationGroup([a, b]) >>> list(g.generate_schreier_sims(af=True)) [[0, 1, 2, 3], [0, 1, 3, 2], [0, 2, 3, 1], [0, 2, 1, 3], [0, 3, 2, 1], [0, 3, 1, 2]] """ def get1(posmax): n = len(posmax) - 1 for i in range(n, -1, -1): if posmax[i] != 1: return i + 1 n = self.degree u = self.coset_repr() # stg stack of group elements stg = [range(n)] # posmax[i] = len(u[i]) posmax = [len(x) for x in u] n1 = get1(posmax) pos = [0] * n1 posmax = posmax[:n1] h = 0 while 1: # backtrack when finished iterating over coset if pos[h] >= posmax[h]: if h == 0: raise StopIteration pos[h] = 0 h -= 1 stg.pop() continue p = perm_af_mul(stg[-1], u[h][pos[h]]) pos[h] += 1 stg.append(p) h += 1 if h == n1: if af: yield p else: p1 = _new_from_array_form(p) yield p1 stg.pop() h -= 1
def orbit_traversal(self, alpha, af=False): """ compute the orbit traversal Output: list of group elements; applying each to alpha one gets the orbit of alpha Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([0, 2, 1]) >>> b = Permutation([1, 0, 2]) >>> G = PermutationGroup([a, b]) >>> G.orbit_traversal(1, af=True) [[1, 0, 2], [0, 1, 2], [0, 2, 1]] """ genv = [p.array_form for p in self.generators] n = self._degree coset_repr = [None]*n if af: coset_repr[alpha] = range(n) else: coset_repr[alpha] = Permutation(range(n)) h = 0 r = len(genv) stg = [range(n)] sta = [alpha] pos = [0]*n while 1: # backtrack when finished iterating over generators if pos[h] >= r: if h == 0: return coset_repr pos[h] = 0 h -= 1 sta.pop() stg.pop() continue g = genv[pos[h]] pos[h] += 1 alpha = sta[-1] ag = g[alpha] if coset_repr[ag] == None: gen1 = perm_af_mul(g, stg[-1]) if af: coset_repr[ag] = gen1 else: coset_repr[ag] = Permutation(gen1) sta.append(ag) stg.append(gen1) h += 1
def orbit(self, alpha): """ compute the orbit {g[i] for g in G} It returns the orbit as a set. Examples ======== >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> from sympy.combinatorics.permutations import Permutation >>> a = Permutation([2, 0, 1]) >>> b = Permutation([2, 1, 0]) >>> g = PermutationGroup([a, b]) >>> g.orbit(0) set([0, 1, 2]) >>> g.orbits() [set([0, 1, 2])] >>> g.orbits(rep=True) [0] """ genv = [p.array_form for p in self.generators] n = self._degree orb = set([alpha]) h = 0 r = len(genv) stg = [range(n)] sta = [alpha] pos = [0]*n while 1: # backtrack when finished iterating over generators if pos[h] >= r: if h == 0: return orb pos[h] = 0 h -= 1 sta.pop() stg.pop() continue g = genv[pos[h]] pos[h] += 1 alpha = sta[-1] ag = g[alpha] if ag not in orb: gen1 = perm_af_mul(g, stg[-1]) orb.add(ag) sta.append(ag) stg.append(gen1) h += 1
def orbit(self, alpha): """ compute the orbit {g[i] for g in G} It returns the orbit as a set. Examples ======== >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> from sympy.combinatorics.permutations import Permutation >>> a = Permutation([2, 0, 1]) >>> b = Permutation([2, 1, 0]) >>> g = PermutationGroup([a, b]) >>> g.orbit(0) set([0, 1, 2]) >>> g.orbits() [set([0, 1, 2])] >>> g.orbits(rep=True) [0] """ genv = [p.array_form for p in self.generators] n = self._degree orb = set([alpha]) h = 0 r = len(genv) stg = [range(n)] sta = [alpha] pos = [0] * n while 1: # backtrack when finished iterating over generators if pos[h] >= r: if h == 0: return orb pos[h] = 0 h -= 1 sta.pop() stg.pop() continue g = genv[pos[h]] pos[h] += 1 alpha = sta[-1] ag = g[alpha] if ag not in orb: gen1 = perm_af_mul(g, stg[-1]) orb.add(ag) sta.append(ag) stg.append(gen1) h += 1
def insert(self, g, alpha): """ insert permutation `g` in stabilizer chain at point alpha """ n = len(g) if not g == self.idn: vertex = self.vertex jg = self.jg i = _smallest_change(g, alpha) ig = g[i] nn = vertex[i].index_neighbor[ig] if nn >= 0: # if ig is already neighbor of i jginn = jg[vertex[i].perm[nn]] if g != jginn: # cycle consisting of two edges; # replace jginn by g and insert h = g**-1*jginn g1 = perm_af_invert(g) h = perm_af_mul(g1, jginn) jg[ vertex[i].perm[nn] ] = g self.insert(h, alpha) else: # new edge self.insert_edge(g, i, ig) self.cycle = [i] if self.find_cycle(i, i, ig, -1): cycle = self.cycle cycle.append(cycle[0]) # find the smallest point (vertex) of the cycle minn = min(cycle) cmin = cycle.index(minn) # now walk around the cycle starting from the smallest # point, and multiply around the cycle to obtain h # satisfying h[cmin] = cmin ap = [] for c in range(cmin, len(cycle)-1) + range(cmin): i = cycle[c] j = cycle[c+1] nn = vertex[i].index_neighbor[j] p = jg[ vertex[i].perm[nn] ] if i > j: p = perm_af_invert(p) ap.append(p) ap.reverse() h = perm_af_muln(*ap) self.remove_edge(cycle[cmin], cycle[cmin + 1]) self.insert(h, alpha)
def coset_decomposition(self, g): """ Decompose `g` as h_0*...*h_{len(u)} The Schreier-Sims coset representation u of `G` gives a univoque decomposition of an element `g` as h_0*...*h_{len(u)}, where h_i belongs to u[i] Output: [h_0, .., h_{len(u)}] if `g` belongs to `G` False otherwise Examples ======== >>> from sympy.combinatorics.permutations import Permutation >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> a = Permutation([[0, 1, 3, 7, 6, 4], [2, 5]]) >>> b = Permutation([[0, 1, 3, 2], [4, 5, 7, 6]]) >>> G = PermutationGroup([a, b]) >>> c = Permutation([[0, 1, 2, 3, 4], [5, 6, 7]]) >>> G.coset_decomposition(c) False >>> c = Permutation([[0, 6], [1, 7], [2, 4], [3, 5]]) >>> G.coset_decomposition(c) [[6, 4, 2, 0, 7, 5, 3, 1], [0, 4, 1, 5, 2, 6, 3, 7], [0, 1, 2, 3, 4, 5, 6, 7]] >>> G.has_element(c) True """ u = self.coset_repr() if isinstance(g, Permutation): g = g.array_form g1 = g n = len(u) a = [] for i in range(n): x = g1[i] for h in u[i]: if h[i] == x: a.append(h) p2 = perm_af_invert(h) g1 = perm_af_mul(p2, g1) break else: return False if perm_af_muln(*a) == g: return a return False
def insert(self, g, alpha): """ insert permutation `g` in stabilizer chain at point alpha """ n = len(g) if not g == self.idn: vertex = self.vertex jg = self.jg i = _smallest_change(g, alpha) ig = g[i] nn = vertex[i].index_neighbor[ig] if nn >= 0: # if ig is already neighbor of i jginn = jg[vertex[i].perm[nn]] if g != jginn: # cycle consisting of two edges; # replace jginn by g and insert h = g**-1*jginn g1 = perm_af_invert(g) h = perm_af_mul(g1, jginn) jg[vertex[i].perm[nn]] = g self.insert(h, alpha) else: # new edge self.insert_edge(g, i, ig) self.cycle = [i] if self.find_cycle(i, i, ig, -1): cycle = self.cycle cycle.append(cycle[0]) # find the smallest point (vertex) of the cycle minn = min(cycle) cmin = cycle.index(minn) # now walk around the cycle starting from the smallest # point, and multiply around the cycle to obtain h # satisfying h[cmin] = cmin ap = [] for c in range(cmin, len(cycle) - 1) + range(cmin): i = cycle[c] j = cycle[c + 1] nn = vertex[i].index_neighbor[j] p = jg[vertex[i].perm[nn]] if i > j: p = perm_af_invert(p) ap.append(p) ap.reverse() h = perm_af_muln(*ap) self.remove_edge(cycle[cmin], cycle[cmin + 1]) self.insert(h, alpha)
def schreier_tree(self, alpha, gen): """ traversal of the orbit of alpha Compute a traversal of the orbit of alpha, storing the values in G._coset_repr; G._coset_repr[i][alpha] = i if i belongs to the orbit of alpha. """ G = self.G G._coset_repr[alpha] = gen G._coset_repr_n += 1 genv = self.gens[:self.r] h = 0 r = self.r stg = [gen] sta = [alpha] pos = [0]*self.n while 1: # backtrack when finished iterating over generators if pos[h] >= r: if h == 0: return pos[h] = 0 h -= 1 sta.pop() stg.pop() continue g = genv[pos[h]] pos[h] += 1 alpha = sta[-1] ag = g[alpha] if G._coset_repr[ag] == None: gen1 = perm_af_mul(g, stg[-1]) G._coset_repr[ag] = gen1 G._coset_repr_n += 1 sta.append(ag) stg.append(gen1) h += 1
def schreier_tree(self, alpha, gen): """ traversal of the orbit of alpha Compute a traversal of the orbit of alpha, storing the values in G._coset_repr; G._coset_repr[i][alpha] = i if i belongs to the orbit of alpha. """ G = self.G G._coset_repr[alpha] = gen G._coset_repr_n += 1 genv = self.gens[:self.r] h = 0 r = self.r stg = [gen] sta = [alpha] pos = [0] * self.n while 1: # backtrack when finished iterating over generators if pos[h] >= r: if h == 0: return pos[h] = 0 h -= 1 sta.pop() stg.pop() continue g = genv[pos[h]] pos[h] += 1 alpha = sta[-1] ag = g[alpha] if G._coset_repr[ag] == None: gen1 = perm_af_mul(g, stg[-1]) G._coset_repr[ag] = gen1 G._coset_repr_n += 1 sta.append(ag) stg.append(gen1) h += 1
def test_Permutation(): p = Permutation([2, 5, 1, 6, 3, 0, 4]) q = Permutation([[1], [0, 3, 5, 6, 2, 4]]) assert Permutation(p.cyclic_form).array_form == p.array_form assert p.cardinality == 5040 assert q.cardinality == 5040 assert q.cycles == 2 assert q * p == Permutation([4, 6, 1, 2, 5, 3, 0]) assert p * q == Permutation([6, 5, 3, 0, 2, 4, 1]) assert perm_af_mul([2, 5, 1, 6, 3, 0, 4], [3, 1, 4, 5, 0, 6, 2]) == \ [6, 5, 3, 0, 2, 4, 1] assert cyclic([(2, 3, 5)], 5) == [[1, 2, 4], [0], [3]] assert (Permutation([[1,2,3],[0,4]])*Permutation([[1,2,4],[0],[3]])).cyclic_form == \ [[1, 3], [0, 4, 2]] assert q.array_form == [3, 1, 4, 5, 0, 6, 2] assert p.cyclic_form == [[3, 6, 4], [0, 2, 1, 5]] assert p.transpositions() == [(3, 4), (3, 6), (0, 5), (0, 1), (0, 2)] assert p**13 == p assert q**2 == Permutation([5, 1, 0, 6, 3, 2, 4]) assert p + q == Permutation([5, 6, 3, 1, 2, 4, 0]) assert q + p == p + q assert p - q == Permutation([6, 3, 5, 1, 2, 4, 0]) assert q - p == Permutation([1, 4, 2, 6, 5, 3, 0]) a = p - q b = q - p assert (a + b).is_Identity assert len(p.atoms()) == 7 assert q.atoms() == set([0, 1, 2, 3, 4, 5, 6]) assert p.inversion_vector() == [2, 4, 1, 3, 1, 0] assert q.inversion_vector() == [3, 1, 2, 2, 0, 1] assert Permutation.from_inversion_vector(p.inversion_vector()) == p assert Permutation.from_inversion_vector(q.inversion_vector()).array_form\ == q.array_form assert Permutation([i for i in range(500, -1, -1)]).inversions() == 125250 assert Permutation([0, 4, 1, 3, 2]).parity() == 0 assert Permutation([0, 1, 4, 3, 2]).parity() == 1 assert perm_af_parity([0, 4, 1, 3, 2]) == 0 assert perm_af_parity([0, 1, 4, 3, 2]) == 1 s = Permutation([0]) assert s.is_Singleton r = Permutation([3, 2, 1, 0]) assert (r**2).is_Identity assert (p * (~p)).is_Identity assert (~p)**13 == Permutation([5, 2, 0, 4, 6, 1, 3]) assert ~(r**2).is_Identity assert p.max() == 6 assert p.min() == 0 q = Permutation([[6], [5], [0, 1, 2, 3, 4]]) assert q.max() == 4 assert q.min() == 0 p = Permutation([1, 5, 2, 0, 3, 6, 4]) q = Permutation([[1, 2, 3, 5, 6], [0, 4]]) assert p.ascents() == [0, 3, 4] assert q.ascents() == [1, 2, 4] assert r.ascents() == [] assert p.descents() == [1, 2, 5] assert q.descents() == [0, 3, 5] assert Permutation(r.descents()).is_Identity assert p.inversions() == 7 assert p.signature() == -1 assert q.inversions() == 11 assert q.signature() == -1 assert (p * (~p)).inversions() == 0 assert (p * (~p)).signature() == 1 assert p.order() == 6 assert q.order() == 10 assert (p**(p.order())).is_Identity assert p.length() == 6 assert q.length() == 7 assert r.length() == 4 assert not p.is_Positive assert p.is_Negative assert not q.is_Positive assert q.is_Negative assert r.is_Positive assert not r.is_Negative assert p.runs() == [[1, 5], [2], [0, 3, 6], [4]] assert q.runs() == [[4], [2, 3, 5], [0, 6], [1]] assert r.runs() == [[3], [2], [1], [0]] assert p.index() == 8 assert q.index() == 8 assert r.index() == 3 assert p.get_precedence_distance(q) == q.get_precedence_distance(p) assert p.get_adjacency_distance(q) == p.get_adjacency_distance(q) assert p.get_positional_distance(q) == p.get_positional_distance(q) p = Permutation([0, 1, 2, 3]) q = Permutation([3, 2, 1, 0]) assert p.get_precedence_distance(q) == 6 assert p.get_adjacency_distance(q) == 3 assert p.get_positional_distance(q) == 8 a = [Permutation.unrank_nonlex(4, i) for i in range(5)] for i in range(5): for j in range(i + 1, 5): assert a[i].commutes_with(a[j]) == (a[i] * a[j] == a[j] * a[i])
def test_Permutation(): p = Permutation([2, 5, 1, 6, 3, 0, 4]) q = Permutation([[1], [0, 3, 5, 6, 2, 4]]) assert Permutation(p.cyclic_form).array_form == p.array_form assert p.cardinality == 5040 assert q.cardinality == 5040 assert q.cycles == 2 assert q*p == Permutation([4, 6, 1, 2, 5, 3, 0]) assert p*q == Permutation([6, 5, 3, 0, 2, 4, 1]) assert perm_af_mul([2, 5, 1, 6, 3, 0, 4], [3, 1, 4, 5, 0, 6, 2]) == \ [6, 5, 3, 0, 2, 4, 1] assert cyclic([(2,3,5)], 5) == [[1, 2, 4], [0], [3]] assert (Permutation([[1,2,3],[0,4]])*Permutation([[1,2,4],[0],[3]])).cyclic_form == \ [[1, 3], [0, 4, 2]] assert q.array_form == [3, 1, 4, 5, 0, 6, 2] assert p.cyclic_form == [[3, 6, 4], [0, 2, 1, 5]] assert p.transpositions() == [(3, 4), (3, 6), (0, 5), (0, 1), (0, 2)] assert p**13 == p assert q**2 == Permutation([5, 1, 0, 6, 3, 2, 4]) assert p+q == Permutation([5, 6, 3, 1, 2, 4, 0]) assert q+p == p+q assert p-q == Permutation([6, 3, 5, 1, 2, 4, 0]) assert q-p == Permutation([1, 4, 2, 6, 5, 3, 0]) a = p-q b = q-p assert (a+b).is_Identity assert p.conjugate(q) == Permutation([5, 3, 0, 4, 6, 2, 1]) assert p.conjugate(q) == ~q*p*q == p**q assert q.conjugate(p) == Permutation([6, 3, 2, 0, 1, 4, 5]) assert q.conjugate(p) == ~p*q*p == q**p assert p.commutator(q) == Permutation([1, 4, 5, 6, 3, 0, 2]) assert q.commutator(p) == Permutation([5, 0, 6, 4, 1, 2, 3]) assert p.commutator(q) == ~ q.commutator(p) assert len(p.atoms()) == 7 assert q.atoms() == set([0, 1, 2, 3, 4, 5, 6]) assert p.inversion_vector() == [2, 4, 1, 3, 1, 0] assert q.inversion_vector() == [3, 1, 2, 2, 0, 1] assert Permutation.from_inversion_vector(p.inversion_vector()) == p assert Permutation.from_inversion_vector(q.inversion_vector()).array_form\ == q.array_form assert Permutation([i for i in range(500,-1,-1)]).inversions() == 125250 assert Permutation([0, 4, 1, 3, 2]).parity() == 0 assert Permutation([0, 1, 4, 3, 2]).parity() == 1 assert perm_af_parity([0, 4, 1, 3, 2]) == 0 assert perm_af_parity([0, 1, 4, 3, 2]) == 1 s = Permutation([0]) assert s.is_Singleton r = Permutation([3, 2, 1, 0]) assert (r**2).is_Identity assert (p*(~p)).is_Identity assert (~p)**13 == Permutation([5, 2, 0, 4, 6, 1, 3]) assert ~(r**2).is_Identity assert p.max() == 6 assert p.min() == 0 q = Permutation([[6], [5], [0, 1, 2, 3, 4]]) assert q.max() == 4 assert q.min() == 0 p = Permutation([1, 5, 2, 0, 3, 6, 4]) q = Permutation([[1, 2, 3, 5, 6], [0, 4]]) assert p.ascents() == [0, 3, 4] assert q.ascents() == [1, 2, 4] assert r.ascents() == [] assert p.descents() == [1, 2, 5] assert q.descents() == [0, 3, 5] assert Permutation(r.descents()).is_Identity assert p.inversions() == 7 assert p.signature() == -1 assert q.inversions() == 11 assert q.signature() == -1 assert (p*(~p)).inversions() == 0 assert (p*(~p)).signature() == 1 assert p.order() == 6 assert q.order() == 10 assert (p**(p.order())).is_Identity assert p.length() == 6 assert q.length() == 7 assert r.length() == 4 assert p.runs() == [[1, 5], [2], [0, 3, 6], [4]] assert q.runs() == [[4], [2, 3, 5], [0, 6], [1]] assert r.runs() == [[3], [2], [1], [0]] assert p.index() == 8 assert q.index() == 8 assert r.index() == 3 assert p.get_precedence_distance(q) == q.get_precedence_distance(p) assert p.get_adjacency_distance(q) == p.get_adjacency_distance(q) assert p.get_positional_distance(q) == p.get_positional_distance(q) p = Permutation([0, 1, 2, 3]) q = Permutation([3, 2, 1, 0]) assert p.get_precedence_distance(q) == 6 assert p.get_adjacency_distance(q) == 3 assert p.get_positional_distance(q) == 8 a = [Permutation.unrank_nonlex(4, i) for i in range(5)] iden = Permutation([0, 1, 2, 3]) for i in range(5): for j in range(i+1, 5): assert a[i].commutes_with(a[j]) == (a[i]*a[j] == a[j]*a[i]) if a[i].commutes_with(a[j]): assert a[i].commutator(a[j]) == iden assert a[j].commutator(a[i]) == iden