Skip to content

Conversation

@valglad
Copy link
Contributor

@valglad valglad commented May 20, 2017

This PR implements a method .subgroup() for groups (both permutation and FpGroups) which takes the subgroup generators as the argument and returns another group whose parent is set to be the original group.

  • PermutationGroups: the method simply creates another PermutationGroup on the given generators. The use of .parent in the .is_subgroup() method allows for faster subgroup checking in some cases.

  • FpGroups: reidemeister_presentation() method returns the generators and relators for a presentation of the subgroup. These are instances of FreeGroupElement. To return an FpGroup based on them, it is necessary to create a FreeGroup on the generators first and make sure that the generators of this free group match up with those used in the relators. For this purpose I made it possible to create a FreeGroup by passing instances of FreeGroupElements to it.

The other changes are relatively minor and not directly relevant to the new method:

  • Generators of FpGroup are returned by the property .generators while a method .relators() has to be called for the relators. It feels a bit inconsistent so I made .relators a property.

  • Rearranged things in the file fp_groups.py to have properties all together near the top of the class definition.

  • Corrected a couple of typos, added a missing error message and slightly changed the description for the arguments of the FreeGroup class.

all(isinstance(s, FreeGroupElement) for s in symbols):
return symbols
raise ValueError("")
raise ValueError("The type of `symbols` must be one of the following: "\
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think, having \ at the end wouldn't be necessary (better not to have them).

if isinstance(symbols, string_types):
return _symbols(symbols, seq=True)
elif isinstance(symbols, Expr):
elif isinstance(symbols, Expr) or isinstance(symbols, FreeGroupElement):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A single isinstance should suffice.

'''
if not all([isinstance(g, FreeGroupElement) for g in gens]):
raise ValueError("Generators must be `FreeGroupElement`s")
letters = [g.array_form[0][0] for g in self.generators]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the same as self.symbols?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self is an FpGroup and it doesn't have .symbols. Though self.free_group.symbols is probably the same.

letters = self.free_group.symbols
for g in gens:
c = [t[0] in letters for t in g.array_form]
if not all(c):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Each FreeGroupElement has attribute group. Perhaps we should use the condition g.group == self.free_group.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would be good but that also makes me realise that we can't just make a new FreeGroup on already existing FreeGroupElements because then the generators end up with a wrong .group attribute. Also, the group attribute of the generators returned by reidemester_presentation() is a free group on more generators than necessary. Hmm, I'll have another look at it. Having reidemester_presentation() return generators with the right group attribute and matching relators would probably be better. Alternatively, go through the generators and relators changing their .group attribute when a new FreeGroup is created on them.

raise ValueError("The group doesn't contain the supplied generators")

G = PermutationGroup(gens)
G._parent = self
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For what purpose is this defined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it's not as relevant to permutation groups. For FpGroups it allows to have is_subgroup method, and also this is a set-up GAP has (i.e. having .parent attribute for subgroups of groups). Theoretically having this might save time when someone tries to call is_subgroup on large permutation groups but mainly I was just mimicking what I did for FpGroups here. Should I remove it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could do that.

assert str(p1) == "((y_1, y_2), (y_1**2, y_2**3, y_2*y_1*y_2*y_1*y_2*y_1))"

H = f.subgroup(H)
assert (H.generators == p1[0]) and (H.relators == p1[1])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could probably have (H.generators, H.relators) == p1.


@property
def relators(self):
return tuple(self._relators)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would there be any reason why we couldn't have self.relators instead of self._relators as a plain attribute?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so. And I don't think there is any reason to have self._free_group as well as self.free_group. Should I change both of them?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you could do that. For relators in particular, that seems better than defining a property. But it would probably be preferable to do the changes in a separate PR.

simplify_presentation(C)

syms = [g.array_form[0][0] for g in C._schreier_generators]
g = free_group(syms)[0]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this related to C._schreier_free_group?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a free group on generators that have the same symbols as a subset of C._schreier_free_group's generators.

@jksuom jksuom merged commit 1847ae9 into sympy:master Jun 13, 2017
@valglad valglad deleted the subgroups branch June 13, 2017 21:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants