# ``Reidemeister Schreier`` algorithm implementation in SymPy #11295

Merged
merged 38 commits into from Jul 8, 2016

None yet

### 2 participants

Member
commented Jun 25, 2016
 No description provided.
 gxyd ```Reidemeister Schreier`` algorithm implementation in SymPy` `d76bd03`
added the GSoC label Jun 25, 2016
added some commits Jun 25, 2016
 gxyd `use "symbols" for schreier generators` `2b0d186` gxyd `construct the schreier free group, use "C.Y" as a list` `5bf2ca8` gxyd `Add "is_cyclic_conjugate" method in free group` `aaa4491` gxyd `C.schreier_free_group is now just a free_group not a tuple` `adf6f7c` gxyd `make C.schreier_generators instead of C.Y` `747fd41` gxyd `remove order 1 generators from C.schreier_generators` `4d49bfd` gxyd `removing generators of length 1 from relators` `b2a3f44`
commented on an outdated diff Jun 27, 2016
sympy/combinatorics/fp_groups.py
 + for i, j in product(range(len(C.P)), range(len(C.A))): + # if equals "", replace by identity element + if C.P[i][j] == "": + C.P[i][j] = C.schreier_free_group.identity + elif isinstance(C.P[i][j], str): + r = C.schreier_generators[y.index(C.P[i][j])] + C.P[i][j] = r + beta = C.table[i][j] + C.P[beta][j + 1] = r**-1 + + +def reidemeister_relators(C): + R = C.fp_group.relators() + rels = set([rewrite(C, coset, word) for word in R for coset in range(C.n)]) + identity = C.schreier_free_group.identity + order_1_gens = set([i for i in rels if len(i) == 1])
 gxyd Jun 27, 2016 • edited Member I am not sure, whether simplifications like these should be kept just here or moved under the `Tietze transformation program` since I have haven't studied the "presentation by generators", if "presentation by generators" also expects such simplification then we move this out.
Member
commented Jun 27, 2016
 I believe we should start coding the "presentation on user generators", since coding that would give an idea about which "simplification techniques" are common between them.
added some commits Jun 27, 2016
 gxyd `Add routines for "Computing a presentation on the user generators"` `4dde74f` gxyd `improve "presentations", add "generator_exponent_sum"` `bc8f2ed`
sympy/combinatorics/fp_groups.py
 + x_i = w[i] + v = v*C.P[alpha][C.A_dict[x_i]] + alpha = C.table[alpha][C.A_dict[x_i]] + return v + + +# routines for modified Todd Coxeter procedure +# Section 5.3.2 from [1] +def modified_define(C, alpha, x): + """ + see also + ======== + CosetTable.define + + """ + C.define(alpha, x)
 jksuom Member Perhaps `define` could return `beta`.
sympy/combinatorics/fp_groups.py
 + + """ + table = C.table + f = alpha + # f_p and b_p are words with coset numbers "f" and "b". + # f_p when we scan forward from beginning, while b_p when we scan backward + # from end. + f_p = "" + b_p = y + i = 0 + r = len(word) + b = alpha + j = r - 1 + while i <= j and table[f][C.A_dict[word[i]]] is not None: + f_p = f_p*C.P[f][C.A_dict[word[i]]] + i += 1
 jksuom Member Next `f` should also be assigned before this.
added some commits Jun 28, 2016
 gxyd `improve the "eliminate_word", since it is to be used in "presentations"` `7d911c0` gxyd `fix "eliminate_word", add tests for it in free_group` `e839633` gxyd `Add "contains_generators" method in free group` `also change "while" loop to "for i in range(len(rels) - 1, -1, -1)"` `7595df6`
sympy/combinatorics/fp_groups.py
 - while i < len(rels): - j = 0 - while j < len(gens): - if rels[i].generator_exponent_sum(gen) == 1: - gen_index = rels[i].index(gen) - bk = rels[i].subword(gen_index + 1) - fw = rels[i].subword(0, gen_index) - redundant_gen[rels[i]] = (bk*fw)**-1 - del rels[i]; gens.remove(gen) - j += 1 - i += 1 + redundant_gens = {} + # examine each relator in relator list for any generator occuring exactly + # once + l1, l2 = len(rels), len(gens) + for i, j in product(range(l1 -1, -1, -1), range(l2 - 1, -1, -1)):
 jksuom Member The loop over `j` should probably be initiated anew for each round of the `i`-loop since `len(gens)` may decrease. The product may be too big.
 gxyd `Add doctest for "reidemeister schreier" procedure` `ab6aeaf`
commented on an outdated diff Jun 29, 2016
sympy/combinatorics/fp_groups.py
 + + +def reidemeister_presentation(fp_grp, H): + """ + fp_group: A finitely presented group, an instance of FpGroup + H: A subgroup whose presentation is to be found, given as a list + of words in generators of `fp_grp` + + Examples + ======== + + >>> from sympy.combinatorics.free_group import free_group, FpGroup + >>> F, x, y = free_group("x, y") + >>> f = FpGroup(F, [x**3, y**5, (x*y)**2]) + >>> H = [x*y, x**-1*y**-1*x*y*x] + >>> reidemeister_relators(f, H)
 gxyd Member Will change this to `reidemeister_presentation` (left by mistake)
added some commits Jun 29, 2016
 gxyd `fixes in elimination technique 1` `b7a0347` gxyd `fix issue in "elimination_technique_1"` `aff64cd` gxyd `Fix doctests` `5419249` gxyd `Refactor the code` `36397ff` gxyd `change the order of using the generators from ``gens``` `3e1fdb9`
commented on an outdated diff Jun 30, 2016
sympy/combinatorics/fp_groups.py
 + >>> H = [x] + >>> reidemeister_presentation(f, H) + ([x_1], [x_1**-4, x_1**-8]) + + >>> f = FpGroup(F, [x**3*y**-3, (x*y)**3, (x*y**-1)**2]) + >>> H = [x] + >>> reidemeister_presentation(f, H) + ([x_0], [, x_0**-4, x_0**6, x_0**-12]) + + """ + C = coset_enumeration_r(fp_grp, H) + C.compress(); C.standardize() + define_schreier_generators(C) + reidemeister_relators(C) + elimination_technique_1(C) + elimination_technique_1(C)
 gxyd Member Perhaps the number of times, any elimination technique should be used is better answered in the Handbook. Will see that later on.
 gxyd `Add "is_independent" method in free_group` `bb2622c`
and 1 other commented on an outdated diff Jun 30, 2016
sympy/combinatorics/fp_groups.py
 + + >>> f = FpGroup(F, [x**3, y**3, (x*y)**3]) + >>> H = [x*y, x*y**-1] + >>> reidemeister_presentation(f, H) + ([x_0, y_0], [x_0**3, y_0**3, x_0*y_0*x_0*y_0*x_0*y_0]) + + # Exercises Q2. Pg 187 from [1] + >>> f = FpGroup(F, [x**2*y**2, y**-1*x*y*x**-3]) + >>> H = [x] + >>> reidemeister_presentation(f, H) + ([x_1], [x_1**-4, x_1**-8]) + + >>> f = FpGroup(F, [x**3*y**-3, (x*y)**3, (x*y**-1)**2]) + >>> H = [x] + >>> reidemeister_presentation(f, H) + ([x_0], [, x_0**-4, x_0**6, x_0**-12])
 gxyd Member This occurs, since of the presence of "removal of dependent relator" in the relator list, I don't exactly understand the definition of "independent relators", I have only have an idea about it (since no official definition, AFAIK). jksuom Member Where are those 'dependent relators' mentioned? gxyd Member In the Havas paper, in the elimination technique 1, second paragraph beginning in brackets. This line (provided the relator is independent of previously discovered but not yet eliminated redundant generators). Sorry for late reply, I somehow skipped the mail. jksuom Member I think that 'independent' simply means that the relator does not involve those generators that have already been marked redundant.
and 1 other commented on an outdated diff Jun 30, 2016
sympy/combinatorics/free_group.py
 @@ -806,6 +831,58 @@ def subword(self, from_i, to_j): array_form = letter_form_to_array_form(letter_form, group) return group.dtype(array_form) + def is_independent(self, word):
 gxyd Member This method is to be used to eliminate a few of the "relators" from the relator list. But I am not sure, about at what stage should this be used in the `elimination_technique_1`. gxyd Member Perhaps this is the line from Havas. From second paragraph beginning (`elimination_technique_1`) provided the relator is independent of previously discovered but not yet eliminated redundant generators). I am not fully sure that I even understood its meaning correctly. jksuom Member It seems that the relators are scanned and generators that appear only once are marked redundant. Those relators that contain such generators are skipped over. After all relators have been scanned redundant generators are replaced in the relators by their representations in terms of other generators and deleted from the list. Then the process can be repeated. (Relators containing generators marked redundant cannot be used to decide if a generator is redundant. Even if it appears only once, it may reappear when another generator is replaced.) gxyd Member That is fine. I think I already understood everything you mentioned in your comment, (even your comment has been executed in code successfully). But the problem is about the removal of `independent` relator from the relator list. See for example see this doctest example https://github.com/sympy/sympy/pull/11295/files#diff-2bca05fcc1cad2237c5589d92187d25cR1288 . Did I missed something? On 06/30/2016 06:04 PM, Kalevi Suominen wrote: In sympy/combinatorics/free_group.py #11295 (comment): @@ -806,6 +831,58 @@ def subword(self, from_i, to_j): array_form = letter_form_to_array_form(letter_form, group) return group.dtype(array_form) def is_independent(self, word): It seems that the relators are scanned and generators that appear only once are marked redundant. Those relators that contain such generators are skipped over. After all relators have been scanned redundant generators are replaced in the relators by their representations in terms of other generators and deleted from the list. Then the process can be repeated. (Relators containing generators marked redundant cannot be used to decide if a generator is redundant. Even if it appears only once, it may reappear when another generator is replaced.) — You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/sympy/sympy/pull/11295/files/bb2622cc696920381b63f2d02e4a5e928dc221e5#r69123414, or mute the thread https://github.com/notifications/unsubscribe/AHWt8f3hKesJ76PVUKgenMpwxGtQN7jSks5qQ7fmgaJpZM4I-QZs.
 gxyd `Add simplification technique 1 for presnetations from Havas paper` `74f91fb`
commented on an outdated diff Jun 30, 2016
sympy/combinatorics/fp_groups.py
 + redundant_gens[gen] = (bk*fw)**(-1*k) + contained_gens.extend((bk*fw).contains_generators()) + del rels[i]; del gens[j] + break + j -= 1 + # eliminate the redundant generator from remaing relators + for i, gen in product(range(len(rels)), redundant_gens): + rels[i] = rels[i].eliminate_word(gen, redundant_gens[gen]) + rels.sort() + C.reidemeister_relators = rels + C.schreier_generators = gens + +def elimination_technique_2(C): + pass + +def simplification_technique_1(C):
 gxyd Member Will probably see a few things for this, in the morning. It wouldn't be hard to implement this technique.
 gxyd `simplification technique for presentations fixed` `19ba329`
Member
commented Jul 1, 2016
 I think the `simplifiication technique` mentioned in Havas paper section 2.6.1 is fine now. I have added the doctests as well.
added some commits Jul 1, 2016
 gxyd `Fix the equality bug in "simplification technique"` `eb79cdf` gxyd `Add elimination technique 2 from Havas paper` `b04c866`
commented on an outdated diff Jul 2, 2016
sympy/combinatorics/fp_groups.py
 +# Pg 350, section 2.5.1 from [2] +def elimination_technique_1(C): + rels = list(C.reidemeister_relators) + # the shorter relators are examined first so that generators selected for + # elimination will have shorter strings as equivalent + rels.sort(reverse=True) + gens = list(C.schreier_generators) + redundant_gens = {} + contained_gens = [] + # examine each relator in relator list for any generator occuring exactly + # once + next_i = False + for i in range(len(rels) -1, -1, -1): + rel = rels[i] + for gen in redundant_gens: + if any([gen.array_form[0][0] == r[0] for r in rel.array_form]):
 jksuom Member This is a loop. It would be better to assign `gen.array_form[0][0]` to a local variable.
commented on an outdated diff Jul 2, 2016
sympy/combinatorics/free_group.py
 if len(gen) != 1: raise ValueError("gen must be a generator or inverse of a generator") - n = 0 - g = abs(gen[0]) - for i in w: - if i == g: - n = n + 1 - elif i == -g: - n = n - 1 - - if gen[0] < 0: - n = -n - return n + s = gen.array_form[0] + return s[1]*sum([i[1] for i in self.array_form if i[0] == s[0]]) + + def generator_exponent_sum(self, gen):
 jksuom Member This name may be misleading since there are also negative exponents. Maybe something like `generator_count` (with no reference to addition) would be better. It could be implicitly interpreted as 'count with multiplicities'.
commented on an outdated diff Jul 2, 2016
sympy/combinatorics/free_group.py
 - if i == g: - n = n + 1 - elif i == -g: - n = n - 1 - - if gen[0] < 0: - n = -n - return n + s = gen.array_form[0] + return s[1]*sum([i[1] for i in self.array_form if i[0] == s[0]]) + + def generator_exponent_sum(self, gen): + """ + For an associative word `self` and a generator `gen`, + ``generator_exponent_sum`` returns the number of times `gen` or + `gen**-1` appears in `self` If both `gen` and its inverse do not
 jksuom Member Perhaps "If neither `gen` nor its inverse"
commented on an outdated diff Jul 2, 2016
sympy/combinatorics/free_group.py
 @@ -753,31 +756,56 @@ def __ge__(self, other): def exponent_sum_word(self, gen):
 jksuom Member Could this be just `exponent_sum`?
commented on an outdated diff Jul 2, 2016
sympy/combinatorics/fp_groups.py
 + redundant_gens = {} + contained_gens = [] + # examine each relator in relator list for any generator occuring exactly + # once + next_i = False + for i in range(len(rels) -1, -1, -1): + rel = rels[i] + for gen in redundant_gens: + if any([gen.array_form[0][0] == r[0] for r in rel.array_form]): + next_i = True + break + if next_i: + next_i = False + continue + j = len(gens) - 1 + while j >= 0:
 jksuom Member Could this also be a for loop with a backwards range?
 gxyd `Improve the methods of FreeGroupElement, fix "presentation"` `36fc9b2`
Member
commented Jul 2, 2016
 Seems like, github is not updating my commit that I have pushed successfully (neither showing the commit, nor the related changes). ``````gaurav@stallman:~/Public/sympy\$ git push github implementation_reidemeister_schreier Username for 'https://github.com': gxyd Password for 'https://gxyd@github.com': Everything up-to-date `````` Though the suggested changes were all valid, I have made those (atleast locally).
Member
commented Jul 2, 2016
 There is a short queue, but that should not cause the problem. Perhaps you could wait until the next commit. (Or else close and reopen.)
added some commits Jul 2, 2016
 gxyd `Fix reidemeister_relators` `c3b14de` gxyd `fix the bug in "eliminate_word"` `10e1ff1`
Member
commented Jul 2, 2016 edited
 Now the following things need to be done: Removing the "dependent relators". For example ``````>>> f = FpGroup(F, [x**3*y**-3, (x*y)**3, (x*y**-1)**2]); H = [x] >>> reidemeister_presentation(f, H) ({x_0}, {, x_0**6, x_0**18}) `````` Relator list should shouldn't contain `x_0**18`. (I mentioned this in blog as well) 2. Removal of `-ve` sign from the single syllable relators. For example ``````>>> f = FpGroup(F, [x**3, y**5, (x*y)**2]); H = [x*y, x**-1*y**-1*x*y*x] >>> reidemeister_presentation(f, H) ({y_1, y_2}, {y_1**2, y_2**-3, y_2*y_1*y_2*y_1*y_2*y_1}) `````` Change `y_2**-3` -> `y_2**3` in relator list. 3. Add tests. 4. Obviously to speed things up. Just a TODO list for myself, not for you. :)
 gxyd `Add "is_dependent" method in FreeGroupElement` `366fe05`
closed this Jul 3, 2016
reopened this Jul 3, 2016
added some commits Jul 3, 2016
 gxyd `changes in API` `5efc899` gxyd `No use of "is_dependent" rather "simplification" does the work` `eafcc90`
Member
commented Jul 3, 2016
 I think, perhaps there seems to be no use of "is_dependent". That functioning is infact to be done by `_simplification_technique_1`.
 gxyd `Add comments for "elimination technique"` `1327a1b`
and 1 other commented on an outdated diff Jul 4, 2016
sympy/combinatorics/fp_groups.py
 + >>> from sympy.combinatorics.free_group import free_group + >>> from sympy.combinatorics.fp_groups import FpGroup, coset_enumeration_r, reidemeister_relators, define_schreier_generators, elimination_technique_2 + >>> F, x, y = free_group("x, y") + >>> f = FpGroup(F, [x**3, y**5, (x*y)**2]); H = [x*y, x**-1*y**-1*x*y*x] + >>> C = coset_enumeration_r(f, H) + >>> C.compress(); C.standardize() + >>> define_schreier_generators(C) + >>> reidemeister_relators(C) + >>> elimination_technique_2(C) + ([y_1, y_2], [y_2**-3, y_2*y_1*y_2*y_1*y_2*y_1, y_1**2]) + + """ + rels = list(C.reidemeister_relators) + rels.sort(reverse=True) + gens = list(C.schreier_generators) + for i in range(len(gens) - 1, -1, -1):
 jksuom Member Maybe `len(rels) - 1`? gxyd Member Yes. On 07/04/2016 10:27 AM, Kalevi Suominen wrote: In sympy/combinatorics/fp_groups.py #11295 (comment): from sympy.combinatorics.free_group import free_group from sympy.combinatorics.fp_groups import FpGroup, coset_enumeration_r, reidemeister_relators, define_schreier_generators, elimination_technique_2 F, x, y = free_group("x, y") f = FpGroup(F, [x3, y5, (x_y)__2]); H = [x_y, x**-1y**-1_x_yx] C = coset_enumeration_r(f, H) C.compress(); C.standardize() define_schreier_generators(C) reidemeister_relators(C) elimination_technique_2(C) ([y_1, y_2], [y_2**-3, y_2_y_1_y_2_y_1_y_2*y_1, y_1**2]) """ rels = list(C.reidemeister_relators) rels.sort(reverse=True) gens = list(C.schreier_generators) for i in range(len(gens) - 1, -1, -1): Maybe |len(rels) - 1|? — You are receiving this because you modified the open/close state. Reply to this email directly, view it on GitHub https://github.com/sympy/sympy/pull/11295/files/1327a1b039a327c531e305bc3663f54362dd9c90#r69408413, or mute the thread https://github.com/notifications/unsubscribe/AHWt8bHZu1Ch3b70raGsuvmq62bJAqt9ks5qSJKmgaJpZM4I-QZs.
added some commits Jul 4, 2016
 gxyd `time improvements` `82833fc` gxyd `"simplify_presentation", used for simplification` `ad7e9f4` gxyd `fix bug, Add tests` `880438b` gxyd `introduce "cyclic_reduction" for elements` `d397249`
Member
commented Jul 6, 2016
 With the latest code, one example is giving a wrong presentation. I have a made a gist file giving output at different stages. The correct output should be a presentation of the form `a^11, b^2, (a*b)^3, (a^4*b*a^-5*b)^2`. (this is the presentation of PSL_2(11) group, given in Implementation and Analysis of the Todd-Coxeter Algorithm paper)
Member
commented Jul 6, 2016
 Can you please into the current issue. Though I am trying, though I have liked to mention one line mentioned as an assumption in both "Handbook" and "Havas paper" We shall assume that all relators are always cyclically reduced; that is, that whenever a relator is changed, it is immediately replaced by its cyclic reduction. Also, even the latest commit introduced a bug for the "doctest" example.
added some commits Jul 7, 2016
 gxyd `fix bugs in "identity_cyclic_reduction"` `dee10d5` gxyd `Add test of group PSL_2(11) from Havas paper` `e965ee4` gxyd `Remove the code for Presentation on Subgroup generators` `e52cdae`
Member
commented Jul 7, 2016
 I think this is now ready for review.
Member
commented Jul 7, 2016
 Looks good. I'll take some more time with the details tomorrow.
changed the title from [WIP] ``Reidemeister Schreier`` algorithm implementation in SymPy to ``Reidemeister Schreier`` algorithm implementation in SymPy Jul 8, 2016
Member
commented Jul 8, 2016
 This is ready to be merged.
merged commit `040151c` into sympy:master Jul 8, 2016

#### 1 check passed

continuous-integration/travis-ci/pr The Travis CI build passed
Details
deleted the gxyd:implementation_reidemeister_schreier branch Jul 8, 2016
restored the gxyd:implementation_reidemeister_schreier branch Aug 16, 2016
deleted the gxyd:implementation_reidemeister_schreier branch Aug 24, 2016
referenced this pull request in diofant/diofant Dec 7, 2016
Open