6

Investigating the subgroups of a finitely presented group in SageMath seems to be problematic. Simple questions like is_normal() do not work for them. In this question I am specifically interested in obtaining the elements of such subgroup. They are of type ElementLibGAP, represented as something like <[ [ 1, 1, 2, 1, 3, -1 ] ]|a*b^-1*a^-1*b^-1>. I can convert this to a SageMath form (in terms of the generators) by parsing the string representation, but that seems extremely clumsy. Is there any better way?

A good documentation pointer would also be helpful. I figured out how to interpret the part [ 1, 1, 2, 1, 3, -1 ] of mentioned representation (the elements must be interpreted in pairs, the first element of a pair is the 1-based index of a subgroup generator, the second is the exponent), until I stumbled on an element that was represented as <[ [ 2, -2, 1, -1, 2, 1 ], [ 3, 2 ] ]|b^2*(b^2*a^-1)^2*b^-2>, while the subgroup did not even have 3 generators!

1 Answers1

11

I will describe how these elements of subgroups work in GAP, and how to get a uniform representation. (I wrote a good part of the relevant GAP code.)

Let's start by constructing an example. These weird looking elements arise if we determine the presentation of a subgroup (or try to define a homomorphism on a subgroup):

gap> f:=FreeGroup("a","b");;
gap> g:=f/ParseRelators(f,"a^11,b^11");
<fp group on the generators [ a, b ]>
gap> h:=PSL(2,11);;
gap> q:=GQuotients(g,h);;
gap> u:=PreImage(q[1],SylowSubgroup(h,11));
gap> Index(g,u);
60
gap> iso:=IsomorphismFpGroup(u);;
gap> w:=MappingGeneratorsImages(iso)[1];
[ <[ [ 1, 1 ] ]|a*b*a^-1>, <[ [ 2, 1 ] ]|a^b>, <[ [ 4, 1 ] ]|a^3*b*a>,
  <[ [ 5, 1 ] ]|a^2*b^-1*a^-1*b^-1>, <[ [ 7, 1 ] ]|b*a*b^-1*a^-1*b^-1*a>,
  <[ [ 10, 1 ] ]|((b*a^2)^2)^a>, <[ [ 11, 1 ] ]|(b*a*b^-1*a^-1*b^-1*a)^a>,
  ...

The reason for this weird representation is word lengths: If we continue to write elements as products in the original generators (a and b), there is a risk that these word expressions can get very long (exponentially in the index), clogging up the system.

Thus GAP chooses internally other generators (which are words in the original generators in which it expresses elements. In our examples, we have that w[1] is the first new generator and w[2] the second new ([2,1] means first power of the second new generator). It is possible that some new generators agree with old ones, but that is not guaranteed. What gets printed after the | symbol is the word expression in the original generators, but GAP will stop doing so if this becomes too long. If we multiply:

gap> w[1]^5*w[2];
<[ [ 1, 5, 2, 1 ] ]|a*b^5*a^-1*b^-1*a*b>

we get first generator power 5 times second generator power 1. Forming a 7th power we get:

gap> c:=(w[1]^5*w[2])^7;
<[ [ 1, 5, 2, 1 ], [ 52, 3 ], [ 53, 2, 52, 1 ] ]|(a*b^5*a^-1*b^-1*a*b)^7>

the same initial product w[1]^5*w[2] first. Next there is [52,3]. In this example there are initially 51 new generators defined. 52 then refers to the first entry in the same list, 53 to the second. The word thus is the second entry (which is the first entry cubed) squared thies the second entry -- that's how the 7th power was computed. This way of representing iterated products is called a "straight line program element" representation.

While this makes sense (I hope) in terms of memory use and algorithm efficiency, but seems a mess for processing.

There are therefore interface functions that will give the word expression in terms of the original generators. For this we need to get first the word expression of the group element (UnderlyingElement) of the group element.

The reason for this is that equality of words is different than equality of group elements. In the example given $a^{12}=a$ in the group (which can be a harder calculation), but they are obviously different as words:

gap> a:=g.1;;a^12=a;
[ warnings about trying harder and harder]
true
gap> UnderlyingElement(a^12)=UnderlyingElement(a);
false

We then get the letter representation of this word:

gap> LetterRepAssocWord(UnderlyingElement(c));
[ 1, 2, 2, 2, 2, 2, -1, -2, 1, 2, 1, 2, 2, 2, 2, 2, -1, -2, 1, 2, 1, 2, 2, 2,
  2, 2, -1, -2, 1, 2, 1, 2, 2, 2, 2, 2, -1, -2, 1, 2, 1, 2, 2, 2, 2, 2, -1,
  -2, 1, 2, 1, 2, 2, 2, 2, 2, -1, -2, 1, 2, 1, 2, 2, 2, 2, 2, -1, -2, 1, 2 ]

which is the obvious represenattion as product $a\cdot b\cdot b\cdot b\cdot b\cdot b\cdot a^{-1}\cdot b^{-1}\cdots$.

You could also use EvalStraightLineProgElm to force-convert to a word:

gap> EvalStraightLineProgElm(UnderlyingElement(c));
(a*b^5*a^-1*b^-1*a*b)^7
gap> Length(last);
70

which of course might use significantly more memory.

Ѕᴀᴀᴅ
  • 35,369
ahulpke
  • 20,399