New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Compute map from subgroup to parent group #14968
Changes from 4 commits
9975af2
cdd3cb0
cff86da
570dea4
524f0d1
89fe924
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -118,21 +118,41 @@ def identity(self): | |
def __contains__(self, g): | ||
return g in self.free_group | ||
|
||
def subgroup(self, gens, C=None): | ||
def subgroup(self, gens, C=None, homomorphism=False): | ||
''' | ||
Return the subgroup generated by `gens` using the | ||
Reidemeister-Schreier algorithm | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should have an explanation of |
||
homomorphism -- When set to True, return a dictionary containing the images | ||
of the presentation generators in the original group. | ||
|
||
Examples | ||
======== | ||
>>> from sympy.combinatorics.fp_groups import (FpGroup, FpSubgroup) | ||
>>> from sympy.combinatorics.free_groups import free_group | ||
>>> 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] | ||
>>> K, T = f.subgroup(H, homomorphism=True) | ||
>>> T(K.generators) | ||
[x*y, x**-1*y**2*x**-1] | ||
|
||
''' | ||
|
||
if not all([isinstance(g, FreeGroupElement) for g in gens]): | ||
raise ValueError("Generators must be `FreeGroupElement`s") | ||
if not all([g.group == self.free_group for g in gens]): | ||
raise ValueError("Given generators are not members of the group") | ||
g, rels = reidemeister_presentation(self, gens, C=C) | ||
if homomorphism: | ||
g, rels, _gens = reidemeister_presentation(self, gens, C=C, homomorphism=True) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You end up running |
||
else: | ||
g, rels = reidemeister_presentation(self, gens, C=C) | ||
if g: | ||
g = FpGroup(g[0].group, rels) | ||
else: | ||
g = FpGroup(free_group('')[0], []) | ||
if homomorphism: | ||
from sympy.combinatorics.homomorphisms import homomorphism | ||
return g, homomorphism(g, self, g.generators, _gens, check=False) | ||
return g | ||
|
||
def coset_enumeration(self, H, strategy="relator_based", max_cosets=None, | ||
|
@@ -1068,25 +1088,44 @@ def _simplification_technique_1(rels): | |
############################################################################### | ||
|
||
# Pg 175 [1] | ||
def define_schreier_generators(C): | ||
def define_schreier_generators(C, homomorphism=False): | ||
''' | ||
Arguments: | ||
C -- Coset table. | ||
homomorphism -- When set to True, return a dictionary containing the images | ||
of the presentation generators in the original group. | ||
''' | ||
y = [] | ||
gamma = 1 | ||
f = C.fp_group | ||
X = f.generators | ||
if homomorphism: | ||
# `_gens` stores the elements of the parent group to | ||
# to which the schreier generators correspond to. | ||
_gens = {} | ||
# compute the schreier Traversal | ||
tau = {} | ||
tau[0] = f.identity | ||
C.P = [[None]*len(C.A) for i in range(C.n)] | ||
for alpha, x in product(C.omega, C.A): | ||
beta = C.table[alpha][C.A_dict[x]] | ||
if beta == gamma: | ||
C.P[alpha][C.A_dict[x]] = "<identity>" | ||
C.P[beta][C.A_dict_inv[x]] = "<identity>" | ||
gamma += 1 | ||
if homomorphism: | ||
tau[beta] = tau[alpha]*x | ||
elif x in X and C.P[alpha][C.A_dict[x]] is None: | ||
y_alpha_x = '%s_%s' % (x, alpha) | ||
y.append(y_alpha_x) | ||
C.P[alpha][C.A_dict[x]] = y_alpha_x | ||
if homomorphism: | ||
_gens[y_alpha_x] = tau[alpha]*x*tau[beta]**-1 | ||
grp_gens = list(free_group(', '.join(y))) | ||
C._schreier_free_group = grp_gens.pop(0) | ||
C._schreier_generators = grp_gens | ||
if homomorphism: | ||
C._schreier_gen_elem = _gens | ||
# replace all elements of P by, free group elements | ||
for i, j in product(range(len(C.P)), range(len(C.A))): | ||
# if equals "<identity>", replace by identity element | ||
|
@@ -1209,11 +1248,13 @@ def elimination_technique_2(C): | |
C._schreier_generators = gens | ||
return C._schreier_generators, C._reidemeister_relators | ||
|
||
def reidemeister_presentation(fp_grp, H, C=None): | ||
def reidemeister_presentation(fp_grp, H, C=None, homomorphism=False, subgroup=False): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just noticed that you didn't remove the |
||
""" | ||
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` | ||
homomorphism: When set to True, return a homomorphism from the subgroup | ||
to the parent group | ||
|
||
Examples | ||
======== | ||
|
@@ -1250,14 +1291,22 @@ def reidemeister_presentation(fp_grp, H, C=None): | |
if not C: | ||
C = coset_enumeration_r(fp_grp, H) | ||
C.compress(); C.standardize() | ||
define_schreier_generators(C) | ||
define_schreier_generators(C, homomorphism=homomorphism) | ||
reidemeister_relators(C) | ||
gens, rels = C._schreier_generators, C._reidemeister_relators | ||
gens, rels = simplify_presentation(gens, rels, change_gens=True) | ||
|
||
C.schreier_generators = tuple(gens) | ||
C.reidemeister_relators = tuple(rels) | ||
|
||
if homomorphism: | ||
_gens = [] | ||
C.schreier_gen_elem = {} | ||
for gen in gens: | ||
C.schreier_gen_elem[gen] = C._schreier_gen_elem[str(gen)] | ||
_gens.append(C.schreier_gen_elem[gen]) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That could be removed. Thought I might be useful elsewhere by making it an attribute, but, I don't think it's necessary. |
||
return C.schreier_generators, C.reidemeister_relators, _gens | ||
|
||
return C.schreier_generators, C.reidemeister_relators | ||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -166,15 +166,34 @@ def test_order(): | |
f = FpGroup(free_group('')[0], []) | ||
assert f.order() == 1 | ||
|
||
def _test_subgroup(K, T, S): | ||
_gens = T(K.generators) | ||
for elem in _gens: | ||
assert elem in S | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or you could say |
||
assert T.is_injective() | ||
assert T.image().order() == S.order() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe this sort of thing usually goes inside the unit test for which it is relevant, because it itself isn't an independent test. So this should go after |
||
|
||
def test_fp_subgroup(): | ||
F, x, y = free_group("x, y") | ||
f = FpGroup(F, [x**4, y**2, x*y*x**-1*y]) | ||
S = FpSubgroup(f, [x*y]) | ||
assert (x*y)**-3 in S | ||
K, T = f.subgroup([x*y], homomorphism=True) | ||
assert T(K.generators) == [y*x**-1] | ||
_test_subgroup(K, T, S) | ||
|
||
S = FpSubgroup(F, [x**-1*y*x]) | ||
S = FpSubgroup(f, [x**-1*y*x]) | ||
assert x**-1*y**4*x in S | ||
assert x**-1*y**4*x**2 not in S | ||
K, T = f.subgroup([x**-1*y*x], homomorphism=True) | ||
assert T(K.generators[0]**3) == y**3 | ||
_test_subgroup(K, T, S) | ||
|
||
f = FpGroup(F, [x**3, y**5, (x*y)**2]) | ||
H = [x*y, x**-1*y**-1*x*y*x] | ||
K, T = f.subgroup(H, homomorphism=True) | ||
S = FpSubgroup(f, H) | ||
_test_subgroup(K, T, S) | ||
|
||
def test_permutation_methods(): | ||
from sympy.combinatorics.fp_groups import FpSubgroup | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are not using this anywhere else. I suppose it could be good to have this sort of a function but it can be more efficient: for example, you could start in row
coset
, find the firstx
s.t.gamma < coset
wheregamma = self.table[coset][self.A_dict[x]]
and returncoset_representative(gamma)*x**-1