From 91f55c9ecb528846b9b91a5a52f62651536e1bc6 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sat, 8 Jun 2019 17:26:46 +0530 Subject: [PATCH 01/28] add-polycyclic-class --- sympy/combinatorics/pc_groups.py | 58 ++++++++++++++++++++++++++++++ sympy/combinatorics/perm_groups.py | 6 ++++ 2 files changed, 64 insertions(+) create mode 100644 sympy/combinatorics/pc_groups.py diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py new file mode 100644 index 000000000000..536333c4b458 --- /dev/null +++ b/sympy/combinatorics/pc_groups.py @@ -0,0 +1,58 @@ +from sympy.core import Basic +from sympy import sieve +from sympy.combinatorics.perm_groups import PermutationGroup + +class PcGroup(Basic): + + is_group = True + is_solvable = True + + def __init__(self, _pcgs): + self.perm_group = PermutationGroup(_pcgs) + self.pc_series = self._pc_series() + self.pcgs = self._compute_pcgs() + + def _pc_series(self): + return self.perm_group.composition_series() + + def _compute_pcgs(self): + # computes the generating sequence for polycyclic groups. + series = self.pc_series + pcgs = [] + for i in range(len(series)-1): + for g in series[i].generators: + if not g in series[i+1]: + pcgs.append(g) + return pcgs + + def relative_orders(self): + rel_orders = [] + for i in range(len(self.pc_series)-1): + G = self.pc_series[i] + H = self.pc_series[i+1] + rel_orders.append(G.order()//H.order()) + return rel_orders + + def is_prime_order(self): + for order in self.relative_orders(): + if order not in sieve: + return False + return True + + def length(self): + return len(self.pcgs) + + def pc_element_exponent(self, element): + series = self.pc_series + pcgs = self.pcgs + exponent = [0]*len(series) + for i in range(len(series)): + exp = 0 + if not element in series[i]: + for j in range(len(pcgs)): + element = (pcgs[j]**-1)*element + exp = exp + 1 + if element in series[i]: + exponent[i] = exp + break + return exponent diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py index f3f84344db0d..690ba3721723 100644 --- a/sympy/combinatorics/perm_groups.py +++ b/sympy/combinatorics/perm_groups.py @@ -4515,6 +4515,12 @@ def _factor_group_by_rels(G, rels): G._fp_presentation = simplify_presentation(G_p) return G._fp_presentation + def pc_group(self): + from sympy.combinatorics.pc_groups import PcGroup + if not self.is_polycyclic: + raise ValueError("The group must be solvable") + return PcGroup(self.generators) + def _orbit(degree, generators, alpha, action='tuples'): r"""Compute the orbit of alpha `\{g(\alpha) | g \in G\}` as a set. From 0a9b68f1d789259a25c6cd71c9f44dbcd54a9c28 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sat, 15 Jun 2019 23:11:44 +0530 Subject: [PATCH 02/28] collection-algorithm --- sympy/combinatorics/pc_groups.py | 181 +++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 536333c4b458..7aaceb45a9b8 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -1,5 +1,6 @@ from sympy.core import Basic from sympy import sieve +from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.perm_groups import PermutationGroup class PcGroup(Basic): @@ -56,3 +57,183 @@ def pc_element_exponent(self, element): exponent[i] = exp break return exponent + + +def minimal_uncollected_subwords(word, relative_order): + """ + Returns the minimal uncollected subwords. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import minimal_uncollected_subwords + >>> from sympy.combinatorics.free_groups import free_group + + Example 8.7 Pg. 281 from Handbook + >>> F, x1, x2 = free_group("x1, x2") + >>> word = x2**2*x1**7 + >>> relative_order = [3, 2] + >>> minimal_uncollected_subwords(word, relative_order)) + {x1*x2**2: 0, x1**7: 1} + + """ + array = word.array_form + uncollected_subwords = {} + for i in range(len(array)-1): + if array[i+1][1] > 0: + # case-1: v = x[i]**a*x[i+1] + uncollected_subwords[array[i][0]**array[i][1]*array[i+1][0]] = 0 + else: + # case-2: v = x[i]**a*x[i+1]*-1 + uncollected_subwords[array[i][0]**array[i][1]*array[i+1][0]**-1] = 0 + + if all(array[i][1]!=exp for exp in range(relative_order[i])): + # case-3: v = x[i]**a + uncollected_subwords[array[i][0]**array[i][1]] = 1 + + i = len(array)-1 + if all(array[i][1]!=exp for exp in range(relative_order[i])): + # case-3: v = x[i]**a + uncollected_subwords[array[i][0]**array[i][1]] = 1 + + return uncollected_subwords + +def _relations(relators): + """ + Separates the given relators of pc presentation in power and + conjugate relations. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import _relations + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x1, x2 = free_group("x1, x2") + >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> power_rel, conj_rel = _relations(pc_relators) + >>> power_rel + {x1**2: 1} + >>> conj_rel + {x1*x2*x1**-1: x2**-1, x1**-1*x2*x1: x2**-1} + + """ + power_relators = {} + conjugate_relators = {} + for key, value in relators.items(): + if len(key.array_form) == 1: + power_relators[key] = value + else: + conjugate_relators[key] = value + return power_relators, conjugate_relators + +def _reduce_word(word, power_relators): + """ + Reduce the given word with the help of power + relators only. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import _reduce_word + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x1, x2 = free_group("x1, x2") + >>> power_relators = {x1**2: 1} + >>> word = x1**7 + >>> _reduce_word(word, power_relators) + x1 + + """ + group = word.group + dividend = word.array_form[0][1] + rem = dividend + for w, v in power_relators.items(): + divisor = w.array_form[0][1] + if w.letter_form_elm[0] == word.letter_form_elm[0] and dividend % divisor < rem: + rem = dividend % divisor + quo = dividend // divisor + value = power_relators[w] + word = (value**quo)*(word.letter_form_elm[0]**rem) + return group.dtype(word) + +def _conjugate_word(word, conjugate_relators): + """ + Returns the reduced word with the help of + conjugate relators. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import _conjugate_word + >>> from sympy.combinatorics.free_groups import free_group + + Example 8.7 Pg. 282 from Handbook + >>> F, x1, x2 = free_group("x1, x2") + >>> word = x2**2*x1 + >>> conjugate_relators = {x1*x2*x1**-1: x2**-1, x1**-1*x2*x1: x2**-1} + >>> _conjugate_word(word, conjugate_relators) + x1*x2**-2 + + """ + exp = word.array_form[0][1] + w = (word[len(word)-1].inverse()*word.letter_form_elm[0]*word.subword(exp, len(word))) + if w in conjugate_relators: + word = word[len(word)-1]*(conjugate_relators[w]**exp) + else: + loop = word.array_form[len(word.array_form)-1][1] + if loop%2 == 0: + word = word[len(word)-1]**loop*word.subword(0, len(word)-loop) + else: + low, high = abs(word.array_form[0][1]), abs(word.array_form[1][1]) + high = high + low + print(word.subword(low, high)) + word = word.eliminate_word(word.subword(low, high), word.subword(low, high).inverse()) + word = word[len(word)-1]**loop*word.subword(0, len(word)-loop) + return word + +def _index(word, w): + """ + Returns the start and ending index of a given + subword in a word. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import _index + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x1, x2 = free_group("x1, x2") + >>> word = x2**2*x1**7 + >>> w = x2**2*x1 + >>> _index(word, w) + (0, 3) + >>> w = x1**7 + >>> _index(word, w) + (2, 9) + + """ + low = -1 + high = -1 + for i in range(len(word)-len(w)+1): + if word.subword(i, i+len(w)) == w: + low = i + high = i+len(w) + break + if low == high == -1: + raise ValueError("Given word is not a subword") + return low, high + +def collected_word(pc_relators, word, relative_order): + """ + + """ + group = word.group + uncollected_subwords = minimal_uncollected_subwords(word, relative_order) + power_relators, conjugate_relators = _relations(pc_relators) + for w, case in uncollected_subwords.items(): + if case == 1: + _word = _reduce_word(w, power_relators) + word = word.eliminate_word(w, _word) + word = _conjugate_word(word, conjugate_relators) + + else: + low, high = _index(word, w) + w = _conjugate_word(w, conjugate_relators) + word = word.substituted_word(low, high, w) + word = _conjugate_word(word, conjugate_relators) + _word = word.subword(0, word.array_form[0][1]) + word = word.eliminate_word( _word, _reduce_word(_word, power_relators)) + return word From 1ea305535758171d39bd246cc1a6c9ab7594121a Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sun, 16 Jun 2019 07:44:50 +0530 Subject: [PATCH 03/28] add-test-in-core/test_args --- sympy/combinatorics/pc_groups.py | 12 ++++++------ sympy/combinatorics/perm_groups.py | 6 +++--- sympy/core/tests/test_args.py | 7 +++++++ 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 7aaceb45a9b8..4dcafe856383 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -3,13 +3,13 @@ from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.perm_groups import PermutationGroup -class PcGroup(Basic): +class PolycyclicGroup(Basic): is_group = True is_solvable = True - def __init__(self, _pcgs): - self.perm_group = PermutationGroup(_pcgs) + def __init__(self, pcgs_): + self.perm_group = PermutationGroup(pcgs_) self.pc_series = self._pc_series() self.pcgs = self._compute_pcgs() @@ -72,8 +72,8 @@ def minimal_uncollected_subwords(word, relative_order): >>> F, x1, x2 = free_group("x1, x2") >>> word = x2**2*x1**7 >>> relative_order = [3, 2] - >>> minimal_uncollected_subwords(word, relative_order)) - {x1*x2**2: 0, x1**7: 1} + >>> minimal_uncollected_subwords(word, relative_order) + {x1**7: 1, x1*x2**2: 0} """ array = word.array_form @@ -112,7 +112,7 @@ def _relations(relators): >>> power_rel {x1**2: 1} >>> conj_rel - {x1*x2*x1**-1: x2**-1, x1**-1*x2*x1: x2**-1} + {x1**-1*x2*x1: x2**-1, x1*x2*x1**-1: x2**-1} """ power_relators = {} diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py index 690ba3721723..6fec6dcc5c5d 100644 --- a/sympy/combinatorics/perm_groups.py +++ b/sympy/combinatorics/perm_groups.py @@ -4515,11 +4515,11 @@ def _factor_group_by_rels(G, rels): G._fp_presentation = simplify_presentation(G_p) return G._fp_presentation - def pc_group(self): - from sympy.combinatorics.pc_groups import PcGroup + def polycyclic_group(self): + from sympy.combinatorics.pc_groups import PolycyclicGroup if not self.is_polycyclic: raise ValueError("The group must be solvable") - return PcGroup(self.generators) + return PolycyclicGroup(self.generators) def _orbit(degree, generators, alpha, action='tuples'): diff --git a/sympy/core/tests/test_args.py b/sympy/core/tests/test_args.py index 9d286093b11e..a2375e161483 100644 --- a/sympy/core/tests/test_args.py +++ b/sympy/core/tests/test_args.py @@ -487,6 +487,13 @@ def test_sympy__combinatorics__perm_groups__PermutationGroup(): assert _test_args(PermutationGroup([Permutation([0, 1])])) +@XFAIL +def test_sympy__combinatorics__pc_groups__PolycyclicGroup(): + from sympy.combinatorics.permutations import Permutation + from sympy.combinatorics.pc_groups import PolycyclicGroup + assert _test_args(PolycyclicGroup([Permutation(1, 2, 3)])) + + def test_sympy__combinatorics__polyhedron__Polyhedron(): from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.polyhedron import Polyhedron From be62f04d181f8d37c5de13599d699e476d45ed00 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Thu, 20 Jun 2019 07:46:14 +0530 Subject: [PATCH 04/28] Add-collector-class --- sympy/combinatorics/pc_groups.py | 370 +++++++++++++++++-------------- 1 file changed, 205 insertions(+), 165 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 4dcafe856383..3a21c32bce3d 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -1,7 +1,7 @@ from sympy.core import Basic from sympy import sieve -from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.perm_groups import PermutationGroup +from sympy.printing.defaults import DefaultPrinting class PolycyclicGroup(Basic): @@ -59,181 +59,221 @@ def pc_element_exponent(self, element): return exponent -def minimal_uncollected_subwords(word, relative_order): - """ - Returns the minimal uncollected subwords. - - Examples - ======== - >>> from sympy.combinatorics.pc_groups import minimal_uncollected_subwords - >>> from sympy.combinatorics.free_groups import free_group - - Example 8.7 Pg. 281 from Handbook - >>> F, x1, x2 = free_group("x1, x2") - >>> word = x2**2*x1**7 - >>> relative_order = [3, 2] - >>> minimal_uncollected_subwords(word, relative_order) - {x1**7: 1, x1*x2**2: 0} - - """ - array = word.array_form - uncollected_subwords = {} - for i in range(len(array)-1): - if array[i+1][1] > 0: - # case-1: v = x[i]**a*x[i+1] - uncollected_subwords[array[i][0]**array[i][1]*array[i+1][0]] = 0 - else: - # case-2: v = x[i]**a*x[i+1]*-1 - uncollected_subwords[array[i][0]**array[i][1]*array[i+1][0]**-1] = 0 - - if all(array[i][1]!=exp for exp in range(relative_order[i])): +class Collector(DefaultPrinting): + + def __init__(self, pc_relators, word): + self.pc_relators = pc_relators + self.word = word + self.group = word.group + + def minimal_uncollected_subwords(self): + """ + Returns the minimal uncollected subwords. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.free_groups import free_group + + Example 8.7 Pg. 281 from Handbook + >>> F, x1, x2 = free_group("x1, x2") + >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> word = x2**2*x1**7 + >>> collector = Collector(pc_relators, word) + >>> collector.minimal_uncollected_subwords() + {((x1, 7),): 1, ((x2, 2), (x1, 1)): 0} + + """ + group = self.word.group + gens = group.symbols + index = {} + i = 1 + for g in gens: + index[g] = i + i += 1 + array = self.word.array_form + uncollected_subwords = {} + for i in range(len(array)-1): + if array[i+1][1] > 0 and index[array[i][0]] > index[array[i+1][0]]: + # case-1: v = x[i]**a*x[i+1] + uncollected_subwords[((array[i][0], array[i][1]), (array[i+1][0], 1))] = 0 + + elif array[i+1][1] < 0 and index[array[i][0]] > index[array[i+1][0]]: + # case-2: v = x[i]**a*x[i+1]*-1 + uncollected_subwords[((array[i][0], array[i][1]), (array[i+1][0], -1))] = 0 + + if array[i][1] < 0 or array[i][1] > index[array[i][0]]: + # case-3: v = x[i]**a + uncollected_subwords[((array[i][0], array[1][1]), )] = 1 + + i = len(array)-1 + if array[i][1] < 0 or array[i][1] > index[array[i][0]]: # case-3: v = x[i]**a - uncollected_subwords[array[i][0]**array[i][1]] = 1 - - i = len(array)-1 - if all(array[i][1]!=exp for exp in range(relative_order[i])): - # case-3: v = x[i]**a - uncollected_subwords[array[i][0]**array[i][1]] = 1 - - return uncollected_subwords - -def _relations(relators): - """ - Separates the given relators of pc presentation in power and - conjugate relations. - - Examples - ======== - >>> from sympy.combinatorics.pc_groups import _relations - >>> from sympy.combinatorics.free_groups import free_group - >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> power_rel, conj_rel = _relations(pc_relators) - >>> power_rel - {x1**2: 1} - >>> conj_rel - {x1**-1*x2*x1: x2**-1, x1*x2*x1**-1: x2**-1} - - """ - power_relators = {} - conjugate_relators = {} - for key, value in relators.items(): - if len(key.array_form) == 1: - power_relators[key] = value - else: - conjugate_relators[key] = value - return power_relators, conjugate_relators - -def _reduce_word(word, power_relators): - """ - Reduce the given word with the help of power - relators only. - - Examples - ======== - >>> from sympy.combinatorics.pc_groups import _reduce_word - >>> from sympy.combinatorics.free_groups import free_group - >>> F, x1, x2 = free_group("x1, x2") - >>> power_relators = {x1**2: 1} - >>> word = x1**7 - >>> _reduce_word(word, power_relators) - x1 - - """ - group = word.group - dividend = word.array_form[0][1] - rem = dividend - for w, v in power_relators.items(): - divisor = w.array_form[0][1] - if w.letter_form_elm[0] == word.letter_form_elm[0] and dividend % divisor < rem: - rem = dividend % divisor - quo = dividend // divisor - value = power_relators[w] - word = (value**quo)*(word.letter_form_elm[0]**rem) - return group.dtype(word) - -def _conjugate_word(word, conjugate_relators): - """ - Returns the reduced word with the help of - conjugate relators. - - Examples - ======== - >>> from sympy.combinatorics.pc_groups import _conjugate_word - >>> from sympy.combinatorics.free_groups import free_group - - Example 8.7 Pg. 282 from Handbook - >>> F, x1, x2 = free_group("x1, x2") - >>> word = x2**2*x1 - >>> conjugate_relators = {x1*x2*x1**-1: x2**-1, x1**-1*x2*x1: x2**-1} - >>> _conjugate_word(word, conjugate_relators) - x1*x2**-2 - - """ - exp = word.array_form[0][1] - w = (word[len(word)-1].inverse()*word.letter_form_elm[0]*word.subword(exp, len(word))) - if w in conjugate_relators: - word = word[len(word)-1]*(conjugate_relators[w]**exp) - else: - loop = word.array_form[len(word.array_form)-1][1] - if loop%2 == 0: - word = word[len(word)-1]**loop*word.subword(0, len(word)-loop) + uncollected_subwords[((array[i][0], array[1][1]), )] = 1 + + return uncollected_subwords + + def relations(self): + """ + Separates the given relators of pc presentation in power and + conjugate relations. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x1, x2 = free_group("x1, x2") + >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> word = x2**2*x1**7 + >>> collector = Collector(pc_relators, word) + >>> power_rel, conj_rel = collector.relations() + >>> power_rel + {x1**2: 1} + >>> conj_rel + {x1**-1*x2*x1: x2**-1, x1*x2*x1**-1: x2**-1} + + """ + power_relators = {} + conjugate_relators = {} + for key, value in self.pc_relators.items(): + if len(key.array_form) == 1: + power_relators[key] = value + else: + conjugate_relators[key] = value + return power_relators, conjugate_relators + + def reduce_word(self): + """ + Reduce the given word with the help of power + relators only. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x1, x2 = free_group("x1, x2") + >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> word = x1**7 + >>> collector = Collector(pc_relators, word) + >>> collector.reduce_word() + x1 + + """ + group = self.word.group + power_relators = self.relations()[0] + dividend = self.word.array_form[0][1] + rem = dividend + for w, v in power_relators.items(): + divisor = w.array_form[0][1] + if w.letter_form_elm[0] == self.word.letter_form_elm[0] and dividend % divisor < rem: + rem = dividend % divisor + quo = dividend // divisor + value = power_relators[w] + reduced_word = (value**quo)*(self.word.letter_form_elm[0]**rem) + return group.dtype(reduced_word) + return None + + def conjugate_word(self): + """ + Returns the reduced word with the help of + conjugate relators. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.free_groups import free_group + + Example 8.7 Pg. 282 from Handbook + >>> F, x1, x2 = free_group("x1, x2") + >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> word = x2**2*x1 + >>> collector = Collector(pc_relators, word) + >>> collector.conjugate_word() + x1*x2**-2 + + """ + conjugate_relators = self.relations()[1] + exp = self.word.array_form[0][1] + w = (self.word[len(self.word)-1].inverse()*self.word.letter_form_elm[0]*self.word.subword(exp, len(self.word))) + if w in conjugate_relators: + conj_word = self.word[len(self.word)-1]*(conjugate_relators[w]**exp) else: - low, high = abs(word.array_form[0][1]), abs(word.array_form[1][1]) - high = high + low - print(word.subword(low, high)) - word = word.eliminate_word(word.subword(low, high), word.subword(low, high).inverse()) - word = word[len(word)-1]**loop*word.subword(0, len(word)-loop) - return word - -def _index(word, w): + loop = self.word.array_form[len(self.word.array_form)-1][1] + if loop%2 == 0: + conj_word = self.word[len(self.word)-1]**loop*self.word.subword(0, len(self.word)-loop) + else: + low, high = abs(self.word.array_form[0][1]), abs(self.word.array_form[1][1]) + high = high + low + conj_word = self.word.eliminate_word(self.word.subword(low, high), self.word.subword(low, high).inverse()) + word = conj_word[len(conj_word)-1]**loop*conj_word.subword(0, len(conj_word)-loop) + return conj_word + + def _index(self, w): + """ + Returns the start and ending index of a given + subword in a word. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x1, x2 = free_group("x1, x2") + >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> word = x2**2*x1**7 + >>> collector = Collector(pc_relators, word) + >>> w = x2**2*x1 + >>> collector._index(w) + (0, 3) + >>> w = x1**7 + >>> collector._index(w) + (2, 9) + + """ + low = -1 + high = -1 + for i in range(len(self.word)-len(w)+1): + if self.word.subword(i, i+len(w)) == w: + low = i + high = i+len(w) + break + if low == high == -1: + raise ValueError("Given word is not a subword") + return low, high + +def collected_word(pc_relators, word): """ - Returns the start and ending index of a given - subword in a word. - Examples ======== - >>> from sympy.combinatorics.pc_groups import _index + >>> from sympy.combinatorics.pc_groups import collected_word + >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group >>> F, x1, x2 = free_group("x1, x2") + >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> word = x2**2*x1**7 - >>> w = x2**2*x1 - >>> _index(word, w) - (0, 3) - >>> w = x1**7 - >>> _index(word, w) - (2, 9) + >>> collector = Collector(pc_relators, word) + >>> collected_word(pc_relators, word) + x1*x2**-2 """ - low = -1 - high = -1 - for i in range(len(word)-len(w)+1): - if word.subword(i, i+len(w)) == w: - low = i - high = i+len(w) - break - if low == high == -1: - raise ValueError("Given word is not a subword") - return low, high - -def collected_word(pc_relators, word, relative_order): - """ + collector = Collector(pc_relators, word) + group = collector.word.group + uncollected_subwords = collector.minimal_uncollected_subwords() + power_relators, conjugate_relators = collector.relations() - """ - group = word.group - uncollected_subwords = minimal_uncollected_subwords(word, relative_order) - power_relators, conjugate_relators = _relations(pc_relators) for w, case in uncollected_subwords.items(): + w = group.dtype(w) if case == 1: - _word = _reduce_word(w, power_relators) - word = word.eliminate_word(w, _word) - word = _conjugate_word(word, conjugate_relators) - + _word = collector.reduce_word() + collector.word = collector.word.eliminate_word(w, _word) + if collector.word.array_form[len(collector.word.array_form)-1][1] > 0: + collector.word = collector.conjugate_word() else: - low, high = _index(word, w) - w = _conjugate_word(w, conjugate_relators) - word = word.substituted_word(low, high, w) - word = _conjugate_word(word, conjugate_relators) - _word = word.subword(0, word.array_form[0][1]) - word = word.eliminate_word( _word, _reduce_word(_word, power_relators)) - return word + low, high = collector._index(w) + collector.word = w + w = collector.conjugate_word() + collector.word = word + collector.word = collector.word.substituted_word(low, high, w) + collector.word = collector.conjugate_word() + _word = collector.word.subword(0, collector.word.array_form[0][1]) + collector.word = collector.word.eliminate_word( _word, collector.reduce_word()) + return collector.word From 1f6232ee0314f1cfc7af8a8d435cef9d91b82f01 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Thu, 20 Jun 2019 09:04:18 +0530 Subject: [PATCH 05/28] include-references --- sympy/combinatorics/pc_groups.py | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 3a21c32bce3d..0f09a8027986 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -61,6 +61,15 @@ def pc_element_exponent(self, element): class Collector(DefaultPrinting): + """ + References + ========== + + .. [1] Holt, D., Eick, B., O'Brien, E. + "Handbook of Computational Group Theory" + Section 8.1.3 + """ + def __init__(self, pc_relators, word): self.pc_relators = pc_relators self.word = word @@ -75,7 +84,7 @@ def minimal_uncollected_subwords(self): >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group - Example 8.7 Pg. 281 from Handbook + Example 8.7 Pg. 281 from [1] >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> word = x2**2*x1**7 @@ -95,11 +104,11 @@ def minimal_uncollected_subwords(self): uncollected_subwords = {} for i in range(len(array)-1): if array[i+1][1] > 0 and index[array[i][0]] > index[array[i+1][0]]: - # case-1: v = x[i]**a*x[i+1] + # case-1: v = x[i]**a*x[i+1], where index[x[i]] > index[x[i+1]] uncollected_subwords[((array[i][0], array[i][1]), (array[i+1][0], 1))] = 0 elif array[i+1][1] < 0 and index[array[i][0]] > index[array[i+1][0]]: - # case-2: v = x[i]**a*x[i+1]*-1 + # case-2: v = x[i]**a*x[i+1]*-1, where index[x[i]] > index[x[i+1]] uncollected_subwords[((array[i][0], array[i][1]), (array[i+1][0], -1))] = 0 if array[i][1] < 0 or array[i][1] > index[array[i][0]]: @@ -183,7 +192,7 @@ def conjugate_word(self): >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group - Example 8.7 Pg. 282 from Handbook + Example 8.7 Pg. 282 from [1] >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> word = x2**2*x1 @@ -208,7 +217,7 @@ def conjugate_word(self): word = conj_word[len(conj_word)-1]**loop*conj_word.subword(0, len(conj_word)-loop) return conj_word - def _index(self, w): + def index(self, w): """ Returns the start and ending index of a given subword in a word. @@ -222,10 +231,10 @@ def _index(self, w): >>> word = x2**2*x1**7 >>> collector = Collector(pc_relators, word) >>> w = x2**2*x1 - >>> collector._index(w) + >>> collector.index(w) (0, 3) >>> w = x1**7 - >>> collector._index(w) + >>> collector.index(w) (2, 9) """ @@ -268,7 +277,7 @@ def collected_word(pc_relators, word): if collector.word.array_form[len(collector.word.array_form)-1][1] > 0: collector.word = collector.conjugate_word() else: - low, high = collector._index(w) + low, high = collector.index(w) collector.word = w w = collector.conjugate_word() collector.word = word From f02f9c7ebc7ebdac9a7a12f28441b24cab9029db Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Thu, 27 Jun 2019 09:12:32 +0530 Subject: [PATCH 06/28] test_and_correct_collector --- sympy/combinatorics/__init__.py | 1 + sympy/combinatorics/pc_groups.py | 235 ++++++++++---------- sympy/combinatorics/tests/test_pc_groups.py | 29 +++ 3 files changed, 145 insertions(+), 120 deletions(-) create mode 100644 sympy/combinatorics/tests/test_pc_groups.py diff --git a/sympy/combinatorics/__init__.py b/sympy/combinatorics/__init__.py index dae9c62de88d..c620fb1b38fa 100644 --- a/sympy/combinatorics/__init__.py +++ b/sympy/combinatorics/__init__.py @@ -11,3 +11,4 @@ from sympy.combinatorics.graycode import GrayCode from sympy.combinatorics.named_groups import (SymmetricGroup, DihedralGroup, CyclicGroup, AlternatingGroup, AbelianGroup, RubikGroup) +from sympy.combinatorics.pc_groups import PolycyclicGroup, Collector diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 0f09a8027986..df7c0439ab75 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -70,10 +70,12 @@ class Collector(DefaultPrinting): Section 8.1.3 """ - def __init__(self, pc_relators, word): + def __init__(self, pc_relators, word, relative_order): self.pc_relators = pc_relators self.word = word self.group = word.group + self.relative_order = relative_order + self.index = {s: i+1 for i, s in enumerate(self.group.symbols)} def minimal_uncollected_subwords(self): """ @@ -86,39 +88,43 @@ def minimal_uncollected_subwords(self): Example 8.7 Pg. 281 from [1] >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> word = x2**2*x1**7 - >>> collector = Collector(pc_relators, word) + >>> relative_order = {x1: 2, x2: 3} + >>> collector = Collector(pc_relators, word, relative_order) >>> collector.minimal_uncollected_subwords() - {((x1, 7),): 1, ((x2, 2), (x1, 1)): 0} + {((x1, 7),): 2, ((x2, 2), (x1, 1)): 0} """ - group = self.word.group - gens = group.symbols - index = {} - i = 1 - for g in gens: - index[g] = i - i += 1 + group = self.group + index = self.index array = self.word.array_form + re = self.relative_order uncollected_subwords = {} - for i in range(len(array)-1): - if array[i+1][1] > 0 and index[array[i][0]] > index[array[i+1][0]]: - # case-1: v = x[i]**a*x[i+1], where index[x[i]] > index[x[i+1]] - uncollected_subwords[((array[i][0], array[i][1]), (array[i+1][0], 1))] = 0 - - elif array[i+1][1] < 0 and index[array[i][0]] > index[array[i+1][0]]: - # case-2: v = x[i]**a*x[i+1]*-1, where index[x[i]] > index[x[i+1]] - uncollected_subwords[((array[i][0], array[i][1]), (array[i+1][0], -1))] = 0 - if array[i][1] < 0 or array[i][1] > index[array[i][0]]: - # case-3: v = x[i]**a - uncollected_subwords[((array[i][0], array[1][1]), )] = 1 + for i in range(len(array)-1): + s1, e1 = array[i] + s2, e2 = array[i+1] + if e2 > 0 and index[s1] > index[s2]: + # case-0: v = x[i]**a*x[i+1], where index[x[i]] > index[x[i+1]] + uncollected_subwords[((s1, e1), (s2, 1))] = 0 + + elif e2 < 0 and index[s1] > index[s2]: + # case-1: v = x[i]**a*x[i+1]*-1, where index[x[i]] > index[x[i+1]] + uncollected_subwords[((s1, e1), (s2, -1))] = 1 + s = ((s1, 1), ) + s = group.dtype(s) + if e1 > re[s]-1: + # case-2: v = x[i]**a + uncollected_subwords[((s1, e1), )] = 2 i = len(array)-1 - if array[i][1] < 0 or array[i][1] > index[array[i][0]]: - # case-3: v = x[i]**a - uncollected_subwords[((array[i][0], array[1][1]), )] = 1 + s1, e1 = array[i] + s = ((s1, 1), ) + s = group.dtype(s) + if e1 > re[s]-1: + # case-2: v = x[i]**a + uncollected_subwords[((s1, e1), )] = 2 return uncollected_subwords @@ -134,7 +140,8 @@ def relations(self): >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> word = x2**2*x1**7 - >>> collector = Collector(pc_relators, word) + >>> relative_order = {x1: 2, x2: 3} + >>> collector = Collector(pc_relators, word, relative_order) >>> power_rel, conj_rel = collector.relations() >>> power_rel {x1**2: 1} @@ -151,73 +158,7 @@ def relations(self): conjugate_relators[key] = value return power_relators, conjugate_relators - def reduce_word(self): - """ - Reduce the given word with the help of power - relators only. - - Examples - ======== - >>> from sympy.combinatorics.pc_groups import Collector - >>> from sympy.combinatorics.free_groups import free_group - >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> word = x1**7 - >>> collector = Collector(pc_relators, word) - >>> collector.reduce_word() - x1 - - """ - group = self.word.group - power_relators = self.relations()[0] - dividend = self.word.array_form[0][1] - rem = dividend - for w, v in power_relators.items(): - divisor = w.array_form[0][1] - if w.letter_form_elm[0] == self.word.letter_form_elm[0] and dividend % divisor < rem: - rem = dividend % divisor - quo = dividend // divisor - value = power_relators[w] - reduced_word = (value**quo)*(self.word.letter_form_elm[0]**rem) - return group.dtype(reduced_word) - return None - - def conjugate_word(self): - """ - Returns the reduced word with the help of - conjugate relators. - - Examples - ======== - >>> from sympy.combinatorics.pc_groups import Collector - >>> from sympy.combinatorics.free_groups import free_group - - Example 8.7 Pg. 282 from [1] - >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> word = x2**2*x1 - >>> collector = Collector(pc_relators, word) - >>> collector.conjugate_word() - x1*x2**-2 - - """ - conjugate_relators = self.relations()[1] - exp = self.word.array_form[0][1] - w = (self.word[len(self.word)-1].inverse()*self.word.letter_form_elm[0]*self.word.subword(exp, len(self.word))) - if w in conjugate_relators: - conj_word = self.word[len(self.word)-1]*(conjugate_relators[w]**exp) - else: - loop = self.word.array_form[len(self.word.array_form)-1][1] - if loop%2 == 0: - conj_word = self.word[len(self.word)-1]**loop*self.word.subword(0, len(self.word)-loop) - else: - low, high = abs(self.word.array_form[0][1]), abs(self.word.array_form[1][1]) - high = high + low - conj_word = self.word.eliminate_word(self.word.subword(low, high), self.word.subword(low, high).inverse()) - word = conj_word[len(conj_word)-1]**loop*conj_word.subword(0, len(conj_word)-loop) - return conj_word - - def index(self, w): + def subword_index(self, w): """ Returns the start and ending index of a given subword in a word. @@ -229,12 +170,13 @@ def index(self, w): >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> word = x2**2*x1**7 - >>> collector = Collector(pc_relators, word) + >>> relative_order = {x1: 2, x2: 3} + >>> collector = Collector(pc_relators, word, relative_order) >>> w = x2**2*x1 - >>> collector.index(w) + >>> collector.subword_index(w) (0, 3) >>> w = x1**7 - >>> collector.index(w) + >>> collector.subword_index(w) (2, 9) """ @@ -246,10 +188,35 @@ def index(self, w): high = i+len(w) break if low == high == -1: - raise ValueError("Given word is not a subword") + return -1, -1 return low, high -def collected_word(pc_relators, word): + def map_relation(self, w): + """ + Examples + ======== + >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x0, x1, x2 = free_group("x0, x1, x2") + >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1:x2, x0**-1*x2*x0:x2*x1} + >>> relative_order = {x0: 2, x1: 2, x2: 3} + >>> word = x2*x1*x0 + >>> collector = Collector(pc_relators, word, relative_order) + >>> w = x2*x1 + >>> collector.map_relation(w) + x2 + >>> w = x1*x0 + >>> collector.map_relation(w) + x1**2 + + """ + group = w.group + gens = list(sorted(w.contains_generators())) + key = gens[0]**-1*gens[1]*gens[0] + return self.pc_relators[key] + + +def collected_word(pc_relators, word, relative_order): """ Examples ======== @@ -257,32 +224,60 @@ def collected_word(pc_relators, word): >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> word = x2**2*x1**7 - >>> collector = Collector(pc_relators, word) - >>> collected_word(pc_relators, word) + >>> relative_order = {x1: 2, x2: 3} + >>> collected_word(pc_relators, word, relative_order) x1*x2**-2 """ - collector = Collector(pc_relators, word) - group = collector.word.group - uncollected_subwords = collector.minimal_uncollected_subwords() + collector = Collector(pc_relators, word, relative_order) power_relators, conjugate_relators = collector.relations() + group = collector.group + flag = 0 + while True: + uncollected_subwords = collector.minimal_uncollected_subwords() + if not uncollected_subwords: + break + for w, case in uncollected_subwords.items(): + w = group.dtype(w) + low, high = collector.subword_index(w) + if low == -1: + continue + if case == 0: + gens = list(sorted(w.contains_generators())) + array = w.array_form + s, e = array[0] + word_ = collector.map_relation(w) + word_ = gens[0]*word_**e + word_ = group.dtype(word_) + collector.word = collector.word.substituted_word(low, high, word_) + + elif case == 1: + gens = list(sorted(w.contains_generators())) + array = w.array_form + s, e = array[0] + word_ = collector.map_relation(w) + word_ = (gens[0]**-1)*word_**e + word_ = group.dtype(word_) + collector.word = collector.word.substituted_word(low, high, word_) - for w, case in uncollected_subwords.items(): - w = group.dtype(w) - if case == 1: - _word = collector.reduce_word() - collector.word = collector.word.eliminate_word(w, _word) - if collector.word.array_form[len(collector.word.array_form)-1][1] > 0: - collector.word = collector.conjugate_word() - else: - low, high = collector.index(w) - collector.word = w - w = collector.conjugate_word() - collector.word = word - collector.word = collector.word.substituted_word(low, high, w) - collector.word = collector.conjugate_word() - _word = collector.word.subword(0, collector.word.array_form[0][1]) - collector.word = collector.word.eliminate_word( _word, collector.reduce_word()) + else: + array = w.array_form + s, e = array[0] + s1 = ((s, 1), ) + s = group.dtype(s1) + re = relative_order[s] + q = e//re + r = e-q*re + key = w[0]**re + if pc_relators[key]: + word_ = ((w[0], r), (pc_relators[key], re)) + word_ = group.dtype(word_) + else: + if r != 0: + word_ = w[0]**r + else: + word_ = None + collector.word = collector.word.eliminate_word(w, word_) return collector.word diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py new file mode 100644 index 000000000000..cb058bf68729 --- /dev/null +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -0,0 +1,29 @@ +from sympy.combinatorics.pc_groups import PolycyclicGroup, Collector, collected_word +from sympy.combinatorics.permutations import Permutation +from sympy.combinatorics.free_groups import free_group + +def test_collected_word(): + F, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") + + # Polycyclic relators for SymmetricGroup(4) + pc_relators = { x0**2: (), x1**3: (), x2**2: (), x3**2: (), + x0**-1*x1*x0: x1**2, x0**-1*x2*x0: x2*x3, + x0**-1*x3*x0: x3, x1**-1*x2*x1: x3, + x1**-1*x3*x1: x2*x3, x2**-1*x3*x2: x3 + } + + word = x3*x2*x1*x0 + relative_order = {x0: 2, x1: 3, x2: 2, x3: 2} + collected_word_ = collected_word(pc_relators, word, relative_order) + + assert collected_word_ == x0*x1**2*x2*x3 + + # Polycyclic Generators of SymmetricGroup(4) + x0 = Permutation(0, 1) + x1 = Permutation(0, 1, 2) + x2 = Permutation(0, 2)(1, 3) + x3 = Permutation(0, 1)(2, 3) + + word = x3*x2*x1*x0 + collected_word_ = x0*x1**2*x2*x3 + assert word == collected_word_ From 5f71423c058adbb0a996e2292f0365ed66ca9c1f Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Fri, 28 Jun 2019 09:33:44 +0530 Subject: [PATCH 07/28] make-collected_word-a-method-to-collector --- sympy/combinatorics/pc_groups.py | 103 +++++++++----------- sympy/combinatorics/tests/test_pc_groups.py | 5 +- 2 files changed, 51 insertions(+), 57 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index df7c0439ab75..6942647d9f52 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -70,14 +70,11 @@ class Collector(DefaultPrinting): Section 8.1.3 """ - def __init__(self, pc_relators, word, relative_order): + def __init__(self, pc_relators, relative_order): self.pc_relators = pc_relators - self.word = word - self.group = word.group self.relative_order = relative_order - self.index = {s: i+1 for i, s in enumerate(self.group.symbols)} - def minimal_uncollected_subwords(self): + def minimal_uncollected_subwords(self, word): """ Returns the minimal uncollected subwords. @@ -89,16 +86,16 @@ def minimal_uncollected_subwords(self): Example 8.7 Pg. 281 from [1] >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> word = x2**2*x1**7 >>> relative_order = {x1: 2, x2: 3} - >>> collector = Collector(pc_relators, word, relative_order) - >>> collector.minimal_uncollected_subwords() + >>> collector = Collector(pc_relators, relative_order) + >>> word = x2**2*x1**7 + >>> collector.minimal_uncollected_subwords(word) {((x1, 7),): 2, ((x2, 2), (x1, 1)): 0} """ - group = self.group - index = self.index - array = self.word.array_form + group = word.group + index = {s: i+1 for i, s in enumerate(group.symbols)} + array = word.array_form re = self.relative_order uncollected_subwords = {} @@ -139,9 +136,8 @@ def relations(self): >>> from sympy.combinatorics.free_groups import free_group >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> word = x2**2*x1**7 >>> relative_order = {x1: 2, x2: 3} - >>> collector = Collector(pc_relators, word, relative_order) + >>> collector = Collector(pc_relators, relative_order) >>> power_rel, conj_rel = collector.relations() >>> power_rel {x1**2: 1} @@ -158,7 +154,7 @@ def relations(self): conjugate_relators[key] = value return power_relators, conjugate_relators - def subword_index(self, w): + def subword_index(self, word, w): """ Returns the start and ending index of a given subword in a word. @@ -169,21 +165,21 @@ def subword_index(self, w): >>> from sympy.combinatorics.free_groups import free_group >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> word = x2**2*x1**7 >>> relative_order = {x1: 2, x2: 3} - >>> collector = Collector(pc_relators, word, relative_order) + >>> collector = Collector(pc_relators, relative_order) + >>> word = x2**2*x1**7 >>> w = x2**2*x1 - >>> collector.subword_index(w) + >>> collector.subword_index(word, w) (0, 3) >>> w = x1**7 - >>> collector.subword_index(w) + >>> collector.subword_index(word, w) (2, 9) """ low = -1 high = -1 - for i in range(len(self.word)-len(w)+1): - if self.word.subword(i, i+len(w)) == w: + for i in range(len(word)-len(w)+1): + if word.subword(i, i+len(w)) == w: low = i high = i+len(w) break @@ -200,8 +196,7 @@ def map_relation(self, w): >>> F, x0, x1, x2 = free_group("x0, x1, x2") >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1:x2, x0**-1*x2*x0:x2*x1} >>> relative_order = {x0: 2, x1: 2, x2: 3} - >>> word = x2*x1*x0 - >>> collector = Collector(pc_relators, word, relative_order) + >>> collector = Collector(pc_relators, relative_order) >>> w = x2*x1 >>> collector.map_relation(w) x2 @@ -216,68 +211,66 @@ def map_relation(self, w): return self.pc_relators[key] -def collected_word(pc_relators, word, relative_order): - """ - Examples - ======== - >>> from sympy.combinatorics.pc_groups import collected_word - >>> from sympy.combinatorics.pc_groups import Collector - >>> from sympy.combinatorics.free_groups import free_group - >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> word = x2**2*x1**7 - >>> relative_order = {x1: 2, x2: 3} - >>> collected_word(pc_relators, word, relative_order) - x1*x2**-2 + def collected_word(self, word): + """ + Examples + ======== + >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x1, x2 = free_group("x1, x2") + >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} + >>> relative_order = {x1: 2, x2: 3} + >>> collector = Collector(pc_relators, relative_order) + >>> word = x2**2*x1**7 + >>> collector.collected_word(word) + x1*x2**-2 - """ - collector = Collector(pc_relators, word, relative_order) - power_relators, conjugate_relators = collector.relations() - group = collector.group - flag = 0 - while True: - uncollected_subwords = collector.minimal_uncollected_subwords() - if not uncollected_subwords: - break - for w, case in uncollected_subwords.items(): + """ + group = word.group + while True: + uncollected_subwords = self.minimal_uncollected_subwords(word) + if not uncollected_subwords: + break + w = list(uncollected_subwords)[0] + case = uncollected_subwords[w] w = group.dtype(w) - low, high = collector.subword_index(w) + low, high = self.subword_index(word, w) if low == -1: continue if case == 0: gens = list(sorted(w.contains_generators())) array = w.array_form s, e = array[0] - word_ = collector.map_relation(w) + word_ = self.map_relation(w) word_ = gens[0]*word_**e word_ = group.dtype(word_) - collector.word = collector.word.substituted_word(low, high, word_) + word = word.substituted_word(low, high, word_) elif case == 1: gens = list(sorted(w.contains_generators())) array = w.array_form s, e = array[0] - word_ = collector.map_relation(w) + word_ = self.map_relation(w) word_ = (gens[0]**-1)*word_**e word_ = group.dtype(word_) - collector.word = collector.word.substituted_word(low, high, word_) + word = word.substituted_word(low, high, word_) else: array = w.array_form s, e = array[0] s1 = ((s, 1), ) s = group.dtype(s1) - re = relative_order[s] + re = self.relative_order[s] q = e//re r = e-q*re key = w[0]**re - if pc_relators[key]: - word_ = ((w[0], r), (pc_relators[key], re)) + if self.pc_relators[key]: + word_ = ((w[0], r), (self.pc_relators[key], re)) word_ = group.dtype(word_) else: if r != 0: word_ = w[0]**r else: word_ = None - collector.word = collector.word.eliminate_word(w, word_) - return collector.word + word = word.eliminate_word(w, word_) + return word diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index cb058bf68729..898c1f72bd8e 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -1,4 +1,4 @@ -from sympy.combinatorics.pc_groups import PolycyclicGroup, Collector, collected_word +from sympy.combinatorics.pc_groups import PolycyclicGroup, Collector from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.free_groups import free_group @@ -14,7 +14,8 @@ def test_collected_word(): word = x3*x2*x1*x0 relative_order = {x0: 2, x1: 3, x2: 2, x3: 2} - collected_word_ = collected_word(pc_relators, word, relative_order) + collector = Collector(pc_relators, relative_order) + collected_word_ = collector.collected_word(word) assert collected_word_ == x0*x1**2*x2*x3 From 29ee81b20bd9de799a8063d63a04955eb04b781c Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Fri, 28 Jun 2019 16:26:51 +0530 Subject: [PATCH 08/28] make-group-init-argument --- sympy/combinatorics/pc_groups.py | 39 +++++++++++++-------- sympy/combinatorics/tests/test_pc_groups.py | 3 +- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 6942647d9f52..3e86511ed187 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -70,9 +70,10 @@ class Collector(DefaultPrinting): Section 8.1.3 """ - def __init__(self, pc_relators, relative_order): + def __init__(self, pc_relators, relative_order, group): self.pc_relators = pc_relators self.relative_order = relative_order + self.group = group def minimal_uncollected_subwords(self, word): """ @@ -87,13 +88,14 @@ def minimal_uncollected_subwords(self, word): >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> relative_order = {x1: 2, x2: 3} - >>> collector = Collector(pc_relators, relative_order) >>> word = x2**2*x1**7 + >>> group = word.group + >>> collector = Collector(pc_relators, relative_order, group) >>> collector.minimal_uncollected_subwords(word) {((x1, 7),): 2, ((x2, 2), (x1, 1)): 0} """ - group = word.group + group = self.group index = {s: i+1 for i, s in enumerate(group.symbols)} array = word.array_form re = self.relative_order @@ -102,15 +104,16 @@ def minimal_uncollected_subwords(self, word): for i in range(len(array)-1): s1, e1 = array[i] s2, e2 = array[i+1] - if e2 > 0 and index[s1] > index[s2]: - # case-0: v = x[i]**a*x[i+1], where index[x[i]] > index[x[i+1]] - uncollected_subwords[((s1, e1), (s2, 1))] = 0 - - elif e2 < 0 and index[s1] > index[s2]: - # case-1: v = x[i]**a*x[i+1]*-1, where index[x[i]] > index[x[i+1]] - uncollected_subwords[((s1, e1), (s2, -1))] = 1 s = ((s1, 1), ) s = group.dtype(s) + if e1 > 0 and e1 < re[s]: + if e2 > 0 and index[s1] > index[s2]: + # case-0: v = x[i]**a*x[i+1], where index[x[i]] > index[x[i+1]] + uncollected_subwords[((s1, e1), (s2, 1))] = 0 + + elif e2 < 0 and index[s1] > index[s2]: + # case-1: v = x[i]**a*x[i+1]*-1, where index[x[i]] > index[x[i+1]] + uncollected_subwords[((s1, e1), (s2, -1))] = 1 if e1 > re[s]-1: # case-2: v = x[i]**a uncollected_subwords[((s1, e1), )] = 2 @@ -137,7 +140,9 @@ def relations(self): >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> relative_order = {x1: 2, x2: 3} - >>> collector = Collector(pc_relators, relative_order) + >>> word = x2**2*x1**7 + >>> group = word.group + >>> collector = Collector(pc_relators, relative_order, group) >>> power_rel, conj_rel = collector.relations() >>> power_rel {x1**2: 1} @@ -166,8 +171,9 @@ def subword_index(self, word, w): >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> relative_order = {x1: 2, x2: 3} - >>> collector = Collector(pc_relators, relative_order) >>> word = x2**2*x1**7 + >>> group = word.group + >>> collector = Collector(pc_relators, relative_order, group) >>> w = x2**2*x1 >>> collector.subword_index(word, w) (0, 3) @@ -196,7 +202,9 @@ def map_relation(self, w): >>> F, x0, x1, x2 = free_group("x0, x1, x2") >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1:x2, x0**-1*x2*x0:x2*x1} >>> relative_order = {x0: 2, x1: 2, x2: 3} - >>> collector = Collector(pc_relators, relative_order) + >>> word = x2**2*x1**7 + >>> group = word.group + >>> collector = Collector(pc_relators, relative_order, group) >>> w = x2*x1 >>> collector.map_relation(w) x2 @@ -220,13 +228,14 @@ def collected_word(self, word): >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> relative_order = {x1: 2, x2: 3} - >>> collector = Collector(pc_relators, relative_order) >>> word = x2**2*x1**7 + >>> group = word.group + >>> collector = Collector(pc_relators, relative_order, group) >>> collector.collected_word(word) x1*x2**-2 """ - group = word.group + group = self.group while True: uncollected_subwords = self.minimal_uncollected_subwords(word) if not uncollected_subwords: diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index 898c1f72bd8e..d96657f31ca7 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -14,7 +14,8 @@ def test_collected_word(): word = x3*x2*x1*x0 relative_order = {x0: 2, x1: 3, x2: 2, x3: 2} - collector = Collector(pc_relators, relative_order) + group = word.group + collector = Collector(pc_relators, relative_order, group) collected_word_ = collector.collected_word(word) assert collected_word_ == x0*x1**2*x2*x3 From 1aa9ba5a20ae741ff50af68898a563fc5d3cf136 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sat, 29 Jun 2019 11:10:56 +0530 Subject: [PATCH 09/28] change-order-inuncoll-add-s(3)-test --- sympy/combinatorics/pc_groups.py | 21 ++++++++++------ sympy/combinatorics/tests/test_pc_groups.py | 28 +++++++++++++++++++++ 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 3e86511ed187..88e571bdd61c 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -95,6 +95,10 @@ def minimal_uncollected_subwords(self, word): {((x1, 7),): 2, ((x2, 2), (x1, 1)): 0} """ + + # To handle the case word = + if not word: + return {} group = self.group index = {s: i+1 for i, s in enumerate(group.symbols)} array = word.array_form @@ -106,17 +110,18 @@ def minimal_uncollected_subwords(self, word): s2, e2 = array[i+1] s = ((s1, 1), ) s = group.dtype(s) - if e1 > 0 and e1 < re[s]: - if e2 > 0 and index[s1] > index[s2]: - # case-0: v = x[i]**a*x[i+1], where index[x[i]] > index[x[i+1]] - uncollected_subwords[((s1, e1), (s2, 1))] = 0 - - elif e2 < 0 and index[s1] > index[s2]: - # case-1: v = x[i]**a*x[i+1]*-1, where index[x[i]] > index[x[i+1]] - uncollected_subwords[((s1, e1), (s2, -1))] = 1 if e1 > re[s]-1: # case-2: v = x[i]**a uncollected_subwords[((s1, e1), )] = 2 + continue + + if e2 > 0 and index[s1] > index[s2]: + # case-0: v = x[i]**a*x[i+1], where index[x[i]] > index[x[i+1]] + uncollected_subwords[((s1, e1), (s2, 1))] = 0 + + elif e2 < 0 and index[s1] > index[s2]: + # case-1: v = x[i]**a*x[i+1]*-1, where index[x[i]] > index[x[i+1]] + uncollected_subwords[((s1, e1), (s2, -1))] = 1 i = len(array)-1 s1, e1 = array[i] diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index d96657f31ca7..c1e63e2b270c 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -29,3 +29,31 @@ def test_collected_word(): word = x3*x2*x1*x0 collected_word_ = x0*x1**2*x2*x3 assert word == collected_word_ + + + + F, x0, x1 = free_group("x0, x1") + # polycyclic relators for Symmetricgroup(3) + pc_relators = {x0**2: (), x1**3: (), x0**-1*x1*x0: x1**2} + relative_order = {x0: 2, x1: 3} + group = F + collector = Collector(pc_relators, relative_order, group) + + word = x1*x0 + assert collector.collected_word(word) == x0*x1**2 + word = x1*x0**2 + assert collector.collected_word(word) == x1 + word = x1**2*x0 + assert collector.collected_word(word) == x0*x1 + word = x1**4*x0**6 + assert collector.collected_word(word) == x1 + word = x0*x1 + # The word is already collected + assert collector.collected_word(word) == x0*x1 + word = x0**2*x1 + assert collector.collected_word(word) == x1 + word = x0**2*x1**3 + # Handle Identity case + assert collector.collected_word(word) == F.identity + word = x1**-2*x0 + assert collector.collected_word(word) == x0*x1**-4 From c82cd23ce04e3dab2127be68a513788698504bdd Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sat, 29 Jun 2019 11:24:59 +0530 Subject: [PATCH 10/28] sympy/. --- sympy/combinatorics/tests/test_pc_groups.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index c1e63e2b270c..bde5c2d5d215 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -39,21 +39,39 @@ def test_collected_word(): group = F collector = Collector(pc_relators, relative_order, group) + a = Permutation(0, 1) # x0 + b = Permutation(0, 1, 2) # x1 + word = x1*x0 assert collector.collected_word(word) == x0*x1**2 + assert b*a == a*b**2 + word = x1*x0**2 assert collector.collected_word(word) == x1 + assert b*a**2 == b + word = x1**2*x0 assert collector.collected_word(word) == x0*x1 + assert b**2*a == a*b + word = x1**4*x0**6 assert collector.collected_word(word) == x1 + assert b**4*a**6 == b + word = x0*x1 # The word is already collected assert collector.collected_word(word) == x0*x1 + assert a*b == a*b + word = x0**2*x1 assert collector.collected_word(word) == x1 + assert a**2*b == b + word = x0**2*x1**3 # Handle Identity case assert collector.collected_word(word) == F.identity + assert a**2*b**3 == Permutation(2) + word = x1**-2*x0 assert collector.collected_word(word) == x0*x1**-4 + assert b**-2*a == a*b**-4 From 78af5dde9f05b21590fb7e03421ade41993cc8a2 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sun, 30 Jun 2019 09:44:48 +0530 Subject: [PATCH 11/28] update --- sympy/combinatorics/pc_groups.py | 110 ++++++++++---------- sympy/combinatorics/tests/test_pc_groups.py | 4 +- 2 files changed, 56 insertions(+), 58 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 88e571bdd61c..5365f2c2c25c 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -1,5 +1,6 @@ from sympy.core import Basic from sympy import sieve +from sympy.core import S from sympy.combinatorics.perm_groups import PermutationGroup from sympy.printing.defaults import DefaultPrinting @@ -75,7 +76,7 @@ def __init__(self, pc_relators, relative_order, group): self.relative_order = relative_order self.group = group - def minimal_uncollected_subwords(self, word): + def minimal_uncollected_subword(self, word): """ Returns the minimal uncollected subwords. @@ -83,55 +84,47 @@ def minimal_uncollected_subwords(self, word): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group + >>> from sympy.core import S Example 8.7 Pg. 281 from [1] >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2, x2: 3} + >>> relative_order = {x1: 2, x2: S.Infinity} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) - >>> collector.minimal_uncollected_subwords(word) - {((x1, 7),): 2, ((x2, 2), (x1, 1)): 0} + >>> collector.minimal_uncollected_subword(word) + ((x2, 2), (x1, 1)) """ - # To handle the case word = if not word: - return {} + return None + group = self.group index = {s: i+1 for i, s in enumerate(group.symbols)} array = word.array_form re = self.relative_order - uncollected_subwords = {} for i in range(len(array)-1): s1, e1 = array[i] s2, e2 = array[i+1] s = ((s1, 1), ) s = group.dtype(s) - if e1 > re[s]-1: - # case-2: v = x[i]**a - uncollected_subwords[((s1, e1), )] = 2 - continue + if (e1 < 1 or e1 > re[s]-1) and re[s] != S.Infinity: + return ((s1, e1), ) - if e2 > 0 and index[s1] > index[s2]: - # case-0: v = x[i]**a*x[i+1], where index[x[i]] > index[x[i+1]] - uncollected_subwords[((s1, e1), (s2, 1))] = 0 - - elif e2 < 0 and index[s1] > index[s2]: - # case-1: v = x[i]**a*x[i+1]*-1, where index[x[i]] > index[x[i+1]] - uncollected_subwords[((s1, e1), (s2, -1))] = 1 + if index[s1] > index[s2]: + e = 1 if e2 > 0 else -1 + return ((s1, e1), (s2, e)) i = len(array)-1 s1, e1 = array[i] s = ((s1, 1), ) s = group.dtype(s) - if e1 > re[s]-1: - # case-2: v = x[i]**a - uncollected_subwords[((s1, e1), )] = 2 - - return uncollected_subwords + if (e1 < 1 or e1 > re[s]-1) and re[s] != S.Infinity: + return ((s1, e1), ) + return None def relations(self): """ @@ -142,9 +135,10 @@ def relations(self): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group + >>> from sympy.core import S >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2, x2: 3} + >>> relative_order = {x1: 2, x2: S.Infinity} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -173,9 +167,10 @@ def subword_index(self, word, w): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group + >>> from sympy.core import S >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2, x2: 3} + >>> relative_order = {x1: 2, x2: S.Infinity} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -204,9 +199,10 @@ def map_relation(self, w): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group + >>> from sympy.core import S >>> F, x0, x1, x2 = free_group("x0, x1, x2") >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1:x2, x0**-1*x2*x0:x2*x1} - >>> relative_order = {x0: 2, x1: 2, x2: 3} + >>> relative_order = {x0: 2, x1: 2, x2: S.Infinity} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -230,9 +226,10 @@ def collected_word(self, word): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group + >>> from sympy.core import S >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2, x2: 3} + >>> relative_order = {x1: 2, x2: S.Infinity} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -242,49 +239,50 @@ def collected_word(self, word): """ group = self.group while True: - uncollected_subwords = self.minimal_uncollected_subwords(word) - if not uncollected_subwords: + w = self.minimal_uncollected_subword(word) + if not w: break - w = list(uncollected_subwords)[0] - case = uncollected_subwords[w] - w = group.dtype(w) - low, high = self.subword_index(word, w) + low, high = self.subword_index(word, group.dtype(w)) if low == -1: continue - if case == 0: - gens = list(sorted(w.contains_generators())) - array = w.array_form - s, e = array[0] - word_ = self.map_relation(w) + if len(w) == 2 and w[1][1] > 0: + gens = list(sorted(group.dtype(w).contains_generators())) + s, e = w[0] + word_ = self.map_relation(group.dtype(w)) word_ = gens[0]*word_**e word_ = group.dtype(word_) word = word.substituted_word(low, high, word_) - elif case == 1: - gens = list(sorted(w.contains_generators())) - array = w.array_form - s, e = array[0] - word_ = self.map_relation(w) - word_ = (gens[0]**-1)*word_**e + elif len(w) == 2 and w[1][1] < 0: + gens = list(sorted(group.dtype(w).contains_generators())) + s, e = w[0] + word_ = self.map_relation(group.dtype(w)) + word_ = gens[0]**-1*word_**e word_ = group.dtype(word_) word = word.substituted_word(low, high, word_) - else: - array = w.array_form - s, e = array[0] - s1 = ((s, 1), ) - s = group.dtype(s1) - re = self.relative_order[s] - q = e//re - r = e-q*re - key = w[0]**re + s, e = w[0] + s1 = ((s, 1), ) + s = group.dtype(s1) + re = self.relative_order[s] + if re == S.Infinity: + continue + q = e//re + r = e-q*re + if r < 0 or r > re-1: + continue + + if len(w) == 1 and (e < 1 or e > re-1): + key = ((w[0][0], re), ) + key = group.dtype(key) if self.pc_relators[key]: - word_ = ((w[0], r), (self.pc_relators[key], re)) + word_ = ((w[0][0], r), (self.pc_relators[key], q)) word_ = group.dtype(word_) else: if r != 0: - word_ = w[0]**r + word_ = ((w[0][0], r), ) + word_ = group.dtype(word_) else: word_ = None - word = word.eliminate_word(w, word_) + word = word.eliminate_word(group.dtype(w), word_) return word diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index bde5c2d5d215..368ac467d91a 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -73,5 +73,5 @@ def test_collected_word(): assert a**2*b**3 == Permutation(2) word = x1**-2*x0 - assert collector.collected_word(word) == x0*x1**-4 - assert b**-2*a == a*b**-4 + assert collector.collected_word(word) == x0*x1**2 + assert b**-2*a == a*b**2 From 0033d11b9de23bf2d11cafa5d76b497bb5ff0cf9 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sun, 30 Jun 2019 11:53:18 +0530 Subject: [PATCH 12/28] remove-s.infinity --- sympy/combinatorics/pc_groups.py | 31 ++++++++++++------------------- 1 file changed, 12 insertions(+), 19 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 5365f2c2c25c..0e8483558aaf 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -1,6 +1,5 @@ from sympy.core import Basic from sympy import sieve -from sympy.core import S from sympy.combinatorics.perm_groups import PermutationGroup from sympy.printing.defaults import DefaultPrinting @@ -84,12 +83,11 @@ def minimal_uncollected_subword(self, word): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group - >>> from sympy.core import S Example 8.7 Pg. 281 from [1] >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2, x2: S.Infinity} + >>> relative_order = {x1: 2} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -111,7 +109,7 @@ def minimal_uncollected_subword(self, word): s2, e2 = array[i+1] s = ((s1, 1), ) s = group.dtype(s) - if (e1 < 1 or e1 > re[s]-1) and re[s] != S.Infinity: + if s in re and (e1 < 0 or e1 > re[s]-1): return ((s1, e1), ) if index[s1] > index[s2]: @@ -122,7 +120,7 @@ def minimal_uncollected_subword(self, word): s1, e1 = array[i] s = ((s1, 1), ) s = group.dtype(s) - if (e1 < 1 or e1 > re[s]-1) and re[s] != S.Infinity: + if s in re and (e1 < 0 or e1 > re[s]-1): return ((s1, e1), ) return None @@ -135,10 +133,10 @@ def relations(self): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group - >>> from sympy.core import S + >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2, x2: S.Infinity} + >>> relative_order = {x1: 2} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -167,10 +165,9 @@ def subword_index(self, word, w): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group - >>> from sympy.core import S >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2, x2: S.Infinity} + >>> relative_order = {x1: 2} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -199,10 +196,9 @@ def map_relation(self, w): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group - >>> from sympy.core import S >>> F, x0, x1, x2 = free_group("x0, x1, x2") >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1:x2, x0**-1*x2*x0:x2*x1} - >>> relative_order = {x0: 2, x1: 2, x2: S.Infinity} + >>> relative_order = {x0: 2, x1: 2, x2: 3} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -226,10 +222,9 @@ def collected_word(self, word): ======== >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group - >>> from sympy.core import S >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2, x2: S.Infinity} + >>> relative_order = {x1: 2} >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -245,9 +240,9 @@ def collected_word(self, word): low, high = self.subword_index(word, group.dtype(w)) if low == -1: continue + s, e = w[0] if len(w) == 2 and w[1][1] > 0: gens = list(sorted(group.dtype(w).contains_generators())) - s, e = w[0] word_ = self.map_relation(group.dtype(w)) word_ = gens[0]*word_**e word_ = group.dtype(word_) @@ -255,24 +250,22 @@ def collected_word(self, word): elif len(w) == 2 and w[1][1] < 0: gens = list(sorted(group.dtype(w).contains_generators())) - s, e = w[0] word_ = self.map_relation(group.dtype(w)) word_ = gens[0]**-1*word_**e word_ = group.dtype(word_) word = word.substituted_word(low, high, word_) - s, e = w[0] s1 = ((s, 1), ) s = group.dtype(s1) - re = self.relative_order[s] - if re == S.Infinity: + if not s in self.relative_order: continue + re = self.relative_order[s] q = e//re r = e-q*re if r < 0 or r > re-1: continue - if len(w) == 1 and (e < 1 or e > re-1): + if len(w) == 1 and (e < 0 or e > re-1): key = ((w[0][0], re), ) key = group.dtype(key) if self.pc_relators[key]: From f290d62c3867391665b49e17975962e92b90abce Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Mon, 1 Jul 2019 21:17:56 +0530 Subject: [PATCH 13/28] convert-relative_order-to-list --- sympy/combinatorics/pc_groups.py | 31 +++++++++------------ sympy/combinatorics/tests/test_pc_groups.py | 4 +-- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 0e8483558aaf..8ff7c28cee49 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -74,6 +74,7 @@ def __init__(self, pc_relators, relative_order, group): self.pc_relators = pc_relators self.relative_order = relative_order self.group = group + self.index = {s: i for i, s in enumerate(group.symbols)} def minimal_uncollected_subword(self, word): """ @@ -87,7 +88,7 @@ def minimal_uncollected_subword(self, word): Example 8.7 Pg. 281 from [1] >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2} + >>> relative_order = [2, None] >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -100,16 +101,14 @@ def minimal_uncollected_subword(self, word): return None group = self.group - index = {s: i+1 for i, s in enumerate(group.symbols)} array = word.array_form re = self.relative_order + index = self.index for i in range(len(array)-1): s1, e1 = array[i] s2, e2 = array[i+1] - s = ((s1, 1), ) - s = group.dtype(s) - if s in re and (e1 < 0 or e1 > re[s]-1): + if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1): return ((s1, e1), ) if index[s1] > index[s2]: @@ -118,9 +117,7 @@ def minimal_uncollected_subword(self, word): i = len(array)-1 s1, e1 = array[i] - s = ((s1, 1), ) - s = group.dtype(s) - if s in re and (e1 < 0 or e1 > re[s]-1): + if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1): return ((s1, e1), ) return None @@ -136,7 +133,7 @@ def relations(self): >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2} + >>> relative_order = [2, None] >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -167,7 +164,7 @@ def subword_index(self, word, w): >>> from sympy.combinatorics.free_groups import free_group >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2} + >>> relative_order = [2, None] >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -197,8 +194,8 @@ def map_relation(self, w): >>> from sympy.combinatorics.pc_groups import Collector >>> from sympy.combinatorics.free_groups import free_group >>> F, x0, x1, x2 = free_group("x0, x1, x2") - >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1:x2, x0**-1*x2*x0:x2*x1} - >>> relative_order = {x0: 2, x1: 2, x2: 3} + >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1: x2, x0**-1*x2*x0: x2*x1} + >>> relative_order = [2, 2, 3] >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -224,7 +221,7 @@ def collected_word(self, word): >>> from sympy.combinatorics.free_groups import free_group >>> F, x1, x2 = free_group("x1, x2") >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = {x1: 2} + >>> relative_order = [2, None] >>> word = x2**2*x1**7 >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) @@ -255,12 +252,10 @@ def collected_word(self, word): word_ = group.dtype(word_) word = word.substituted_word(low, high, word_) - s1 = ((s, 1), ) - s = group.dtype(s1) - if not s in self.relative_order: + if not self.relative_order[self.index[s]]: continue - re = self.relative_order[s] - q = e//re + re = self.relative_order[self.index[s]] + q = e // re r = e-q*re if r < 0 or r > re-1: continue diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index 368ac467d91a..bc19d248300d 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -13,7 +13,7 @@ def test_collected_word(): } word = x3*x2*x1*x0 - relative_order = {x0: 2, x1: 3, x2: 2, x3: 2} + relative_order = [2, 3, 2, 2] group = word.group collector = Collector(pc_relators, relative_order, group) collected_word_ = collector.collected_word(word) @@ -35,7 +35,7 @@ def test_collected_word(): F, x0, x1 = free_group("x0, x1") # polycyclic relators for Symmetricgroup(3) pc_relators = {x0**2: (), x1**3: (), x0**-1*x1*x0: x1**2} - relative_order = {x0: 2, x1: 3} + relative_order = [2, 3] group = F collector = Collector(pc_relators, relative_order, group) From 267af71012e32de6cb6cc7b3ee2891d9fc682727 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Fri, 5 Jul 2019 13:21:46 +0530 Subject: [PATCH 14/28] added-pc_presentation --- sympy/combinatorics/pc_groups.py | 115 ++++++++++++++++---- sympy/combinatorics/perm_groups.py | 13 ++- sympy/combinatorics/tests/test_pc_groups.py | 1 + sympy/core/tests/test_args.py | 7 -- 4 files changed, 109 insertions(+), 27 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 8ff7c28cee49..ccb7fc57db8f 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -3,30 +3,16 @@ from sympy.combinatorics.perm_groups import PermutationGroup from sympy.printing.defaults import DefaultPrinting -class PolycyclicGroup(Basic): +class PolycyclicGroup(DefaultPrinting): is_group = True is_solvable = True - def __init__(self, pcgs_): - self.perm_group = PermutationGroup(pcgs_) - self.pc_series = self._pc_series() - self.pcgs = self._compute_pcgs() + def __init__(self, pc_sequence, pc_series): + self.pc_series = pc_series + self.pcgs = pc_sequence - def _pc_series(self): - return self.perm_group.composition_series() - - def _compute_pcgs(self): - # computes the generating sequence for polycyclic groups. - series = self.pc_series - pcgs = [] - for i in range(len(series)-1): - for g in series[i].generators: - if not g in series[i+1]: - pcgs.append(g) - return pcgs - - def relative_orders(self): + def relative_order(self): rel_orders = [] for i in range(len(self.pc_series)-1): G = self.pc_series[i] @@ -58,6 +44,94 @@ def pc_element_exponent(self, element): break return exponent + def power_relations(self, group): + power_relators = [] + for i, s in enumerate(group.symbols): + gen = ((s, self.relative_order()[i]), ) + gen = group.dtype(gen) + power_relators.append(gen) + power_relators.reverse() + return power_relators + + def conjugate_relations(self, group): + conjugate_relations = [] + g = group.symbols + for i in range(len(g)): + for j in range(i+1, len(g)): + x1 = g[i] + x2 = g[j] + relation = ((x1, -1), (x2, 1), (x1, 1)) + conjugate_relations.append(group.dtype(relation)) + conjugate_relations.reverse() + return conjugate_relations + + def pc_presentation(self, group): + pc_relators = {} + sym = list(group.generators) + sym.reverse() + + pc_sequence = self.pcgs + pc_sequence.reverse() + + perm_to_free = {} + free_to_perm = {} + + for gen, s in zip(pc_sequence, sym): + perm_to_free[gen] = s + perm_to_free[gen**2] = s**2 + free_to_perm[s] = gen + free_to_perm[s**2] = gen**2 + + power_relators = self.power_relations(group) + conjugate_relators = self.conjugate_relations(group) + + G = PermutationGroup([pc_sequence[0]]) + for i, rel in enumerate(power_relators): + array = rel.array_form + s, e = array[0] + + l = G.generator_product(free_to_perm[rel[0]]**e, original = True) + word = group.identity + + for gens in l: + word = word*perm_to_free[gens] + collector = Collector(pc_relators, self.relative_order(), group) + word = collector.collected_word(word) + + if word: + pc_relators[rel] = word + else: + pc_relators[rel] = () + + if i+1 < len(pc_sequence) and pc_sequence[i+1] not in G: + G = PermutationGroup(G.generators + [pc_sequence[i+1]]) + + G = PermutationGroup([pc_sequence[0], pc_sequence[1]]) + + for i, rel in enumerate(conjugate_relators, start = 1): + gens = sorted(rel.contains_generators()) + + gens = [free_to_perm[gens[0]], free_to_perm[gens[1]]] + relation = gens[0]**-1*gens[1]*gens[0] + + l = G.generator_product(relation, original = True) + + word = group.identity + for gen in l: + word = word*perm_to_free[gen] + + collector = Collector(pc_relators, self.relative_order(), group) + word = collector.collected_word(word) + if word: + pc_relators[rel] = word + else: + pc_relators[rel] = () + + if i+1 < len(pc_sequence) and pc_sequence[i+1] not in G: + G = PermutationGroup(G.generators + [pc_sequence[i+1]] ) + + return pc_relators + class Collector(DefaultPrinting): @@ -117,6 +191,9 @@ def minimal_uncollected_subword(self, word): i = len(array)-1 s1, e1 = array[i] + # print(re) + # print(index) + # print("s1: ", s1, "e1: ", e1) if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1): return ((s1, e1), ) return None diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py index 6fec6dcc5c5d..1688f48c09c3 100644 --- a/sympy/combinatorics/perm_groups.py +++ b/sympy/combinatorics/perm_groups.py @@ -4519,7 +4519,18 @@ def polycyclic_group(self): from sympy.combinatorics.pc_groups import PolycyclicGroup if not self.is_polycyclic: raise ValueError("The group must be solvable") - return PolycyclicGroup(self.generators) + + pc_series = self.composition_series() + pc_sequence = [] + ref = pc_series[0][0] + + for i in range(len(pc_series)-1): + for g in pc_series[i].generators: + if not g in pc_series[i+1] and g.order() <= ref.order(): + ref = g + pc_sequence.append(ref) + ref = pc_series[0][0] + return PolycyclicGroup(pc_sequence, pc_series) def _orbit(degree, generators, alpha, action='tuples'): diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index bc19d248300d..6fe551884a97 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -1,6 +1,7 @@ from sympy.combinatorics.pc_groups import PolycyclicGroup, Collector from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.free_groups import free_group +from sympy.combinatorics.named_groups import SymmetricGroup def test_collected_word(): F, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") diff --git a/sympy/core/tests/test_args.py b/sympy/core/tests/test_args.py index a2375e161483..9d286093b11e 100644 --- a/sympy/core/tests/test_args.py +++ b/sympy/core/tests/test_args.py @@ -487,13 +487,6 @@ def test_sympy__combinatorics__perm_groups__PermutationGroup(): assert _test_args(PermutationGroup([Permutation([0, 1])])) -@XFAIL -def test_sympy__combinatorics__pc_groups__PolycyclicGroup(): - from sympy.combinatorics.permutations import Permutation - from sympy.combinatorics.pc_groups import PolycyclicGroup - assert _test_args(PolycyclicGroup([Permutation(1, 2, 3)])) - - def test_sympy__combinatorics__polyhedron__Polyhedron(): from sympy.combinatorics.permutations import Permutation from sympy.combinatorics.polyhedron import Polyhedron From 6935976cf8e9c3d8a05569b6085147ac463ba78f Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sat, 6 Jul 2019 00:33:05 +0530 Subject: [PATCH 15/28] add-tail-sequence-test-for-polycyclic_presentation --- sympy/combinatorics/pc_groups.py | 19 ++++----- sympy/combinatorics/perm_groups.py | 15 ++----- sympy/combinatorics/tests/test_pc_groups.py | 47 +++++++++++++++++++++ 3 files changed, 60 insertions(+), 21 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index ccb7fc57db8f..5c2d12fe7e5a 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -8,16 +8,17 @@ class PolycyclicGroup(DefaultPrinting): is_group = True is_solvable = True - def __init__(self, pc_sequence, pc_series): - self.pc_series = pc_series + def __init__(self, pc_sequence): + #self.pc_series = pc_series self.pcgs = pc_sequence def relative_order(self): - rel_orders = [] - for i in range(len(self.pc_series)-1): - G = self.pc_series[i] - H = self.pc_series[i+1] - rel_orders.append(G.order()//H.order()) + # rel_orders = [] + # for i in range(len(self.pc_series)-1): + # G = self.pc_series[i] + # H = self.pc_series[i+1] + # rel_orders.append(G.order()//H.order()) + rel_orders = [g.order() for g in self.pcgs] return rel_orders def is_prime_order(self): @@ -191,9 +192,7 @@ def minimal_uncollected_subword(self, word): i = len(array)-1 s1, e1 = array[i] - # print(re) - # print(index) - # print("s1: ", s1, "e1: ", e1) + if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1): return ((s1, e1), ) return None diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py index 1688f48c09c3..72bb881584ac 100644 --- a/sympy/combinatorics/perm_groups.py +++ b/sympy/combinatorics/perm_groups.py @@ -4520,17 +4520,10 @@ def polycyclic_group(self): if not self.is_polycyclic: raise ValueError("The group must be solvable") - pc_series = self.composition_series() - pc_sequence = [] - ref = pc_series[0][0] - - for i in range(len(pc_series)-1): - for g in pc_series[i].generators: - if not g in pc_series[i+1] and g.order() <= ref.order(): - ref = g - pc_sequence.append(ref) - ref = pc_series[0][0] - return PolycyclicGroup(pc_sequence, pc_series) + series = self.derived_series() + pc_sequence = series[len(series)-2].generators + + return PolycyclicGroup(pc_sequence) def _orbit(degree, generators, alpha, action='tuples'): diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index 6fe551884a97..6b71ba65e565 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -76,3 +76,50 @@ def test_collected_word(): word = x1**-2*x0 assert collector.collected_word(word) == x0*x1**2 assert b**-2*a == a*b**2 + + +def test_pc_presentation(): + # SymmetricGroup(3) + F, x0, x1 = free_group("x0, x1") + group = F + G = SymmetricGroup(3) + + pc_group = G.polycyclic_group() + pc_presentation = pc_group.pc_presentation(F) + assert pc_presentation == { x1**3: (), x0**3: (), x0**-1*x1*x0: x0**2 } + + x0, x1 = pc_group.pcgs + assert x0**-1*x1*x0 == x0**2 + + + # SymmetricGroup(4) + F, x0, x1 = free_group("x0, x1") + group = F + G = SymmetricGroup(4) + + pc_group = G.polycyclic_group() + pc_presentation = pc_group.pc_presentation(F) + assert pc_presentation == { x1**2: (), x0**2: (), x0**-1*x1*x0: x1 } + + x0, x1 = pc_group.pcgs + assert x0**-1*x1*x0 == x1 + + + # SymmetricGroup(9).sylow_subgroup(3) + F, x0, x1, x2 = free_group("x0, x1, x2") + group = F + S = SymmetricGroup(9) + G = S.sylow_subgroup(3) + der = G.derived_series() + + pc_group = G.polycyclic_group() + pc_presentation = pc_group.pc_presentation(F) + assert pc_presentation == { x2**3: (), x1**3: (), x0**3: (), + x1**-1*x2*x1: x2, x0**-1*x2*x0: x2, + x0**-1*x1*x0: x0**2 } + + # x0, x1, x2 = pc_group.pcgs (I don't know why the sequence gets reversed here) + x0, x1, x2 = der[len(der)-2].generators + assert x1**-1*x2*x1 == x2 + assert x0**-1*x2*x0 == x2 + assert x0**-1*x1*x0 == x0**2 From e5c9b1b2388a41b72b7c90f836fa01c100419c30 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sat, 6 Jul 2019 00:41:06 +0530 Subject: [PATCH 16/28] update --- sympy/combinatorics/tests/test_pc_groups.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index 6b71ba65e565..3e6f379aaee3 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -88,6 +88,7 @@ def test_pc_presentation(): pc_presentation = pc_group.pc_presentation(F) assert pc_presentation == { x1**3: (), x0**3: (), x0**-1*x1*x0: x0**2 } + pc_group.pcgs.reverse() x0, x1 = pc_group.pcgs assert x0**-1*x1*x0 == x0**2 @@ -101,6 +102,7 @@ def test_pc_presentation(): pc_presentation = pc_group.pc_presentation(F) assert pc_presentation == { x1**2: (), x0**2: (), x0**-1*x1*x0: x1 } + pc_group.pcgs.reverse() x0, x1 = pc_group.pcgs assert x0**-1*x1*x0 == x1 @@ -110,7 +112,6 @@ def test_pc_presentation(): group = F S = SymmetricGroup(9) G = S.sylow_subgroup(3) - der = G.derived_series() pc_group = G.polycyclic_group() pc_presentation = pc_group.pc_presentation(F) @@ -118,8 +119,8 @@ def test_pc_presentation(): x1**-1*x2*x1: x2, x0**-1*x2*x0: x2, x0**-1*x1*x0: x0**2 } - # x0, x1, x2 = pc_group.pcgs (I don't know why the sequence gets reversed here) - x0, x1, x2 = der[len(der)-2].generators + pc_group.pcgs.reverse() + x0, x1, x2 = pc_group.pcgs assert x1**-1*x2*x1 == x2 assert x0**-1*x2*x0 == x2 assert x0**-1*x1*x0 == x0**2 From feadb8ba5a4783a56b4478bdd681823bf27ef827 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Wed, 10 Jul 2019 10:24:16 +0530 Subject: [PATCH 17/28] add-exp-vector-and-docs --- sympy/combinatorics/pc_groups.py | 169 +++++++++++++++++++++-------- sympy/combinatorics/perm_groups.py | 18 ++- 2 files changed, 140 insertions(+), 47 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 5c2d12fe7e5a..7e3c8a4858a1 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -8,21 +8,20 @@ class PolycyclicGroup(DefaultPrinting): is_group = True is_solvable = True - def __init__(self, pc_sequence): - #self.pc_series = pc_series + def __init__(self, pc_sequence, pc_series): self.pcgs = pc_sequence + self.pc_series = pc_series def relative_order(self): - # rel_orders = [] - # for i in range(len(self.pc_series)-1): - # G = self.pc_series[i] - # H = self.pc_series[i+1] - # rel_orders.append(G.order()//H.order()) - rel_orders = [g.order() for g in self.pcgs] + rel_orders = [] + for i in range(len(self.pc_series)-1): + G = self.pc_series[i] + H = self.pc_series[i+1] + rel_orders.append(G.order() // H.order()) return rel_orders def is_prime_order(self): - for order in self.relative_orders(): + for order in self.relative_order(): if order not in sieve: return False return True @@ -30,22 +29,50 @@ def is_prime_order(self): def length(self): return len(self.pcgs) - def pc_element_exponent(self, element): - series = self.pc_series - pcgs = self.pcgs - exponent = [0]*len(series) - for i in range(len(series)): - exp = 0 - if not element in series[i]: - for j in range(len(pcgs)): - element = (pcgs[j]**-1)*element - exp = exp + 1 - if element in series[i]: - exponent[i] = exp - break - return exponent + def exponent_vector(self, element, group): + G = PermutationGroup() + for g in self.pcgs: + G = PermutationGroup(G.generators + [g]) + gens = G.generator_product(element, original = True) + + perm_to_free = {} + for sym, g in zip(group.generators, self.pcgs): + perm_to_free[g] = sym + w = group.identity + for g in gens: + w = w*perm_to_free[g] + + pc_presentation = self.pc_presentation(group) + collector = Collector(pc_presentation, self.relative_order(), group) + word = collector.collected_word(w) + + index = {s: i for i, s in enumerate(group.symbols)} + exp_vector = [0]*len(group) + word = word.array_form + for t in word: + exp_vector[index[t[0]]] = t[1] + return exp_vector def power_relations(self, group): + """ + Return a list of power relations. + If `x` is a free group element and `re` represents + the relative order of `x` then power relation of a + single generator `x` is `x**re`. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import PolycyclicGroup + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x0, x1 = free_group("x0, x1") + >>> G = SymmetricGroup(4) + >>> pc_group = G.polycyclic_group() + >>> group = F + >>> pc_group.power_relations(group) + [x1**2, x0**6] + + """ power_relators = [] for i, s in enumerate(group.symbols): gen = ((s, self.relative_order()[i]), ) @@ -55,6 +82,25 @@ def power_relations(self, group): return power_relators def conjugate_relations(self, group): + """ + Return a list of conjugate relations. + Conjugate relations of a polycyclic group are of the + form `x[i]**-1*x[i+1]*x[i]`. + + Examples + ======== + >>> from sympy.combinatorics.pc_groups import PolycyclicGroup + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.free_groups import free_group + >>> F, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") + >>> G = SymmetricGroup(4) + >>> pc_group = G.polycyclic_group() + >>> group = F + >>> pc_group.conjugate_relations(group) + [x2**-1*x3*x2, x1**-1*x3*x1, x1**-1*x2*x1, x0**-1*x3*x0, + x0**-1*x2*x0, x0**-1*x1*x0] + + """ conjugate_relations = [] g = group.symbols for i in range(len(g)): @@ -69,24 +115,33 @@ def conjugate_relations(self, group): def pc_presentation(self, group): pc_relators = {} sym = list(group.generators) + # start from the bottom that is, `x2->x1->x0` sym.reverse() pc_sequence = self.pcgs pc_sequence.reverse() + # store the mapping of polycyclic sequence with the + # free group elements and vice-versa perm_to_free = {} free_to_perm = {} for gen, s in zip(pc_sequence, sym): + # since `s**-1` can also be produced by the generator_product + perm_to_free[gen**-1] = s**-1 perm_to_free[gen] = s - perm_to_free[gen**2] = s**2 + free_to_perm[s**-1] = gen**-1 free_to_perm[s] = gen - free_to_perm[s**2] = gen**2 + # the LHS of the power and conjugate relations power_relators = self.power_relations(group) conjugate_relators = self.conjugate_relations(group) + # form the group with only the last element in polycyclic + # sequence which will be incremented as moving up G = PermutationGroup([pc_sequence[0]]) + + # compute the RHS of power relations for i, rel in enumerate(power_relators): array = rel.array_form s, e = array[0] @@ -94,42 +149,43 @@ def pc_presentation(self, group): l = G.generator_product(free_to_perm[rel[0]]**e, original = True) word = group.identity + # concatenate the generators in `l` to form the word + # to be collected. for gens in l: word = word*perm_to_free[gens] collector = Collector(pc_relators, self.relative_order(), group) - word = collector.collected_word(word) - if word: - pc_relators[rel] = word - else: - pc_relators[rel] = () + word = collector.collected_word(word) + pc_relators[rel] = word if word else () - if i+1 < len(pc_sequence) and pc_sequence[i+1] not in G: - G = PermutationGroup(G.generators + [pc_sequence[i+1]]) + if i < len(pc_sequence) and pc_sequence[i] not in G: + G = PermutationGroup(G.generators + [pc_sequence[i]]) - G = PermutationGroup([pc_sequence[0], pc_sequence[1]]) + G = PermutationGroup([pc_sequence[0]]) + index = {s: i for i, s in enumerate(group.generators)} + index1 = {i: s for i, s in enumerate(group.generators)} - for i, rel in enumerate(conjugate_relators, start = 1): + # compute the RHS of conjugate relations + for rel in conjugate_relators: gens = sorted(rel.contains_generators()) + if free_to_perm[index1[index[gens[0]]+1]] not in G: + G = PermutationGroup(G.generators + [free_to_perm[index1[index[gens[0]]+1]]]) + # map free group generators to the pc_sequence elements + # and compute the conjugate relation `x[i]**-1*x[i+1]*x[i]` gens = [free_to_perm[gens[0]], free_to_perm[gens[1]]] relation = gens[0]**-1*gens[1]*gens[0] l = G.generator_product(relation, original = True) - word = group.identity + + # concatenate the generators in `l` to form the word + # to be collected. for gen in l: word = word*perm_to_free[gen] - collector = Collector(pc_relators, self.relative_order(), group) word = collector.collected_word(word) - if word: - pc_relators[rel] = word - else: - pc_relators[rel] = () - - if i+1 < len(pc_sequence) and pc_sequence[i+1] not in G: - G = PermutationGroup(G.generators + [pc_sequence[i+1]] ) + pc_relators[rel] = word if word else () return pc_relators @@ -155,6 +211,18 @@ def minimal_uncollected_subword(self, word): """ Returns the minimal uncollected subwords. + A word `v` defined on generators in `X` is a minimal + uncollected subword of the word `w` if `v` is a subword + of `w` and it has one of the following form + + i) `v = x[i+1]**a_j*x[i]` + + ii) `v = x[i+1]**a_j*x[i]**-1` + + iii) `v = x[i]**a_j` for relative_order of `x[i] != infinity` + and `a_j` is not in `{1, ..., s-1}`. Where, s is the power + exponent of the corresponding generator. + Examples ======== >>> from sympy.combinatorics.pc_groups import Collector @@ -265,6 +333,12 @@ def subword_index(self, word, w): def map_relation(self, w): """ + Return a conjugate relation. + Given a word formed by two free group elements, the + corresponding conjugate relation with those free + group elements is formed and mapped with the collected + word in the polycyclic presentation. + Examples ======== >>> from sympy.combinatorics.pc_groups import Collector @@ -284,13 +358,20 @@ def map_relation(self, w): """ group = w.group - gens = list(sorted(w.contains_generators())) + gens = sorted(w.contains_generators()) key = gens[0]**-1*gens[1]*gens[0] return self.pc_relators[key] def collected_word(self, word): """ + Return the collected form of a word. + + A word `w` is called collected, if `w = x{i_1}**a_1*...*x{i_r}**a_r` + with `i_1 < i_2< ... < i_r` and `a_j` is in `{1, ..., s_j-1}` + if `s_j != infinity`. + Otherwise w is uncollected. + Examples ======== >>> from sympy.combinatorics.pc_groups import Collector diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py index 72bb881584ac..c189b1785043 100644 --- a/sympy/combinatorics/perm_groups.py +++ b/sympy/combinatorics/perm_groups.py @@ -4520,10 +4520,22 @@ def polycyclic_group(self): if not self.is_polycyclic: raise ValueError("The group must be solvable") - series = self.derived_series() - pc_sequence = series[len(series)-2].generators + der = self.derived_series() + pc_series = [] + pc_sequence = [] + relative_order = [] + pc_series.append(der[-1]) + der.reverse() - return PolycyclicGroup(pc_sequence) + for i in range(len(der)-1): + H = der[i] + for g in der[i+1].generators: + if g not in H: + H = PermutationGroup([g] + H.generators) + pc_series.insert(0, H) + pc_sequence.insert(0, g) + + return PolycyclicGroup(pc_sequence, pc_series) def _orbit(degree, generators, alpha, action='tuples'): From a72ba44a04f937fa8a79caee33a9029424af9fd5 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Wed, 10 Jul 2019 13:13:41 +0530 Subject: [PATCH 18/28] add-presentation-tests --- sympy/combinatorics/pc_groups.py | 98 +++++++++++---------- sympy/combinatorics/tests/test_pc_groups.py | 84 ++++++++---------- 2 files changed, 92 insertions(+), 90 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 7e3c8a4858a1..7669a3795574 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -70,7 +70,7 @@ def power_relations(self, group): >>> pc_group = G.polycyclic_group() >>> group = F >>> pc_group.power_relations(group) - [x1**2, x0**6] + [x1**3, x0**2] """ power_relators = [] @@ -164,6 +164,7 @@ def pc_presentation(self, group): G = PermutationGroup([pc_sequence[0]]) index = {s: i for i, s in enumerate(group.generators)} index1 = {i: s for i, s in enumerate(group.generators)} + pc_sequence.reverse() # compute the RHS of conjugate relations for rel in conjugate_relators: @@ -177,6 +178,7 @@ def pc_presentation(self, group): relation = gens[0]**-1*gens[1]*gens[0] l = G.generator_product(relation, original = True) + l.reverse() word = group.identity # concatenate the generators in `l` to form the word @@ -236,13 +238,13 @@ def minimal_uncollected_subword(self, word): >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) >>> collector.minimal_uncollected_subword(word) - ((x2, 2), (x1, 1)) + [((x2, 2), (x1, 1)), ((x1, 7),)] """ # To handle the case word = if not word: return None - + l = [] group = self.group array = word.array_form re = self.relative_order @@ -251,19 +253,21 @@ def minimal_uncollected_subword(self, word): for i in range(len(array)-1): s1, e1 = array[i] s2, e2 = array[i+1] + if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1): - return ((s1, e1), ) + l.append(((s1, e1), )) if index[s1] > index[s2]: e = 1 if e2 > 0 else -1 - return ((s1, e1), (s2, e)) + l.append(((s1, e1), (s2, e))) i = len(array)-1 s1, e1 = array[i] if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1): - return ((s1, e1), ) - return None + l.append(((s1, e1), )) + + return l def relations(self): """ @@ -388,46 +392,50 @@ def collected_word(self, word): """ group = self.group while True: - w = self.minimal_uncollected_subword(word) - if not w: + l = self.minimal_uncollected_subword(word) + if not l: break - low, high = self.subword_index(word, group.dtype(w)) - if low == -1: - continue - s, e = w[0] - if len(w) == 2 and w[1][1] > 0: - gens = list(sorted(group.dtype(w).contains_generators())) - word_ = self.map_relation(group.dtype(w)) - word_ = gens[0]*word_**e - word_ = group.dtype(word_) - word = word.substituted_word(low, high, word_) - - elif len(w) == 2 and w[1][1] < 0: - gens = list(sorted(group.dtype(w).contains_generators())) - word_ = self.map_relation(group.dtype(w)) - word_ = gens[0]**-1*word_**e - word_ = group.dtype(word_) - word = word.substituted_word(low, high, word_) - - if not self.relative_order[self.index[s]]: - continue - re = self.relative_order[self.index[s]] - q = e // re - r = e-q*re - if r < 0 or r > re-1: - continue - - if len(w) == 1 and (e < 0 or e > re-1): - key = ((w[0][0], re), ) - key = group.dtype(key) - if self.pc_relators[key]: - word_ = ((w[0][0], r), (self.pc_relators[key], q)) + l.sort(key = len) + for w in l: + if not w: + break + low, high = self.subword_index(word, group.dtype(w)) + if low == -1: + continue + s, e = w[0] + if len(w) == 2 and w[1][1] > 0: + gens = list(sorted(group.dtype(w).contains_generators())) + word_ = self.map_relation(group.dtype(w)) + word_ = gens[0]*word_**e + word_ = group.dtype(word_) + word = word.substituted_word(low, high, word_) + + elif len(w) == 2 and w[1][1] < 0: + gens = list(sorted(group.dtype(w).contains_generators())) + word_ = self.map_relation(group.dtype(w)) + word_ = gens[0]**-1*word_**e word_ = group.dtype(word_) - else: - if r != 0: - word_ = ((w[0][0], r), ) + word = word.substituted_word(low, high, word_) + + if not self.relative_order[self.index[s]]: + continue + re = self.relative_order[self.index[s]] + q = e // re + r = e-q*re + if r < 0 or r > re-1: + continue + + if len(w) == 1 and (e < 0 or e > re-1): + key = ((w[0][0], re), ) + key = group.dtype(key) + if self.pc_relators[key]: + word_ = ((w[0][0], r), (self.pc_relators[key], q)) word_ = group.dtype(word_) else: - word_ = None - word = word.eliminate_word(group.dtype(w), word_) + if r != 0: + word_ = ((w[0][0], r), ) + word_ = group.dtype(word_) + else: + word_ = None + word = word.eliminate_word(group.dtype(w), word_) return word diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index 3e6f379aaee3..7f969e5abf3a 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -79,48 +79,42 @@ def test_collected_word(): def test_pc_presentation(): - # SymmetricGroup(3) - F, x0, x1 = free_group("x0, x1") - group = F - G = SymmetricGroup(3) - - pc_group = G.polycyclic_group() - pc_presentation = pc_group.pc_presentation(F) - assert pc_presentation == { x1**3: (), x0**3: (), x0**-1*x1*x0: x0**2 } - - pc_group.pcgs.reverse() - x0, x1 = pc_group.pcgs - assert x0**-1*x1*x0 == x0**2 - - - # SymmetricGroup(4) - F, x0, x1 = free_group("x0, x1") - group = F - G = SymmetricGroup(4) - - pc_group = G.polycyclic_group() - pc_presentation = pc_group.pc_presentation(F) - assert pc_presentation == { x1**2: (), x0**2: (), x0**-1*x1*x0: x1 } - - pc_group.pcgs.reverse() - x0, x1 = pc_group.pcgs - assert x0**-1*x1*x0 == x1 - - - # SymmetricGroup(9).sylow_subgroup(3) - F, x0, x1, x2 = free_group("x0, x1, x2") - group = F - S = SymmetricGroup(9) - G = S.sylow_subgroup(3) - - pc_group = G.polycyclic_group() - pc_presentation = pc_group.pc_presentation(F) - assert pc_presentation == { x2**3: (), x1**3: (), x0**3: (), - x1**-1*x2*x1: x2, x0**-1*x2*x0: x2, - x0**-1*x1*x0: x0**2 } - - pc_group.pcgs.reverse() - x0, x1, x2 = pc_group.pcgs - assert x1**-1*x2*x1 == x2 - assert x0**-1*x2*x0 == x2 - assert x0**-1*x1*x0 == x0**2 + F1, x0, x1 = free_group("x0, x1") + F2, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") + F3, x0, x1, x2, x3, x4, x5, x6 = free_group("x0, x1, x2, x3, x4, x5, x6") + + l = [(SymmetricGroup(3), F1), (SymmetricGroup(4), F2), + (SymmetricGroup(9).sylow_subgroup(3), F2), (SymmetricGroup(9).sylow_subgroup(2), F3), + (SymmetricGroup(8).sylow_subgroup(2), F3)] + + for t in l: + pc_group = t[0].polycyclic_group() + pc_presentation = pc_group.pc_presentation(t[1]) + + pcgs = pc_group.pcgs + free_to_perm = {} + for s, g in zip(t[1].symbols, pcgs): + free_to_perm[s] = g + + for k, v in pc_presentation.items(): + k_array = k.array_form + if v != (): + v_array = v.array_form + + lhs = Permutation() + for gen in k_array: + s = gen[0] + e = gen[1] + lhs = lhs*free_to_perm[s]**e + + if v == (): + assert lhs.is_identity + continue + + rhs = Permutation() + for gen in v_array: + s = gen[0] + e = gen[1] + rhs = rhs*free_to_perm[s]**e + + assert lhs == rhs From cb4ee73f228164d4330ffc7acc06b1676f976f61 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sat, 13 Jul 2019 11:56:07 +0530 Subject: [PATCH 19/28] update-pc_presentation --- sympy/combinatorics/pc_groups.py | 225 ++++++++++++++++--------------- 1 file changed, 115 insertions(+), 110 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 7669a3795574..fd8891fcf0db 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -1,5 +1,5 @@ from sympy.core import Basic -from sympy import sieve +from sympy import isprime from sympy.combinatorics.perm_groups import PermutationGroup from sympy.printing.defaults import DefaultPrinting @@ -21,10 +21,7 @@ def relative_order(self): return rel_orders def is_prime_order(self): - for order in self.relative_order(): - if order not in sieve: - return False - return True + return all(isprime(order) for order in self.relative_order()) def length(self): return len(self.pcgs) @@ -114,80 +111,93 @@ def conjugate_relations(self, group): def pc_presentation(self, group): pc_relators = {} - sym = list(group.generators) - # start from the bottom that is, `x2->x1->x0` - sym.reverse() - pc_sequence = self.pcgs - pc_sequence.reverse() # store the mapping of polycyclic sequence with the # free group elements and vice-versa perm_to_free = {} free_to_perm = {} - for gen, s in zip(pc_sequence, sym): + for gen, s in zip(pc_sequence, group.generators): # since `s**-1` can also be produced by the generator_product perm_to_free[gen**-1] = s**-1 perm_to_free[gen] = s free_to_perm[s**-1] = gen**-1 free_to_perm[s] = gen - # the LHS of the power and conjugate relations - power_relators = self.power_relations(group) + # Get the LHS of conjugate relations conjugate_relators = self.conjugate_relations(group) + relators = conjugate_relators + + index = {i: s for i, s in enumerate(group.generators)} + rev_index = {s: i for i, s in enumerate(group.generators)} + + # Get the combination of power and conjugate relators + # in the sequence they should be processed starting from + # the bottom + for i, rel in enumerate(relators): + gens = sorted(rel.contains_generators()) + ind = rev_index[gens[0]] + s = index[ind+1] + + re = self.relative_order()[ind+1] + power_relator = s**re + + if power_relator not in relators: + relators.insert(i, power_relator) - # form the group with only the last element in polycyclic - # sequence which will be incremented as moving up - G = PermutationGroup([pc_sequence[0]]) + s = index[0] + re = self.relative_order()[0] + power_relator = s**re + relators.insert(i+1, power_relator) + + # Initialize a group `G` which will be expanded as + # moving up + G = PermutationGroup() + collector = Collector(pc_relators, self.relative_order(), group) + + # compute the RHS of relators and form polycyclic relations + for rel in relators: + gens = sorted(rel.contains_generators()) + ind = rev_index[gens[0]] + s = index[ind+1] if ind+1 < len(group) else index[ind] + + if free_to_perm[s] not in G: + G = PermutationGroup([free_to_perm[s]] + G.generators) - # compute the RHS of power relations - for i, rel in enumerate(power_relators): array = rel.array_form - s, e = array[0] + if len(array) == 1: + s, e = array[0] + relation = free_to_perm[rel[0]]**e + l = G.generator_product(relation, original = True) + l.reverse() + word = group.identity - l = G.generator_product(free_to_perm[rel[0]]**e, original = True) - word = group.identity + # concatenate the generators in `l` to form the word + # to be collected. + for gens in l: + word = word*perm_to_free[gens] - # concatenate the generators in `l` to form the word - # to be collected. - for gens in l: - word = word*perm_to_free[gens] - collector = Collector(pc_relators, self.relative_order(), group) + word = collector.collected_word(word) + collector.pc_relators[rel] = word if word else () - word = collector.collected_word(word) - pc_relators[rel] = word if word else () + else: + # map free group generators to the pc_sequence elements + # and compute the conjugate relation `x[i]**-1*x[i+1]*x[i]` + gens = [free_to_perm[gens[0]], free_to_perm[gens[1]]] + relation = gens[0]**-1*gens[1]*gens[0] - if i < len(pc_sequence) and pc_sequence[i] not in G: - G = PermutationGroup(G.generators + [pc_sequence[i]]) + l = G.generator_product(relation, original = True) + l.reverse() + word = group.identity - G = PermutationGroup([pc_sequence[0]]) - index = {s: i for i, s in enumerate(group.generators)} - index1 = {i: s for i, s in enumerate(group.generators)} - pc_sequence.reverse() + # concatenate the generators in `l` to form the word + # to be collected. + for gen in l: + word = word*perm_to_free[gen] - # compute the RHS of conjugate relations - for rel in conjugate_relators: - gens = sorted(rel.contains_generators()) - if free_to_perm[index1[index[gens[0]]+1]] not in G: - G = PermutationGroup(G.generators + [free_to_perm[index1[index[gens[0]]+1]]]) - - # map free group generators to the pc_sequence elements - # and compute the conjugate relation `x[i]**-1*x[i+1]*x[i]` - gens = [free_to_perm[gens[0]], free_to_perm[gens[1]]] - relation = gens[0]**-1*gens[1]*gens[0] - - l = G.generator_product(relation, original = True) - l.reverse() - word = group.identity - - # concatenate the generators in `l` to form the word - # to be collected. - for gen in l: - word = word*perm_to_free[gen] - collector = Collector(pc_relators, self.relative_order(), group) - word = collector.collected_word(word) - pc_relators[rel] = word if word else () + word = collector.collected_word(word) + collector.pc_relators[rel] = word if word else () return pc_relators @@ -238,36 +248,33 @@ def minimal_uncollected_subword(self, word): >>> group = word.group >>> collector = Collector(pc_relators, relative_order, group) >>> collector.minimal_uncollected_subword(word) - [((x2, 2), (x1, 1)), ((x1, 7),)] + ((x1, 7),) """ # To handle the case word = if not word: return None - l = [] + group = self.group array = word.array_form re = self.relative_order index = self.index - for i in range(len(array)-1): + for i in range(len(array)): s1, e1 = array[i] - s2, e2 = array[i+1] if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1): - l.append(((s1, e1), )) + return ((s1, e1), ) + + for i in range(len(array)-1): + s1, e1 = array[i] + s2, e2 = array[i+1] if index[s1] > index[s2]: e = 1 if e2 > 0 else -1 - l.append(((s1, e1), (s2, e))) - - i = len(array)-1 - s1, e1 = array[i] + return ((s1, e1), (s2, e)) - if re[index[s1]] and (e1 < 0 or e1 > re[index[s1]]-1): - l.append(((s1, e1), )) - - return l + return None def relations(self): """ @@ -392,50 +399,48 @@ def collected_word(self, word): """ group = self.group while True: - l = self.minimal_uncollected_subword(word) - if not l: + w = self.minimal_uncollected_subword(word) + if not w: break - l.sort(key = len) - for w in l: - if not w: - break - low, high = self.subword_index(word, group.dtype(w)) - if low == -1: - continue - s, e = w[0] - if len(w) == 2 and w[1][1] > 0: - gens = list(sorted(group.dtype(w).contains_generators())) - word_ = self.map_relation(group.dtype(w)) - word_ = gens[0]*word_**e - word_ = group.dtype(word_) - word = word.substituted_word(low, high, word_) - elif len(w) == 2 and w[1][1] < 0: - gens = list(sorted(group.dtype(w).contains_generators())) - word_ = self.map_relation(group.dtype(w)) - word_ = gens[0]**-1*word_**e + low, high = self.subword_index(word, group.dtype(w)) + if low == -1: + continue + s, e = w[0] + if len(w) == 2 and w[1][1] > 0: + gens = list(sorted(group.dtype(w).contains_generators())) + word_ = self.map_relation(group.dtype(w)) + word_ = gens[0]*word_**e + word_ = group.dtype(word_) + word = word.substituted_word(low, high, word_) + + elif len(w) == 2 and w[1][1] < 0: + gens = list(sorted(group.dtype(w).contains_generators())) + word_ = self.map_relation(group.dtype(w)) + word_ = gens[0]**-1*word_**e + word_ = group.dtype(word_) + word = word.substituted_word(low, high, word_) + + if not self.relative_order[self.index[s]]: + continue + re = self.relative_order[self.index[s]] + q = e // re + r = e-q*re + if r < 0 or r > re-1: + continue + + if len(w) == 1 and (e < 0 or e > re-1): + key = ((w[0][0], re), ) + key = group.dtype(key) + if self.pc_relators[key]: + word_ = ((w[0][0], r), (self.pc_relators[key], q)) word_ = group.dtype(word_) - word = word.substituted_word(low, high, word_) - - if not self.relative_order[self.index[s]]: - continue - re = self.relative_order[self.index[s]] - q = e // re - r = e-q*re - if r < 0 or r > re-1: - continue - - if len(w) == 1 and (e < 0 or e > re-1): - key = ((w[0][0], re), ) - key = group.dtype(key) - if self.pc_relators[key]: - word_ = ((w[0][0], r), (self.pc_relators[key], q)) + else: + if r != 0: + word_ = ((w[0][0], r), ) word_ = group.dtype(word_) else: - if r != 0: - word_ = ((w[0][0], r), ) - word_ = group.dtype(word_) - else: - word_ = None - word = word.eliminate_word(group.dtype(w), word_) + word_ = None + word = word.eliminate_word(group.dtype(w), word_) + return word From ed4f14220b28887d8474d843ae38054e53cb5b50 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sun, 14 Jul 2019 12:22:35 +0530 Subject: [PATCH 20/28] pc-group-methods --- sympy/combinatorics/pc_groups.py | 98 ++++++++++++++++++++- sympy/combinatorics/tests/test_pc_groups.py | 22 +++++ 2 files changed, 118 insertions(+), 2 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index fd8891fcf0db..f4e389a28d38 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -27,13 +27,50 @@ def length(self): return len(self.pcgs) def exponent_vector(self, element, group): + """ + Return the exponent vector of length equal to the + length of polycyclic generating sequence. + + For a given generator/element `g` of the polycyclic group, + it can be represented as `g = x{1}**e{1}....x{n}**e{n}`, + where `x{i}` represents polycyclic generators and `n` is + the number of generators in the group equal to the length + of pcgs. + + Examples + ======== + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.free_groups import free_group + >>> from sympy.combinatorics.permutations import Permutation + >>> G = SymmetricGroup(4) + >>> PcGroup = G.polycyclic_group() + >>> pcgs = PcGroup.pcgs + >>> group, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") + >>> PcGroup.exponent_vector(G[0], group) + [1, 0, 0, 0] + >>> exp = PcGroup.exponent_vector(G[1], group) + >>> g = Permutation() + >>> for i in range(len(exp)): + ... g = g*pcgs[i] if exp[i] else g + >>> assert g == G[1] + + References + ========== + + .. [1] Holt, D., Eick, B., O'Brien, E. + "Handbook of Computational Group Theory" + Section 8.1.1, Definition 8.4 + + """ G = PermutationGroup() for g in self.pcgs: - G = PermutationGroup(G.generators + [g]) + G = PermutationGroup([g] + G.generators) gens = G.generator_product(element, original = True) + gens.reverse() perm_to_free = {} for sym, g in zip(group.generators, self.pcgs): + perm_to_free[g**-1] = sym**-1 perm_to_free[g] = sym w = group.identity for g in gens: @@ -50,6 +87,63 @@ def exponent_vector(self, element, group): exp_vector[index[t[0]]] = t[1] return exp_vector + def depth(self, element, group): + """ + Return the depth of a given element. + + The depth of a given element `g` is defined by + `dep{g} = i if e{1} = e{2} = ... = e{i-1} = 0` + and `e{i} != 0`, where `e` represents the exponent-vector. + + Examples + ======== + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.free_groups import free_group + >>> group, x, y = free_group("x, y") + >>> G = SymmetricGroup(3) + >>> PcGroup = G.polycyclic_group() + >>> PcGroup.depth(G[0], group) + 2 + >>> PcGroup.depth(G[1], group) + 1 + + References + ========== + + .. [1] Holt, D., Eick, B., O'Brien, E. + "Handbook of Computational Group Theory" + Section 8.1.1, Definition 8.5 + + """ + exp_vector = self.exponent_vector(element, group) + return next((i+1 for i, x in enumerate(exp_vector) if x), len(self.pcgs)+1) + + def leading_exponent(self, element, group): + """ + Return the leading non-zero exponent. + + The leading exponent for a given element `g` is defined + by `leading_exponent{g} = e{i}`, if `depth{g} = i`. + + Examples + ======== + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.free_groups import free_group + >>> group, x, y = free_group("x, y") + >>> G = SymmetricGroup(3) + >>> PcGroup = G.polycyclic_group() + >>> PcGroup.leading_exponent(G[0], group) + 1 + >>> PcGroup.leading_exponent(G[1], group) + 1 + + """ + exp_vector = self.exponent_vector(element, group) + depth = self.depth(element, group) + if depth != len(self.pcgs)+1: + return exp_vector[depth-1] + return None + def power_relations(self, group): """ Return a list of power relations. @@ -149,7 +243,7 @@ def pc_presentation(self, group): s = index[0] re = self.relative_order()[0] power_relator = s**re - relators.insert(i+1, power_relator) + relators.insert(len(relators)+1, power_relator) # Initialize a group `G` which will be expanded as # moving up diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index 7f969e5abf3a..3d3700e2b9c8 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -118,3 +118,25 @@ def test_pc_presentation(): rhs = rhs*free_to_perm[s]**e assert lhs == rhs + + +def test_exponent_vector(): + F1, x0, x1 = free_group("x0, x1") + F2, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") + F3, x0, x1, x2, x3, x4, x5, x6 = free_group("x0, x1, x2, x3, x4, x5, x6") + + l = [(SymmetricGroup(3), F1), (SymmetricGroup(4), F2), + (SymmetricGroup(9).sylow_subgroup(3), F2), (SymmetricGroup(9).sylow_subgroup(2), F3), + (SymmetricGroup(8).sylow_subgroup(2), F3)] + + for t in l: + PcGroup = t[0].polycyclic_group() + pcgs = PcGroup.pcgs + + for gen in t[0].generators: + exp = PcGroup.exponent_vector(gen, t[1]) + g = Permutation() + for i in range(len(exp)): + g = g*pcgs[i] if exp[i] else g + + assert g == gen From fbdc1ad7eb92d3d545519c1ad7e5f6063665b2b8 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Tue, 16 Jul 2019 13:20:33 +0530 Subject: [PATCH 21/28] update-pc_presentation --- sympy/combinatorics/pc_groups.py | 167 +++++++------------------------ 1 file changed, 38 insertions(+), 129 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index f4e389a28d38..5bbb508ded48 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -144,155 +144,64 @@ def leading_exponent(self, element, group): return exp_vector[depth-1] return None - def power_relations(self, group): - """ - Return a list of power relations. - If `x` is a free group element and `re` represents - the relative order of `x` then power relation of a - single generator `x` is `x**re`. - - Examples - ======== - >>> from sympy.combinatorics.pc_groups import PolycyclicGroup - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.free_groups import free_group - >>> F, x0, x1 = free_group("x0, x1") - >>> G = SymmetricGroup(4) - >>> pc_group = G.polycyclic_group() - >>> group = F - >>> pc_group.power_relations(group) - [x1**3, x0**2] - - """ - power_relators = [] - for i, s in enumerate(group.symbols): - gen = ((s, self.relative_order()[i]), ) - gen = group.dtype(gen) - power_relators.append(gen) - power_relators.reverse() - return power_relators - - def conjugate_relations(self, group): - """ - Return a list of conjugate relations. - Conjugate relations of a polycyclic group are of the - form `x[i]**-1*x[i+1]*x[i]`. - - Examples - ======== - >>> from sympy.combinatorics.pc_groups import PolycyclicGroup - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.free_groups import free_group - >>> F, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") - >>> G = SymmetricGroup(4) - >>> pc_group = G.polycyclic_group() - >>> group = F - >>> pc_group.conjugate_relations(group) - [x2**-1*x3*x2, x1**-1*x3*x1, x1**-1*x2*x1, x0**-1*x3*x0, - x0**-1*x2*x0, x0**-1*x1*x0] - - """ - conjugate_relations = [] - g = group.symbols - for i in range(len(g)): - for j in range(i+1, len(g)): - x1 = g[i] - x2 = g[j] - relation = ((x1, -1), (x2, 1), (x1, 1)) - conjugate_relations.append(group.dtype(relation)) - conjugate_relations.reverse() - return conjugate_relations - def pc_presentation(self, group): + rel_order = self.relative_order() pc_relators = {} - pc_sequence = self.pcgs - - # store the mapping of polycyclic sequence with the - # free group elements and vice-versa perm_to_free = {} free_to_perm = {} + pcgs = self.pcgs - for gen, s in zip(pc_sequence, group.generators): - # since `s**-1` can also be produced by the generator_product + for gen, s in zip(pcgs, group.generators): perm_to_free[gen**-1] = s**-1 perm_to_free[gen] = s free_to_perm[s**-1] = gen**-1 free_to_perm[s] = gen - # Get the LHS of conjugate relations - conjugate_relators = self.conjugate_relations(group) - relators = conjugate_relators - - index = {i: s for i, s in enumerate(group.generators)} - rev_index = {s: i for i, s in enumerate(group.generators)} + collector = Collector(pc_relators, rel_order, group) + pcgs.reverse() + series = self.pc_series + series.reverse() + collected_gens = [] - # Get the combination of power and conjugate relators - # in the sequence they should be processed starting from - # the bottom - for i, rel in enumerate(relators): - gens = sorted(rel.contains_generators()) - ind = rev_index[gens[0]] - s = index[ind+1] + for i, gen in enumerate(pcgs): + re = rel_order[len(rel_order)-i-1] + relation = perm_to_free[gen]**re + G = series[i] if i != 0 else series[i+1] - re = self.relative_order()[ind+1] - power_relator = s**re + l = G.generator_product(gen**re, original = True) + l.reverse() - if power_relator not in relators: - relators.insert(i, power_relator) + word = group.identity + for g in l: + word = word*perm_to_free[g] - s = index[0] - re = self.relative_order()[0] - power_relator = s**re - relators.insert(len(relators)+1, power_relator) + word = collector.collected_word(word) + pc_relators[relation] = word if word else () + collector.pc_relators = pc_relators - # Initialize a group `G` which will be expanded as - # moving up - G = PermutationGroup() - collector = Collector(pc_relators, self.relative_order(), group) - - # compute the RHS of relators and form polycyclic relations - for rel in relators: - gens = sorted(rel.contains_generators()) - ind = rev_index[gens[0]] - s = index[ind+1] if ind+1 < len(group) else index[ind] - - if free_to_perm[s] not in G: - G = PermutationGroup([free_to_perm[s]] + G.generators) - - array = rel.array_form - if len(array) == 1: - s, e = array[0] - relation = free_to_perm[rel[0]]**e - l = G.generator_product(relation, original = True) - l.reverse() - word = group.identity + collected_gens.append(gen) + if len(collected_gens) > 1: + conj = collected_gens[len(collected_gens)-1] + conjecture = perm_to_free[conj] - # concatenate the generators in `l` to form the word - # to be collected. - for gens in l: - word = word*perm_to_free[gens] - - word = collector.collected_word(word) - collector.pc_relators[rel] = word if word else () - - else: - # map free group generators to the pc_sequence elements - # and compute the conjugate relation `x[i]**-1*x[i+1]*x[i]` - gens = [free_to_perm[gens[0]], free_to_perm[gens[1]]] - relation = gens[0]**-1*gens[1]*gens[0] + for j in range(len(collected_gens)-1): + conjugated = perm_to_free[collected_gens[j]] - l = G.generator_product(relation, original = True) - l.reverse() - word = group.identity + relation = conjecture**-1*conjugated*conjecture + gens = conj**-1*collected_gens[j]*conj - # concatenate the generators in `l` to form the word - # to be collected. - for gen in l: - word = word*perm_to_free[gen] + l = G.generator_product(gens, original = True) + l.reverse() + word = group.identity + for g in l: + word = word*perm_to_free[g] - word = collector.collected_word(word) - collector.pc_relators[rel] = word if word else () + word = collector.collected_word(word) + pc_relators[relation] = word if word else () + collector.pc_relators = pc_relators + series.reverse() + pcgs.reverse() return pc_relators From 415d476d8dbbe31e2f335e8abc735cc983a78be0 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Wed, 17 Jul 2019 19:25:06 +0530 Subject: [PATCH 22/28] add-tests-and-presentation-docs --- sympy/combinatorics/pc_groups.py | 55 +++++++++++++++++++-- sympy/combinatorics/tests/test_pc_groups.py | 48 ++++++++++++------ 2 files changed, 82 insertions(+), 21 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 5bbb508ded48..1a44bc600221 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -132,8 +132,6 @@ def leading_exponent(self, element, group): >>> group, x, y = free_group("x, y") >>> G = SymmetricGroup(3) >>> PcGroup = G.polycyclic_group() - >>> PcGroup.leading_exponent(G[0], group) - 1 >>> PcGroup.leading_exponent(G[1], group) 1 @@ -145,17 +143,64 @@ def leading_exponent(self, element, group): return None def pc_presentation(self, group): + """ + Return the polycyclic presentation. + + There are two types of relations used in polycyclic + presentation. + i) Power relations of the form `x{i}^re{i} = R{i}{i}`, + `for 0 <= i < length(pcgs)` where `x` represents polycyclic + generator and `re` is the corresponding relative order. + + ii) Conjugate relations of the form `x{j}^-1*x{i}*x{j}`, + `for 0 <= j < i <= length(pcgs)`. + + Examples + ======== + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.permutations import Permutation + >>> from sympy.combinatorics.free_groups import free_group + >>> S = SymmetricGroup(49).sylow_subgroup(7) + >>> der = S.derived_series() + >>> G = der[len(der)-2] + >>> PcGroup = G.polycyclic_group() + >>> pcgs = PcGroup.pcgs + >>> len(pcgs) + 6 + >>> group, x0, x1, x2, x3, x4, x5 = free_group("x0, x1, x2, x3, x4, x5") + >>> pc_resentation = PcGroup.pc_presentation(group) + >>> free_to_perm = {} + >>> for s, g in zip(group.symbols, pcgs): + ... free_to_perm[s] = g + + >>> for k, v in pc_resentation.items(): + ... k_array = k.array_form + ... if v != (): + ... v_array = v.array_form + ... lhs = Permutation() + ... for gen in k_array: + ... s = gen[0] + ... e = gen[1] + ... lhs = lhs*free_to_perm[s]**e + ... if v == (): + ... assert lhs.is_identity + ... continue + ... rhs = Permutation() + ... for gen in v_array: + ... s = gen[0] + ... e = gen[1] + ... rhs = rhs*free_to_perm[s]**e + ... assert lhs == rhs + + """ rel_order = self.relative_order() pc_relators = {} perm_to_free = {} - free_to_perm = {} pcgs = self.pcgs for gen, s in zip(pcgs, group.generators): perm_to_free[gen**-1] = s**-1 perm_to_free[gen] = s - free_to_perm[s**-1] = gen**-1 - free_to_perm[s] = gen collector = Collector(pc_relators, rel_order, group) pcgs.reverse() diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index 3d3700e2b9c8..30c48450cf2f 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -87,6 +87,22 @@ def test_pc_presentation(): (SymmetricGroup(9).sylow_subgroup(3), F2), (SymmetricGroup(9).sylow_subgroup(2), F3), (SymmetricGroup(8).sylow_subgroup(2), F3)] + S = SymmetricGroup(125).sylow_subgroup(5) + G = S.derived_series()[2] + group, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17,\ + x18, x19 = free_group("x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12,\ + x13, x14, x15, x16, x17, x18, x19") + l.append((G, group)) + + G = SymmetricGroup(25).sylow_subgroup(5) + group, x0, x1, x2, x3, x4, x5 = free_group("x0, x1, x2, x3, x4, x5") + l.append((G, group)) + + S = SymmetricGroup(11**2).sylow_subgroup(11) + G = S.derived_series()[2] + group, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9 = free_group("x0, x1, x2, x3, x4, x5, x6, x7, x8, x9") + l.append((G, group)) + for t in l: pc_group = t[0].polycyclic_group() pc_presentation = pc_group.pc_presentation(t[1]) @@ -120,23 +136,23 @@ def test_pc_presentation(): assert lhs == rhs -def test_exponent_vector(): - F1, x0, x1 = free_group("x0, x1") - F2, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") - F3, x0, x1, x2, x3, x4, x5, x6 = free_group("x0, x1, x2, x3, x4, x5, x6") +# def test_exponent_vector(): +# F1, x0, x1 = free_group("x0, x1") +# F2, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") +# F3, x0, x1, x2, x3, x4, x5, x6 = free_group("x0, x1, x2, x3, x4, x5, x6") - l = [(SymmetricGroup(3), F1), (SymmetricGroup(4), F2), - (SymmetricGroup(9).sylow_subgroup(3), F2), (SymmetricGroup(9).sylow_subgroup(2), F3), - (SymmetricGroup(8).sylow_subgroup(2), F3)] +# l = [(SymmetricGroup(3), F1), (SymmetricGroup(4), F2), +# (SymmetricGroup(9).sylow_subgroup(3), F2), (SymmetricGroup(9).sylow_subgroup(2), F3), +# (SymmetricGroup(8).sylow_subgroup(2), F3)] - for t in l: - PcGroup = t[0].polycyclic_group() - pcgs = PcGroup.pcgs +# for t in l: +# PcGroup = t[0].polycyclic_group() +# pcgs = PcGroup.pcgs - for gen in t[0].generators: - exp = PcGroup.exponent_vector(gen, t[1]) - g = Permutation() - for i in range(len(exp)): - g = g*pcgs[i] if exp[i] else g +# for gen in t[0].generators: +# exp = PcGroup.exponent_vector(gen, t[1]) +# g = Permutation() +# for i in range(len(exp)): +# g = g*pcgs[i] if exp[i] else g - assert g == gen +# assert g == gen From ecfbb7d43d223426411aef229d65318271f989b3 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Sat, 20 Jul 2019 14:31:46 +0530 Subject: [PATCH 23/28] update-group-to-free_group --- sympy/combinatorics/pc_groups.py | 147 ++++++++++++++++--------------- 1 file changed, 76 insertions(+), 71 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 1a44bc600221..2899f826a529 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -26,7 +26,7 @@ def is_prime_order(self): def length(self): return len(self.pcgs) - def exponent_vector(self, element, group): + def exponent_vector(self, element, free_group): """ Return the exponent vector of length equal to the length of polycyclic generating sequence. @@ -34,7 +34,7 @@ def exponent_vector(self, element, group): For a given generator/element `g` of the polycyclic group, it can be represented as `g = x{1}**e{1}....x{n}**e{n}`, where `x{i}` represents polycyclic generators and `n` is - the number of generators in the group equal to the length + the number of generators in the free_group equal to the length of pcgs. Examples @@ -45,10 +45,10 @@ def exponent_vector(self, element, group): >>> G = SymmetricGroup(4) >>> PcGroup = G.polycyclic_group() >>> pcgs = PcGroup.pcgs - >>> group, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") - >>> PcGroup.exponent_vector(G[0], group) + >>> free_group, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") + >>> PcGroup.exponent_vector(G[0], free_group) [1, 0, 0, 0] - >>> exp = PcGroup.exponent_vector(G[1], group) + >>> exp = PcGroup.exponent_vector(G[1], free_group) >>> g = Permutation() >>> for i in range(len(exp)): ... g = g*pcgs[i] if exp[i] else g @@ -69,25 +69,25 @@ def exponent_vector(self, element, group): gens.reverse() perm_to_free = {} - for sym, g in zip(group.generators, self.pcgs): + for sym, g in zip(free_group.generators, self.pcgs): perm_to_free[g**-1] = sym**-1 perm_to_free[g] = sym - w = group.identity + w = free_group.identity for g in gens: w = w*perm_to_free[g] - pc_presentation = self.pc_presentation(group) - collector = Collector(pc_presentation, self.relative_order(), group) + pc_presentation = self.pc_presentation(free_group) + collector = Collector(pc_presentation, self.relative_order(), free_group) word = collector.collected_word(w) - index = {s: i for i, s in enumerate(group.symbols)} - exp_vector = [0]*len(group) + index = {s: i for i, s in enumerate(free_group.symbols)} + exp_vector = [0]*len(free_group) word = word.array_form for t in word: exp_vector[index[t[0]]] = t[1] return exp_vector - def depth(self, element, group): + def depth(self, element, free_group): """ Return the depth of a given element. @@ -99,12 +99,12 @@ def depth(self, element, group): ======== >>> from sympy.combinatorics.named_groups import SymmetricGroup >>> from sympy.combinatorics.free_groups import free_group - >>> group, x, y = free_group("x, y") + >>> free_group, x, y = free_group("x, y") >>> G = SymmetricGroup(3) >>> PcGroup = G.polycyclic_group() - >>> PcGroup.depth(G[0], group) + >>> PcGroup.depth(G[0], free_group) 2 - >>> PcGroup.depth(G[1], group) + >>> PcGroup.depth(G[1], free_group) 1 References @@ -115,10 +115,10 @@ def depth(self, element, group): Section 8.1.1, Definition 8.5 """ - exp_vector = self.exponent_vector(element, group) + exp_vector = self.exponent_vector(element, free_group) return next((i+1 for i, x in enumerate(exp_vector) if x), len(self.pcgs)+1) - def leading_exponent(self, element, group): + def leading_exponent(self, element, free_group): """ Return the leading non-zero exponent. @@ -129,20 +129,20 @@ def leading_exponent(self, element, group): ======== >>> from sympy.combinatorics.named_groups import SymmetricGroup >>> from sympy.combinatorics.free_groups import free_group - >>> group, x, y = free_group("x, y") + >>> free_group, x, y = free_group("x, y") >>> G = SymmetricGroup(3) >>> PcGroup = G.polycyclic_group() - >>> PcGroup.leading_exponent(G[1], group) + >>> PcGroup.leading_exponent(G[1], free_group) 1 """ - exp_vector = self.exponent_vector(element, group) - depth = self.depth(element, group) + exp_vector = self.exponent_vector(element, free_group) + depth = self.depth(element, free_group) if depth != len(self.pcgs)+1: return exp_vector[depth-1] return None - def pc_presentation(self, group): + def pc_presentation(self, free_group): """ Return the polycyclic presentation. @@ -167,10 +167,10 @@ def pc_presentation(self, group): >>> pcgs = PcGroup.pcgs >>> len(pcgs) 6 - >>> group, x0, x1, x2, x3, x4, x5 = free_group("x0, x1, x2, x3, x4, x5") - >>> pc_resentation = PcGroup.pc_presentation(group) + >>> free_group, x0, x1, x2, x3, x4, x5 = free_group("x0, x1, x2, x3, x4, x5") + >>> pc_resentation = PcGroup.pc_presentation(free_group) >>> free_to_perm = {} - >>> for s, g in zip(group.symbols, pcgs): + >>> for s, g in zip(free_group.symbols, pcgs): ... free_to_perm[s] = g >>> for k, v in pc_resentation.items(): @@ -198,11 +198,11 @@ def pc_presentation(self, group): perm_to_free = {} pcgs = self.pcgs - for gen, s in zip(pcgs, group.generators): + for gen, s in zip(pcgs, free_group.generators): perm_to_free[gen**-1] = s**-1 perm_to_free[gen] = s - collector = Collector(pc_relators, rel_order, group) + collector = Collector(pc_relators, rel_order, free_group) pcgs.reverse() series = self.pc_series series.reverse() @@ -216,7 +216,7 @@ def pc_presentation(self, group): l = G.generator_product(gen**re, original = True) l.reverse() - word = group.identity + word = free_group.identity for g in l: word = word*perm_to_free[g] @@ -227,17 +227,17 @@ def pc_presentation(self, group): collected_gens.append(gen) if len(collected_gens) > 1: conj = collected_gens[len(collected_gens)-1] - conjecture = perm_to_free[conj] + conjugator = perm_to_free[conj] for j in range(len(collected_gens)-1): conjugated = perm_to_free[collected_gens[j]] - relation = conjecture**-1*conjugated*conjecture + relation = conjugator**-1*conjugated*conjugator gens = conj**-1*collected_gens[j]*conj l = G.generator_product(gens, original = True) l.reverse() - word = group.identity + word = free_group.identity for g in l: word = word*perm_to_free[g] @@ -261,11 +261,11 @@ class Collector(DefaultPrinting): Section 8.1.3 """ - def __init__(self, pc_relators, relative_order, group): + def __init__(self, pc_relators, relative_order, free_group): self.pc_relators = pc_relators self.relative_order = relative_order - self.group = group - self.index = {s: i for i, s in enumerate(group.symbols)} + self.free_group = free_group + self.index = {s: i for i, s in enumerate(free_group.symbols)} def minimal_uncollected_subword(self, word): """ @@ -293,8 +293,8 @@ def minimal_uncollected_subword(self, word): >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> relative_order = [2, None] >>> word = x2**2*x1**7 - >>> group = word.group - >>> collector = Collector(pc_relators, relative_order, group) + >>> free_group = word.group + >>> collector = Collector(pc_relators, relative_order, free_group) >>> collector.minimal_uncollected_subword(word) ((x1, 7),) @@ -303,7 +303,6 @@ def minimal_uncollected_subword(self, word): if not word: return None - group = self.group array = word.array_form re = self.relative_order index = self.index @@ -338,8 +337,8 @@ def relations(self): >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> relative_order = [2, None] >>> word = x2**2*x1**7 - >>> group = word.group - >>> collector = Collector(pc_relators, relative_order, group) + >>> free_group = word.group + >>> collector = Collector(pc_relators, relative_order, free_group) >>> power_rel, conj_rel = collector.relations() >>> power_rel {x1**2: 1} @@ -369,8 +368,8 @@ def subword_index(self, word, w): >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> relative_order = [2, None] >>> word = x2**2*x1**7 - >>> group = word.group - >>> collector = Collector(pc_relators, relative_order, group) + >>> free_group = word.group + >>> collector = Collector(pc_relators, relative_order, free_group) >>> w = x2**2*x1 >>> collector.subword_index(word, w) (0, 3) @@ -406,8 +405,8 @@ def map_relation(self, w): >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1: x2, x0**-1*x2*x0: x2*x1} >>> relative_order = [2, 2, 3] >>> word = x2**2*x1**7 - >>> group = word.group - >>> collector = Collector(pc_relators, relative_order, group) + >>> free_group = word.group + >>> collector = Collector(pc_relators, relative_order, free_group) >>> w = x2*x1 >>> collector.map_relation(w) x2 @@ -416,9 +415,11 @@ def map_relation(self, w): x1**2 """ - group = w.group - gens = sorted(w.contains_generators()) - key = gens[0]**-1*gens[1]*gens[0] + array = w.array_form + s1 = array[0][0] + s2 = array[1][0] + key = ((s2, -1), (s1, 1), (s2, 1)) + key = self.free_group.dtype(key) return self.pc_relators[key] @@ -439,56 +440,60 @@ def collected_word(self, word): >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} >>> relative_order = [2, None] >>> word = x2**2*x1**7 - >>> group = word.group - >>> collector = Collector(pc_relators, relative_order, group) + >>> free_group = word.group + >>> collector = Collector(pc_relators, relative_order, free_group) >>> collector.collected_word(word) x1*x2**-2 """ - group = self.group + free_group = self.free_group while True: w = self.minimal_uncollected_subword(word) if not w: break - low, high = self.subword_index(word, group.dtype(w)) + low, high = self.subword_index(word, free_group.dtype(w)) if low == -1: continue - s, e = w[0] + s1, e1 = w[0] if len(w) == 2 and w[1][1] > 0: - gens = list(sorted(group.dtype(w).contains_generators())) - word_ = self.map_relation(group.dtype(w)) - word_ = gens[0]*word_**e - word_ = group.dtype(word_) + s2, e2 = w[1] + s2 = ((s2, 1), ) + s2 = free_group.dtype(s2) + word_ = self.map_relation(free_group.dtype(w)) + word_ = s2*word_**e1 + word_ = free_group.dtype(word_) word = word.substituted_word(low, high, word_) - elif len(w) == 2 and w[1][1] < 0: - gens = list(sorted(group.dtype(w).contains_generators())) - word_ = self.map_relation(group.dtype(w)) - word_ = gens[0]**-1*word_**e - word_ = group.dtype(word_) - word = word.substituted_word(low, high, word_) - - if not self.relative_order[self.index[s]]: + if not self.relative_order[self.index[s1]]: continue - re = self.relative_order[self.index[s]] - q = e // re - r = e-q*re + re = self.relative_order[self.index[s1]] + q = e1 // re + r = e1-q*re if r < 0 or r > re-1: continue - if len(w) == 1 and (e < 0 or e > re-1): + if len(w) == 1 and (e1 < 0 or e1 > re-1): key = ((w[0][0], re), ) - key = group.dtype(key) + key = free_group.dtype(key) if self.pc_relators[key]: word_ = ((w[0][0], r), (self.pc_relators[key], q)) - word_ = group.dtype(word_) + word_ = free_group.dtype(word_) else: if r != 0: word_ = ((w[0][0], r), ) - word_ = group.dtype(word_) + word_ = free_group.dtype(word_) else: word_ = None - word = word.eliminate_word(group.dtype(w), word_) + word = word.eliminate_word(free_group.dtype(w), word_) + + elif len(w) == 2 and w[1][1] < 0: + s2, e2 = w[1] + s2 = ((s2, 1), ) + s2 = free_group.dtype(s2) + word_ = self.map_relation(free_group.dtype(w)) + word_ = s2**-1*word_**e1 + word_ = free_group.dtype(word_) + word = word.substituted_word(low, high, word_) return word From e210b27807e77037798c2186abfb7575111140bd Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Wed, 24 Jul 2019 14:21:15 +0530 Subject: [PATCH 24/28] modify-class-structure --- sympy/combinatorics/pc_groups.py | 566 ++++++++++---------- sympy/combinatorics/perm_groups.py | 6 +- sympy/combinatorics/tests/test_pc_groups.py | 138 +---- 3 files changed, 312 insertions(+), 398 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 2899f826a529..de7bf34dd079 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -1,254 +1,26 @@ from sympy.core import Basic -from sympy import isprime +from sympy import isprime, symbols from sympy.combinatorics.perm_groups import PermutationGroup from sympy.printing.defaults import DefaultPrinting +from sympy.combinatorics.free_groups import free_group class PolycyclicGroup(DefaultPrinting): is_group = True is_solvable = True - def __init__(self, pc_sequence, pc_series): + def __init__(self, pc_sequence, pc_series, relative_order, collector = None): self.pcgs = pc_sequence self.pc_series = pc_series - - def relative_order(self): - rel_orders = [] - for i in range(len(self.pc_series)-1): - G = self.pc_series[i] - H = self.pc_series[i+1] - rel_orders.append(G.order() // H.order()) - return rel_orders + self.relative_order = relative_order + self.collector = Collector(self.pcgs, pc_series, relative_order) if not collector else collector def is_prime_order(self): - return all(isprime(order) for order in self.relative_order()) + return all(isprime(order) for order in self.relative_order) def length(self): return len(self.pcgs) - def exponent_vector(self, element, free_group): - """ - Return the exponent vector of length equal to the - length of polycyclic generating sequence. - - For a given generator/element `g` of the polycyclic group, - it can be represented as `g = x{1}**e{1}....x{n}**e{n}`, - where `x{i}` represents polycyclic generators and `n` is - the number of generators in the free_group equal to the length - of pcgs. - - Examples - ======== - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.free_groups import free_group - >>> from sympy.combinatorics.permutations import Permutation - >>> G = SymmetricGroup(4) - >>> PcGroup = G.polycyclic_group() - >>> pcgs = PcGroup.pcgs - >>> free_group, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") - >>> PcGroup.exponent_vector(G[0], free_group) - [1, 0, 0, 0] - >>> exp = PcGroup.exponent_vector(G[1], free_group) - >>> g = Permutation() - >>> for i in range(len(exp)): - ... g = g*pcgs[i] if exp[i] else g - >>> assert g == G[1] - - References - ========== - - .. [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of Computational Group Theory" - Section 8.1.1, Definition 8.4 - - """ - G = PermutationGroup() - for g in self.pcgs: - G = PermutationGroup([g] + G.generators) - gens = G.generator_product(element, original = True) - gens.reverse() - - perm_to_free = {} - for sym, g in zip(free_group.generators, self.pcgs): - perm_to_free[g**-1] = sym**-1 - perm_to_free[g] = sym - w = free_group.identity - for g in gens: - w = w*perm_to_free[g] - - pc_presentation = self.pc_presentation(free_group) - collector = Collector(pc_presentation, self.relative_order(), free_group) - word = collector.collected_word(w) - - index = {s: i for i, s in enumerate(free_group.symbols)} - exp_vector = [0]*len(free_group) - word = word.array_form - for t in word: - exp_vector[index[t[0]]] = t[1] - return exp_vector - - def depth(self, element, free_group): - """ - Return the depth of a given element. - - The depth of a given element `g` is defined by - `dep{g} = i if e{1} = e{2} = ... = e{i-1} = 0` - and `e{i} != 0`, where `e` represents the exponent-vector. - - Examples - ======== - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.free_groups import free_group - >>> free_group, x, y = free_group("x, y") - >>> G = SymmetricGroup(3) - >>> PcGroup = G.polycyclic_group() - >>> PcGroup.depth(G[0], free_group) - 2 - >>> PcGroup.depth(G[1], free_group) - 1 - - References - ========== - - .. [1] Holt, D., Eick, B., O'Brien, E. - "Handbook of Computational Group Theory" - Section 8.1.1, Definition 8.5 - - """ - exp_vector = self.exponent_vector(element, free_group) - return next((i+1 for i, x in enumerate(exp_vector) if x), len(self.pcgs)+1) - - def leading_exponent(self, element, free_group): - """ - Return the leading non-zero exponent. - - The leading exponent for a given element `g` is defined - by `leading_exponent{g} = e{i}`, if `depth{g} = i`. - - Examples - ======== - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.free_groups import free_group - >>> free_group, x, y = free_group("x, y") - >>> G = SymmetricGroup(3) - >>> PcGroup = G.polycyclic_group() - >>> PcGroup.leading_exponent(G[1], free_group) - 1 - - """ - exp_vector = self.exponent_vector(element, free_group) - depth = self.depth(element, free_group) - if depth != len(self.pcgs)+1: - return exp_vector[depth-1] - return None - - def pc_presentation(self, free_group): - """ - Return the polycyclic presentation. - - There are two types of relations used in polycyclic - presentation. - i) Power relations of the form `x{i}^re{i} = R{i}{i}`, - `for 0 <= i < length(pcgs)` where `x` represents polycyclic - generator and `re` is the corresponding relative order. - - ii) Conjugate relations of the form `x{j}^-1*x{i}*x{j}`, - `for 0 <= j < i <= length(pcgs)`. - - Examples - ======== - >>> from sympy.combinatorics.named_groups import SymmetricGroup - >>> from sympy.combinatorics.permutations import Permutation - >>> from sympy.combinatorics.free_groups import free_group - >>> S = SymmetricGroup(49).sylow_subgroup(7) - >>> der = S.derived_series() - >>> G = der[len(der)-2] - >>> PcGroup = G.polycyclic_group() - >>> pcgs = PcGroup.pcgs - >>> len(pcgs) - 6 - >>> free_group, x0, x1, x2, x3, x4, x5 = free_group("x0, x1, x2, x3, x4, x5") - >>> pc_resentation = PcGroup.pc_presentation(free_group) - >>> free_to_perm = {} - >>> for s, g in zip(free_group.symbols, pcgs): - ... free_to_perm[s] = g - - >>> for k, v in pc_resentation.items(): - ... k_array = k.array_form - ... if v != (): - ... v_array = v.array_form - ... lhs = Permutation() - ... for gen in k_array: - ... s = gen[0] - ... e = gen[1] - ... lhs = lhs*free_to_perm[s]**e - ... if v == (): - ... assert lhs.is_identity - ... continue - ... rhs = Permutation() - ... for gen in v_array: - ... s = gen[0] - ... e = gen[1] - ... rhs = rhs*free_to_perm[s]**e - ... assert lhs == rhs - - """ - rel_order = self.relative_order() - pc_relators = {} - perm_to_free = {} - pcgs = self.pcgs - - for gen, s in zip(pcgs, free_group.generators): - perm_to_free[gen**-1] = s**-1 - perm_to_free[gen] = s - - collector = Collector(pc_relators, rel_order, free_group) - pcgs.reverse() - series = self.pc_series - series.reverse() - collected_gens = [] - - for i, gen in enumerate(pcgs): - re = rel_order[len(rel_order)-i-1] - relation = perm_to_free[gen]**re - G = series[i] if i != 0 else series[i+1] - - l = G.generator_product(gen**re, original = True) - l.reverse() - - word = free_group.identity - for g in l: - word = word*perm_to_free[g] - - word = collector.collected_word(word) - pc_relators[relation] = word if word else () - collector.pc_relators = pc_relators - - collected_gens.append(gen) - if len(collected_gens) > 1: - conj = collected_gens[len(collected_gens)-1] - conjugator = perm_to_free[conj] - - for j in range(len(collected_gens)-1): - conjugated = perm_to_free[collected_gens[j]] - - relation = conjugator**-1*conjugated*conjugator - gens = conj**-1*collected_gens[j]*conj - - l = G.generator_product(gens, original = True) - l.reverse() - word = free_group.identity - for g in l: - word = word*perm_to_free[g] - - word = collector.collected_word(word) - pc_relators[relation] = word if word else () - collector.pc_relators = pc_relators - - series.reverse() - pcgs.reverse() - return pc_relators - class Collector(DefaultPrinting): @@ -261,11 +33,13 @@ class Collector(DefaultPrinting): Section 8.1.3 """ - def __init__(self, pc_relators, relative_order, free_group): - self.pc_relators = pc_relators + def __init__(self, pcgs, pc_series, relative_order, group = None, pc_presentation = None): + self.pcgs = pcgs + self.pc_series = pc_series self.relative_order = relative_order - self.free_group = free_group - self.index = {s: i for i, s in enumerate(free_group.symbols)} + self.free_group = free_group('x:{0}'.format(len(pcgs)))[0] if not group else group + self.index = {s: i for i, s in enumerate(self.free_group.symbols)} + self.pc_presentation = self.pc_relators() def minimal_uncollected_subword(self, word): """ @@ -285,18 +59,15 @@ def minimal_uncollected_subword(self, word): Examples ======== - >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.named_groups import SymmetricGroup >>> from sympy.combinatorics.free_groups import free_group - - Example 8.7 Pg. 281 from [1] + >>> G = SymmetricGroup(4) + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = [2, None] >>> word = x2**2*x1**7 - >>> free_group = word.group - >>> collector = Collector(pc_relators, relative_order, free_group) >>> collector.minimal_uncollected_subword(word) - ((x1, 7),) + ((x2, 2),) """ # To handle the case word = @@ -330,25 +101,20 @@ def relations(self): Examples ======== - >>> from sympy.combinatorics.pc_groups import Collector - >>> from sympy.combinatorics.free_groups import free_group - - >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = [2, None] - >>> word = x2**2*x1**7 - >>> free_group = word.group - >>> collector = Collector(pc_relators, relative_order, free_group) + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> G = SymmetricGroup(3) + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector >>> power_rel, conj_rel = collector.relations() >>> power_rel - {x1**2: 1} + {x0**2: (), x1**3: ()} >>> conj_rel - {x1**-1*x2*x1: x2**-1, x1*x2*x1**-1: x2**-1} + {x0**-1*x1*x0: x1**2} """ power_relators = {} conjugate_relators = {} - for key, value in self.pc_relators.items(): + for key, value in self.pc_presentation.items(): if len(key.array_form) == 1: power_relators[key] = value else: @@ -362,14 +128,13 @@ def subword_index(self, word, w): Examples ======== - >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.named_groups import SymmetricGroup >>> from sympy.combinatorics.free_groups import free_group + >>> G = SymmetricGroup(4) + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : 1, x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = [2, None] >>> word = x2**2*x1**7 - >>> free_group = word.group - >>> collector = Collector(pc_relators, relative_order, free_group) >>> w = x2**2*x1 >>> collector.subword_index(word, w) (0, 3) @@ -399,17 +164,12 @@ def map_relation(self, w): Examples ======== - >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.named_groups import SymmetricGroup >>> from sympy.combinatorics.free_groups import free_group - >>> F, x0, x1, x2 = free_group("x0, x1, x2") - >>> pc_relators = {x0**-1*x1*x0: x1**2, x1**-1*x2*x1: x2, x0**-1*x2*x0: x2*x1} - >>> relative_order = [2, 2, 3] - >>> word = x2**2*x1**7 - >>> free_group = word.group - >>> collector = Collector(pc_relators, relative_order, free_group) - >>> w = x2*x1 - >>> collector.map_relation(w) - x2 + >>> G = SymmetricGroup(3) + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector + >>> F, x0, x1 = free_group("x0, x1") >>> w = x1*x0 >>> collector.map_relation(w) x1**2 @@ -420,7 +180,7 @@ def map_relation(self, w): s2 = array[1][0] key = ((s2, -1), (s1, 1), (s2, 1)) key = self.free_group.dtype(key) - return self.pc_relators[key] + return self.pc_presentation[key] def collected_word(self, word): @@ -434,16 +194,31 @@ def collected_word(self, word): Examples ======== - >>> from sympy.combinatorics.pc_groups import Collector + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.perm_groups import PermutationGroup >>> from sympy.combinatorics.free_groups import free_group - >>> F, x1, x2 = free_group("x1, x2") - >>> pc_relators = {x1**2 : (), x1*x2*x1**-1 : x2**-1, x1**-1*x2*x1 : x2**-1} - >>> relative_order = [2, None] - >>> word = x2**2*x1**7 - >>> free_group = word.group - >>> collector = Collector(pc_relators, relative_order, free_group) - >>> collector.collected_word(word) - x1*x2**-2 + >>> G = SymmetricGroup(4) + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector + >>> F, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") + >>> word = x3*x2*x1*x0 + >>> collected_word = collector.collected_word(word) + >>> free_to_perm = {} + >>> free_group = collector.free_group + >>> for sym, gen in zip(free_group.symbols, collector.pcgs): + ... free_to_perm[sym] = gen + >>> G1 = PermutationGroup() + >>> for w in word: + ... sym = w[0] + ... perm = free_to_perm[sym] + ... G1 = PermutationGroup([perm] + G1.generators) + >>> G2 = PermutationGroup() + >>> for w in collected_word: + ... sym = w[0] + ... perm = free_to_perm[sym] + ... G2 = PermutationGroup([perm] + G2.generators) + >>> G1 == G2 + True """ free_group = self.free_group @@ -476,8 +251,8 @@ def collected_word(self, word): if len(w) == 1 and (e1 < 0 or e1 > re-1): key = ((w[0][0], re), ) key = free_group.dtype(key) - if self.pc_relators[key]: - word_ = ((w[0][0], r), (self.pc_relators[key], q)) + if self.pc_presentation[key]: + word_ = ((w[0][0], r), (self.pc_presentation[key], q)) word_ = free_group.dtype(word_) else: if r != 0: @@ -497,3 +272,224 @@ def collected_word(self, word): word = word.substituted_word(low, high, word_) return word + + + def pc_relators(self): + """ + Return the polycyclic presentation. + + There are two types of relations used in polycyclic + presentation. + i) Power relations of the form `x{i}^re{i} = R{i}{i}`, + `for 0 <= i < length(pcgs)` where `x` represents polycyclic + generator and `re` is the corresponding relative order. + + ii) Conjugate relations of the form `x{j}^-1*x{i}*x{j}`, + `for 0 <= j < i <= length(pcgs)`. + + Examples + ======== + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.permutations import Permutation + >>> S = SymmetricGroup(49).sylow_subgroup(7) + >>> der = S.derived_series() + >>> G = der[len(der)-2] + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector + >>> pcgs = PcGroup.pcgs + >>> len(pcgs) + 6 + >>> free_group = collector.free_group + >>> pc_resentation = collector.pc_presentation + >>> free_to_perm = {} + >>> for s, g in zip(free_group.symbols, pcgs): + ... free_to_perm[s] = g + + >>> for k, v in pc_resentation.items(): + ... k_array = k.array_form + ... if v != (): + ... v_array = v.array_form + ... lhs = Permutation() + ... for gen in k_array: + ... s = gen[0] + ... e = gen[1] + ... lhs = lhs*free_to_perm[s]**e + ... if v == (): + ... assert lhs.is_identity + ... continue + ... rhs = Permutation() + ... for gen in v_array: + ... s = gen[0] + ... e = gen[1] + ... rhs = rhs*free_to_perm[s]**e + ... assert lhs == rhs + + """ + free_group = self.free_group + rel_order = self.relative_order + pc_relators = {} + perm_to_free = {} + pcgs = self.pcgs + + for gen, s in zip(pcgs, free_group.generators): + perm_to_free[gen**-1] = s**-1 + perm_to_free[gen] = s + + pcgs.reverse() + series = self.pc_series + series.reverse() + collected_gens = [] + + for i, gen in enumerate(pcgs): + re = rel_order[len(rel_order)-i-1] + relation = perm_to_free[gen]**re + G = series[i] if i != 0 else series[i+1] + + l = G.generator_product(gen**re, original = True) + l.reverse() + + word = free_group.identity + for g in l: + word = word*perm_to_free[g] + + word = self.collected_word(word) + pc_relators[relation] = word if word else () + self.pc_presentation = pc_relators + + collected_gens.append(gen) + if len(collected_gens) > 1: + conj = collected_gens[len(collected_gens)-1] + conjugator = perm_to_free[conj] + + for j in range(len(collected_gens)-1): + conjugated = perm_to_free[collected_gens[j]] + + relation = conjugator**-1*conjugated*conjugator + gens = conj**-1*collected_gens[j]*conj + + l = G.generator_product(gens, original = True) + l.reverse() + word = free_group.identity + for g in l: + word = word*perm_to_free[g] + + word = self.collected_word(word) + pc_relators[relation] = word if word else () + self.pc_presentation = pc_relators + + series.reverse() + pcgs.reverse() + return pc_relators + + def exponent_vector(self, element): + """ + Return the exponent vector of length equal to the + length of polycyclic generating sequence. + + For a given generator/element `g` of the polycyclic group, + it can be represented as `g = x{1}**e{1}....x{n}**e{n}`, + where `x{i}` represents polycyclic generators and `n` is + the number of generators in the free_group equal to the length + of pcgs. + + Examples + ======== + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> from sympy.combinatorics.permutations import Permutation + >>> G = SymmetricGroup(4) + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector + >>> pcgs = PcGroup.pcgs + >>> collector.exponent_vector(G[0]) + [1, 0, 0, 0] + >>> exp = collector.exponent_vector(G[1]) + >>> g = Permutation() + >>> for i in range(len(exp)): + ... g = g*pcgs[i] if exp[i] else g + >>> assert g == G[1] + + References + ========== + + .. [1] Holt, D., Eick, B., O'Brien, E. + "Handbook of Computational Group Theory" + Section 8.1.1, Definition 8.4 + + """ + free_group = self.free_group + G = PermutationGroup() + for g in self.pcgs: + G = PermutationGroup([g] + G.generators) + gens = G.generator_product(element, original = True) + gens.reverse() + + perm_to_free = {} + for sym, g in zip(free_group.generators, self.pcgs): + perm_to_free[g**-1] = sym**-1 + perm_to_free[g] = sym + w = free_group.identity + for g in gens: + w = w*perm_to_free[g] + + pc_presentation = self.pc_presentation + word = self.collected_word(w) + + index = self.index + exp_vector = [0]*len(free_group) + word = word.array_form + for t in word: + exp_vector[index[t[0]]] = t[1] + return exp_vector + + def depth(self, element): + """ + Return the depth of a given element. + + The depth of a given element `g` is defined by + `dep{g} = i if e{1} = e{2} = ... = e{i-1} = 0` + and `e{i} != 0`, where `e` represents the exponent-vector. + + Examples + ======== + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> G = SymmetricGroup(3) + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector + >>> collector.depth(G[0]) + 2 + >>> collector.depth(G[1]) + 1 + + References + ========== + + .. [1] Holt, D., Eick, B., O'Brien, E. + "Handbook of Computational Group Theory" + Section 8.1.1, Definition 8.5 + + """ + exp_vector = self.exponent_vector(element) + return next((i+1 for i, x in enumerate(exp_vector) if x), len(self.pcgs)+1) + + def leading_exponent(self, element): + """ + Return the leading non-zero exponent. + + The leading exponent for a given element `g` is defined + by `leading_exponent{g} = e{i}`, if `depth{g} = i`. + + Examples + ======== + >>> from sympy.combinatorics.named_groups import SymmetricGroup + >>> G = SymmetricGroup(3) + >>> PcGroup = G.polycyclic_group() + >>> collector = PcGroup.collector + >>> collector.leading_exponent(G[1]) + 1 + + """ + exp_vector = self.exponent_vector(element) + depth = self.depth(element) + if depth != len(self.pcgs)+1: + return exp_vector[depth-1] + return None diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py index c189b1785043..62fe0fd3d8f7 100644 --- a/sympy/combinatorics/perm_groups.py +++ b/sympy/combinatorics/perm_groups.py @@ -4535,7 +4535,11 @@ def polycyclic_group(self): pc_series.insert(0, H) pc_sequence.insert(0, g) - return PolycyclicGroup(pc_sequence, pc_series) + G1 = pc_series[0].order() + G2 = pc_series[1].order() + relative_order.insert(0, G1 // G2) + + return PolycyclicGroup(pc_sequence, pc_series, relative_order, collector = None) def _orbit(degree, generators, alpha, action='tuples'): diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index 30c48450cf2f..e64e53987419 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -1,115 +1,31 @@ from sympy.combinatorics.pc_groups import PolycyclicGroup, Collector from sympy.combinatorics.permutations import Permutation -from sympy.combinatorics.free_groups import free_group from sympy.combinatorics.named_groups import SymmetricGroup -def test_collected_word(): - F, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") - - # Polycyclic relators for SymmetricGroup(4) - pc_relators = { x0**2: (), x1**3: (), x2**2: (), x3**2: (), - x0**-1*x1*x0: x1**2, x0**-1*x2*x0: x2*x3, - x0**-1*x3*x0: x3, x1**-1*x2*x1: x3, - x1**-1*x3*x1: x2*x3, x2**-1*x3*x2: x3 - } - - word = x3*x2*x1*x0 - relative_order = [2, 3, 2, 2] - group = word.group - collector = Collector(pc_relators, relative_order, group) - collected_word_ = collector.collected_word(word) - - assert collected_word_ == x0*x1**2*x2*x3 - - # Polycyclic Generators of SymmetricGroup(4) - x0 = Permutation(0, 1) - x1 = Permutation(0, 1, 2) - x2 = Permutation(0, 2)(1, 3) - x3 = Permutation(0, 1)(2, 3) - - word = x3*x2*x1*x0 - collected_word_ = x0*x1**2*x2*x3 - assert word == collected_word_ - - - - F, x0, x1 = free_group("x0, x1") - # polycyclic relators for Symmetricgroup(3) - pc_relators = {x0**2: (), x1**3: (), x0**-1*x1*x0: x1**2} - relative_order = [2, 3] - group = F - collector = Collector(pc_relators, relative_order, group) - - a = Permutation(0, 1) # x0 - b = Permutation(0, 1, 2) # x1 - - word = x1*x0 - assert collector.collected_word(word) == x0*x1**2 - assert b*a == a*b**2 - - word = x1*x0**2 - assert collector.collected_word(word) == x1 - assert b*a**2 == b - - word = x1**2*x0 - assert collector.collected_word(word) == x0*x1 - assert b**2*a == a*b - - word = x1**4*x0**6 - assert collector.collected_word(word) == x1 - assert b**4*a**6 == b - - word = x0*x1 - # The word is already collected - assert collector.collected_word(word) == x0*x1 - assert a*b == a*b - - word = x0**2*x1 - assert collector.collected_word(word) == x1 - assert a**2*b == b - - word = x0**2*x1**3 - # Handle Identity case - assert collector.collected_word(word) == F.identity - assert a**2*b**3 == Permutation(2) - - word = x1**-2*x0 - assert collector.collected_word(word) == x0*x1**2 - assert b**-2*a == a*b**2 - - def test_pc_presentation(): - F1, x0, x1 = free_group("x0, x1") - F2, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") - F3, x0, x1, x2, x3, x4, x5, x6 = free_group("x0, x1, x2, x3, x4, x5, x6") - - l = [(SymmetricGroup(3), F1), (SymmetricGroup(4), F2), - (SymmetricGroup(9).sylow_subgroup(3), F2), (SymmetricGroup(9).sylow_subgroup(2), F3), - (SymmetricGroup(8).sylow_subgroup(2), F3)] + Groups = [SymmetricGroup(3), SymmetricGroup(4), SymmetricGroup(9).sylow_subgroup(3), + SymmetricGroup(9).sylow_subgroup(2), SymmetricGroup(8).sylow_subgroup(2)] S = SymmetricGroup(125).sylow_subgroup(5) G = S.derived_series()[2] - group, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x16, x17,\ - x18, x19 = free_group("x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12,\ - x13, x14, x15, x16, x17, x18, x19") - l.append((G, group)) + Groups.append(G) G = SymmetricGroup(25).sylow_subgroup(5) - group, x0, x1, x2, x3, x4, x5 = free_group("x0, x1, x2, x3, x4, x5") - l.append((G, group)) + Groups.append(G) S = SymmetricGroup(11**2).sylow_subgroup(11) G = S.derived_series()[2] - group, x0, x1, x2, x3, x4, x5, x6, x7, x8, x9 = free_group("x0, x1, x2, x3, x4, x5, x6, x7, x8, x9") - l.append((G, group)) + Groups.append(G) - for t in l: - pc_group = t[0].polycyclic_group() - pc_presentation = pc_group.pc_presentation(t[1]) + for G in Groups: + PcGroup = G.polycyclic_group() + collector = PcGroup.collector + pc_presentation = collector.pc_presentation - pcgs = pc_group.pcgs + pcgs = PcGroup.pcgs + free_group = collector.free_group free_to_perm = {} - for s, g in zip(t[1].symbols, pcgs): + for s, g in zip(free_group.symbols, pcgs): free_to_perm[s] = g for k, v in pc_presentation.items(): @@ -136,23 +52,21 @@ def test_pc_presentation(): assert lhs == rhs -# def test_exponent_vector(): -# F1, x0, x1 = free_group("x0, x1") -# F2, x0, x1, x2, x3 = free_group("x0, x1, x2, x3") -# F3, x0, x1, x2, x3, x4, x5, x6 = free_group("x0, x1, x2, x3, x4, x5, x6") +def test_exponent_vector(): -# l = [(SymmetricGroup(3), F1), (SymmetricGroup(4), F2), -# (SymmetricGroup(9).sylow_subgroup(3), F2), (SymmetricGroup(9).sylow_subgroup(2), F3), -# (SymmetricGroup(8).sylow_subgroup(2), F3)] + Groups = [SymmetricGroup(3), SymmetricGroup(4), SymmetricGroup(9).sylow_subgroup(3), + SymmetricGroup(9).sylow_subgroup(2), SymmetricGroup(8).sylow_subgroup(2)] -# for t in l: -# PcGroup = t[0].polycyclic_group() -# pcgs = PcGroup.pcgs + for G in Groups: + PcGroup = G.polycyclic_group() + collector = PcGroup.collector -# for gen in t[0].generators: -# exp = PcGroup.exponent_vector(gen, t[1]) -# g = Permutation() -# for i in range(len(exp)): -# g = g*pcgs[i] if exp[i] else g + pcgs = PcGroup.pcgs + free_group = collector.free_group -# assert g == gen + for gen in G.generators: + exp = collector.exponent_vector(gen) + g = Permutation() + for i in range(len(exp)): + g = g*pcgs[i] if exp[i] else g + assert g == gen From 7d9e3e497cd39a857c3b35ffbda99e803ff7b086 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Wed, 24 Jul 2019 21:41:15 +0530 Subject: [PATCH 25/28] minor-fixes --- sympy/combinatorics/pc_groups.py | 16 +++++++--------- sympy/combinatorics/perm_groups.py | 2 +- sympy/combinatorics/tests/test_pc_groups.py | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index de7bf34dd079..af1f424dd9e2 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -9,7 +9,7 @@ class PolycyclicGroup(DefaultPrinting): is_group = True is_solvable = True - def __init__(self, pc_sequence, pc_series, relative_order, collector = None): + def __init__(self, pc_sequence, pc_series, relative_order, collector=None): self.pcgs = pc_sequence self.pc_series = pc_series self.relative_order = relative_order @@ -33,7 +33,7 @@ class Collector(DefaultPrinting): Section 8.1.3 """ - def __init__(self, pcgs, pc_series, relative_order, group = None, pc_presentation = None): + def __init__(self, pcgs, pc_series, relative_order, group=None, pc_presentation=None): self.pcgs = pcgs self.pc_series = pc_series self.relative_order = relative_order @@ -335,15 +335,15 @@ def pc_relators(self): perm_to_free[gen**-1] = s**-1 perm_to_free[gen] = s - pcgs.reverse() - series = self.pc_series - series.reverse() + pcgs = pcgs[::-1] + series = self.pc_series[::-1] + rel_order = rel_order[::-1] collected_gens = [] for i, gen in enumerate(pcgs): - re = rel_order[len(rel_order)-i-1] + re = rel_order[i] relation = perm_to_free[gen]**re - G = series[i] if i != 0 else series[i+1] + G = series[i] l = G.generator_product(gen**re, original = True) l.reverse() @@ -377,8 +377,6 @@ def pc_relators(self): pc_relators[relation] = word if word else () self.pc_presentation = pc_relators - series.reverse() - pcgs.reverse() return pc_relators def exponent_vector(self, element): diff --git a/sympy/combinatorics/perm_groups.py b/sympy/combinatorics/perm_groups.py index 62fe0fd3d8f7..f4e988c7e103 100644 --- a/sympy/combinatorics/perm_groups.py +++ b/sympy/combinatorics/perm_groups.py @@ -4539,7 +4539,7 @@ def polycyclic_group(self): G2 = pc_series[1].order() relative_order.insert(0, G1 // G2) - return PolycyclicGroup(pc_sequence, pc_series, relative_order, collector = None) + return PolycyclicGroup(pc_sequence, pc_series, relative_order, collector=None) def _orbit(degree, generators, alpha, action='tuples'): diff --git a/sympy/combinatorics/tests/test_pc_groups.py b/sympy/combinatorics/tests/test_pc_groups.py index e64e53987419..ff4b9e7dad55 100644 --- a/sympy/combinatorics/tests/test_pc_groups.py +++ b/sympy/combinatorics/tests/test_pc_groups.py @@ -68,5 +68,5 @@ def test_exponent_vector(): exp = collector.exponent_vector(gen) g = Permutation() for i in range(len(exp)): - g = g*pcgs[i] if exp[i] else g + g = g*pcgs[i]**exp[i] if exp[i] else g assert g == gen From d80c0edd1d47d26c744af8256e08008954b1ba12 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Thu, 25 Jul 2019 06:50:57 +0530 Subject: [PATCH 26/28] update-add-doc --- doc/src/modules/combinatorics/index.rst | 1 + doc/src/modules/combinatorics/pc_groups.rst | 12 +++++++++ sympy/combinatorics/pc_groups.py | 29 +++++++++------------ 3 files changed, 26 insertions(+), 16 deletions(-) create mode 100644 doc/src/modules/combinatorics/pc_groups.rst diff --git a/doc/src/modules/combinatorics/index.rst b/doc/src/modules/combinatorics/index.rst index f753a41ae16a..2d70a46a13ee 100644 --- a/doc/src/modules/combinatorics/index.rst +++ b/doc/src/modules/combinatorics/index.rst @@ -23,3 +23,4 @@ Contents testutil.rst tensor_can.rst fp_groups.rst + pc_groups.rst diff --git a/doc/src/modules/combinatorics/pc_groups.rst b/doc/src/modules/combinatorics/pc_groups.rst new file mode 100644 index 000000000000..4114bbebebfd --- /dev/null +++ b/doc/src/modules/combinatorics/pc_groups.rst @@ -0,0 +1,12 @@ +.. _combinatorics-pc_groups: + +Polycyclic Groups +================= + +.. module:: sympy.combinatorics.pc_groups + +.. autoclass:: PolycyclicGroup + :members: + +.. autoclass:: Collector + :members: \ No newline at end of file diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index af1f424dd9e2..4b5a37723862 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -231,24 +231,12 @@ def collected_word(self, word): if low == -1: continue s1, e1 = w[0] - if len(w) == 2 and w[1][1] > 0: - s2, e2 = w[1] - s2 = ((s2, 1), ) - s2 = free_group.dtype(s2) - word_ = self.map_relation(free_group.dtype(w)) - word_ = s2*word_**e1 - word_ = free_group.dtype(word_) - word = word.substituted_word(low, high, word_) - if not self.relative_order[self.index[s1]]: - continue re = self.relative_order[self.index[s1]] q = e1 // re r = e1-q*re - if r < 0 or r > re-1: - continue - if len(w) == 1 and (e1 < 0 or e1 > re-1): + if len(w) == 1: key = ((w[0][0], re), ) key = free_group.dtype(key) if self.pc_presentation[key]: @@ -262,6 +250,15 @@ def collected_word(self, word): word_ = None word = word.eliminate_word(free_group.dtype(w), word_) + if len(w) == 2 and w[1][1] > 0: + s2, e2 = w[1] + s2 = ((s2, 1), ) + s2 = free_group.dtype(s2) + word_ = self.map_relation(free_group.dtype(w)) + word_ = s2*word_**e1 + word_ = free_group.dtype(word_) + word = word.substituted_word(low, high, word_) + elif len(w) == 2 and w[1][1] < 0: s2, e2 = w[1] s2 = ((s2, 1), ) @@ -403,7 +400,7 @@ def exponent_vector(self, element): >>> exp = collector.exponent_vector(G[1]) >>> g = Permutation() >>> for i in range(len(exp)): - ... g = g*pcgs[i] if exp[i] else g + ... g = g*pcgs[i]*exp[i] if exp[i] else g >>> assert g == G[1] References @@ -411,7 +408,7 @@ def exponent_vector(self, element): .. [1] Holt, D., Eick, B., O'Brien, E. "Handbook of Computational Group Theory" - Section 8.1.1, Definition 8.4 + Section 8.1.1, Definition 8.4 """ free_group = self.free_group @@ -463,7 +460,7 @@ def depth(self, element): .. [1] Holt, D., Eick, B., O'Brien, E. "Handbook of Computational Group Theory" - Section 8.1.1, Definition 8.5 + Section 8.1.1, Definition 8.5 """ exp_vector = self.exponent_vector(element) From 4c9ca8ad8508e3c347e8097fd8b4d19d5b8cc673 Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Thu, 25 Jul 2019 06:54:16 +0530 Subject: [PATCH 27/28] update --- sympy/combinatorics/pc_groups.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 4b5a37723862..0a6075a2b277 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -400,7 +400,7 @@ def exponent_vector(self, element): >>> exp = collector.exponent_vector(G[1]) >>> g = Permutation() >>> for i in range(len(exp)): - ... g = g*pcgs[i]*exp[i] if exp[i] else g + ... g = g*pcgs[i]**exp[i] if exp[i] else g >>> assert g == G[1] References From 86b8fe72470e2abf1c4e1e8a7eb95ee6dd9b9d8b Mon Sep 17 00:00:00 2001 From: Divyanshu Date: Thu, 25 Jul 2019 12:12:25 +0530 Subject: [PATCH 28/28] update --- sympy/combinatorics/pc_groups.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sympy/combinatorics/pc_groups.py b/sympy/combinatorics/pc_groups.py index 0a6075a2b277..25507b375fa8 100644 --- a/sympy/combinatorics/pc_groups.py +++ b/sympy/combinatorics/pc_groups.py @@ -230,13 +230,13 @@ def collected_word(self, word): low, high = self.subword_index(word, free_group.dtype(w)) if low == -1: continue - s1, e1 = w[0] - - re = self.relative_order[self.index[s1]] - q = e1 // re - r = e1-q*re + s1, e1 = w[0] if len(w) == 1: + re = self.relative_order[self.index[s1]] + q = e1 // re + r = e1-q*re + key = ((w[0][0], re), ) key = free_group.dtype(key) if self.pc_presentation[key]: