Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/finitely_presented.py
8814 views
1
"""
2
Finitely Presented Groups
3
4
Finitely presented groups are constructed as quotients of
5
:mod:`~sage.groups.free_group`::
6
7
sage: F.<a,b,c> = FreeGroup()
8
sage: G = F / [a^2, b^2, c^2, a*b*c*a*b*c]
9
sage: G
10
Finitely presented group < a, b, c | a^2, b^2, c^2, (a*b*c)^2 >
11
12
One can create their elements by mutiplying the generators or by
13
specifying a Tietze list (see
14
:meth:`~sage.groups.finitely_presented.FinitelyPresentedGroupElement.Tietze`)
15
as in the case of free groups::
16
17
sage: G.gen(0) * G.gen(1)
18
a*b
19
sage: G([1,2,-1])
20
a*b*a^-1
21
sage: a.parent()
22
Free Group on generators {a, b, c}
23
sage: G.inject_variables()
24
Defining a, b, c
25
sage: a.parent()
26
Finitely presented group < a, b, c | a^2, b^2, c^2, (a*b*c)^2 >
27
28
Notice that, even if they are represented in the same way, the
29
elements of a finitely presented group and the elements of the
30
corresponding free group are not the same thing. However, they can be
31
converted from one parent to the other::
32
33
sage: F.<a,b,c> = FreeGroup()
34
sage: G = F / [a^2,b^2,c^2,a*b*c*a*b*c]
35
sage: F([1])
36
a
37
sage: G([1])
38
a
39
sage: F([1]) is G([1])
40
False
41
sage: F([1]) == G([1])
42
False
43
sage: G(a*b/c)
44
a*b*c^-1
45
sage: F(G(a*b/c))
46
a*b*c^-1
47
48
Finitely presented groups are implemented via GAP. You can use the
49
:meth:`~sage.groups.libgap_wrapper.ParentLibGAP.gap` method to access
50
the underlying LibGAP object::
51
52
sage: G = FreeGroup(2)
53
sage: G.inject_variables()
54
Defining x0, x1
55
sage: H = G / (x0^2, (x0*x1)^2, x1^2)
56
sage: H.gap()
57
<fp group on the generators [ x0, x1 ]>
58
59
This can be useful, for example, to use GAP functions that are not yet
60
wrapped in Sage::
61
62
sage: H.gap().LowerCentralSeries()
63
[ Group(<fp, no generators known>), Group(<fp, no generators known>) ]
64
65
The same holds for the group elements::
66
67
sage: G = FreeGroup(2)
68
sage: H = G / (G([1, 1]), G([2, 2, 2]), G([1, 2, -1, -2])); H
69
Finitely presented group < x0, x1 | x0^2, x1^3, x0*x1*x0^-1*x1^-1 >
70
sage: a = H([1])
71
sage: a
72
x0
73
sage: a.gap()
74
x0
75
sage: a.gap().Order()
76
2
77
sage: type(_) # note that the above output is not a Sage integer
78
<type 'sage.libs.gap.element.GapElement_Integer'>
79
80
You can use call syntax to replace the generators with a set of
81
arbitrary ring elements. For example, take the free abelian group
82
obtained by modding out the commutator subgroup of the free group::
83
84
sage: G = FreeGroup(2)
85
sage: G_ab = G / [G([1, 2, -1, -2])]; G_ab
86
Finitely presented group < x0, x1 | x0*x1*x0^-1*x1^-1 >
87
sage: a,b = G_ab.gens()
88
sage: g = a * b
89
sage: M1 = matrix([[1,0],[0,2]])
90
sage: M2 = matrix([[0,1],[1,0]])
91
sage: g(3, 5)
92
15
93
sage: g(M1, M1)
94
[1 0]
95
[0 4]
96
sage: M1*M2 == M2*M1 # matrices do not commute
97
False
98
sage: g(M1, M2)
99
Traceback (most recent call last):
100
...
101
ValueError: the values do not satisfy all relations of the group
102
103
.. WARNING::
104
105
Some methods are not guaranteed to finish since the word problem
106
for finitely presented groups is, in general, undecidable. In
107
those cases the process may run unil the available memory is
108
exhausted.
109
110
REFERENCES:
111
112
- :wikipedia:`Presentation_of_a_group`
113
114
- :wikipedia:`Word_problem_for_groups`
115
116
AUTHOR:
117
118
- Miguel Angel Marco Buzunariz
119
"""
120
121
##############################################################################
122
# Copyright (C) 2012 Miguel Angel Marco Buzunariz <[email protected]>
123
#
124
# Distributed under the terms of the GNU General Public License (GPL)
125
#
126
# The full text of the GPL is available at:
127
#
128
# http://www.gnu.org/licenses/
129
##############################################################################
130
131
132
from sage.groups.group import Group
133
from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP
134
from sage.groups.libgap_mixin import GroupMixinLibGAP
135
from sage.structure.unique_representation import UniqueRepresentation
136
from sage.libs.gap.libgap import libgap
137
from sage.libs.gap.element import GapElement
138
from sage.rings.integer import Integer
139
from sage.rings.integer_ring import IntegerRing
140
from sage.misc.cachefunc import cached_method
141
from sage.groups.free_group import FreeGroupElement
142
143
from sage.structure.element import Element, MultiplicativeGroupElement
144
from sage.interfaces.gap import gap
145
from sage.rings.integer import Integer
146
from sage.rings.integer_ring import IntegerRing
147
from sage.functions.generalized import sign
148
from sage.matrix.constructor import matrix
149
150
class FinitelyPresentedGroupElement(FreeGroupElement):
151
"""
152
A wrapper of GAP's Finitely Presented Group elements.
153
154
The elements are created by passing the Tietze list that determines them.
155
156
EXAMPLES::
157
158
sage: G = FreeGroup('a, b')
159
sage: H = G / [G([1]), G([2, 2, 2])]
160
sage: H([1, 2, 1, -1])
161
a*b
162
sage: H([1, 2, 1, -2])
163
a*b*a*b^-1
164
sage: x = H([1, 2, -1, -2])
165
sage: x
166
a*b*a^-1*b^-1
167
sage: y = H([2, 2, 2, 1, -2, -2, -2])
168
sage: y
169
b^3*a*b^-3
170
sage: x*y
171
a*b*a^-1*b^2*a*b^-3
172
sage: x^(-1)
173
b*a*b^-1*a^-1
174
"""
175
176
def __init__(self, parent, x, check=True):
177
"""
178
The Python constructor.
179
180
See :class:`FinitelyPresentedGroupElement` for details.
181
182
TESTS::
183
184
sage: G = FreeGroup('a, b')
185
sage: H = G / [G([1]), G([2, 2, 2])]
186
sage: H([1, 2, 1, -1])
187
a*b
188
189
sage: TestSuite(G).run()
190
sage: TestSuite(H).run()
191
192
sage: G.<a,b> = FreeGroup()
193
sage: H = G / (G([1]), G([2, 2, 2]))
194
sage: x = H([1, 2, -1, -2])
195
sage: TestSuite(x).run()
196
sage: TestSuite(G.one()).run()
197
"""
198
if not isinstance(x, GapElement):
199
F = parent.free_group()
200
free_element = F(x)
201
fp_family = parent.one().gap().FamilyObj()
202
x = libgap.ElementOfFpGroup(fp_family, free_element.gap())
203
ElementLibGAP.__init__(self, parent, x)
204
205
def __reduce__(self):
206
"""
207
Used in pickling.
208
209
TESTS::
210
211
sage: F.<a,b> = FreeGroup()
212
sage: G = F / [a*b, a^2]
213
sage: G.inject_variables()
214
Defining a, b
215
sage: a.__reduce__()
216
(Finitely presented group < a, b | a*b, a^2 >, ((1,),))
217
sage: (a*b*a^-1).__reduce__()
218
(Finitely presented group < a, b | a*b, a^2 >, ((1, 2, -1),))
219
220
sage: F.<a,b,c> = FreeGroup('a, b, c')
221
sage: G = F.quotient([a*b*c/(b*c*a), a*b*c/(c*a*b)])
222
sage: G.__reduce__()
223
(<class 'sage.groups.finitely_presented.FinitelyPresentedGroup'>,
224
(Free Group on generators {a, b, c},
225
(a*b*c*a^-1*c^-1*b^-1, a*b*c*b^-1*a^-1*c^-1)))
226
sage: G.inject_variables()
227
Defining a, b, c
228
sage: x = a*b*c
229
sage: x.__reduce__()
230
(Finitely presented group < a, b, c | a*b*c*a^-1*c^-1*b^-1, a*b*c*b^-1*a^-1*c^-1 >,
231
((1, 2, 3),))
232
"""
233
return (self.parent(), tuple([self.Tietze()]))
234
235
def _repr_(self):
236
"""
237
Return a string representation.
238
239
OUTPUT:
240
241
String.
242
243
EXAMPLES::
244
245
sage: G.<a,b> = FreeGroup()
246
sage: H = G / [a^2, b^3]
247
sage: H.gen(0)
248
a
249
sage: H.gen(0)._repr_()
250
'a'
251
sage: H.one()
252
1
253
"""
254
# computing that an element is actually one can be very expensive
255
if self.Tietze() == ():
256
return '1'
257
else:
258
return self.gap()._repr_()
259
260
@cached_method
261
def Tietze(self):
262
"""
263
Return the Tietze list of the element.
264
265
The Tietze list of a word is a list of integers that represent
266
the letters in the word. A positive integer `i` represents
267
the letter corresponding to the `i`-th generator of the group.
268
Negative integers represent the inverses of generators.
269
270
OUTPUT:
271
272
A tuple of integers.
273
274
EXAMPLES::
275
276
sage: G = FreeGroup('a, b')
277
sage: H = G / (G([1]), G([2, 2, 2]))
278
sage: H.inject_variables()
279
Defining a, b
280
sage: a.Tietze()
281
(1,)
282
sage: x = a^2*b^(-3)*a^(-2)
283
sage: x.Tietze()
284
(1, 1, -2, -2, -2, -1, -1)
285
"""
286
tl = self.gap().UnderlyingElement().TietzeWordAbstractWord()
287
return tuple(tl.sage())
288
289
def __call__(self, *values, **kwds):
290
"""
291
Replace the generators of the free group with ``values``.
292
293
INPUT:
294
295
- ``*values`` -- a list/tuple/iterable of the same length as
296
the number of generators.
297
298
- ``check=True`` -- boolean keyword (default:
299
``True``). Whether to verify that ``values`` satisfy the
300
relations in the finitely presented group.
301
302
OUTPUT:
303
304
The product of ``values`` in the order and with exponents
305
specified by ``self``.
306
307
EXAMPLES::
308
309
sage: G.<a,b> = FreeGroup()
310
sage: H = G / [a/b]; H
311
Finitely presented group < a, b | a*b^-1 >
312
sage: H.simplified()
313
Finitely presented group < a | >
314
315
The generator `b` can be eliminated using the relation `a=b`. Any
316
values that you plug into a word must satisfy this relation::
317
318
sage: A, B = H.gens()
319
sage: w = A^2 * B
320
sage: w(2,2)
321
8
322
sage: w(3,3)
323
27
324
sage: w(1,2)
325
Traceback (most recent call last):
326
...
327
ValueError: the values do not satisfy all relations of the group
328
sage: w(1, 2, check=False) # result depends on presentation of the group element
329
2
330
"""
331
values = list(values)
332
if kwds.get('check', True):
333
for rel in self.parent().relations():
334
rel = rel(values)
335
if rel != 1:
336
raise ValueError('the values do not satisfy all relations of the group')
337
return super(FinitelyPresentedGroupElement, self).__call__(values)
338
339
340
def wrap_FpGroup(libgap_fpgroup):
341
"""
342
Wrap a GAP finitely presented group.
343
344
This function changes the comparison method of
345
``libgap_free_group`` to comparison by Python ``id``. If you want
346
to put the LibGAP free group into a container ``(set, dict)`` then you
347
should understand the implications of
348
:meth:`~sage.libs.gap.element.GapElement._set_compare_by_id`. To
349
be safe, it is recommended that you just work with the resulting
350
Sage :class:`FinitelyPresentedGroup`.
351
352
INPUT:
353
354
- ``libgap_fpgroup`` -- a LibGAP finitely presented group
355
356
OUTPUT:
357
358
A Sage :class:`FinitelyPresentedGroup`.
359
360
EXAMPLES:
361
362
First construct a LibGAP finitely presented group::
363
364
sage: F = libgap.FreeGroup(['a', 'b'])
365
sage: a_cubed = F.GeneratorsOfGroup()[0] ^ 3
366
sage: P = F / libgap([ a_cubed ]); P
367
<fp group of size infinity on the generators [ a, b ]>
368
sage: type(P)
369
<type 'sage.libs.gap.element.GapElement'>
370
371
Now wrap it::
372
373
sage: from sage.groups.finitely_presented import wrap_FpGroup
374
sage: wrap_FpGroup(P)
375
Finitely presented group < a, b | a^3 >
376
"""
377
assert libgap_fpgroup.IsFpGroup()
378
libgap_fpgroup._set_compare_by_id()
379
from sage.groups.free_group import wrap_FreeGroup
380
free_group = wrap_FreeGroup(libgap_fpgroup.FreeGroupOfFpGroup())
381
relations = tuple( free_group(rel.UnderlyingElement())
382
for rel in libgap_fpgroup.RelatorsOfFpGroup() )
383
return FinitelyPresentedGroup(free_group, relations)
384
385
386
class RewritingSystem(object):
387
"""
388
A class that wraps GAP's rewriting systems.
389
390
A rewriting system is a set of rules that allow to transform
391
one word in the group to an equivalent one.
392
393
If the rewriting system is confluent, then the transformated
394
word is a unique reduced form of the element of the group.
395
396
.. WARNING::
397
398
Note that the process of making a rewriting system confluent
399
might not end.
400
401
INPUT:
402
403
- ``G`` -- a group
404
405
REFERENCES:
406
407
- :wikipedia:`Knuth-Bendix_completion_algorithm`
408
409
EXAMPLES::
410
411
sage: F.<a,b> = FreeGroup()
412
sage: G = F / [a*b/a/b]
413
sage: k = G.rewriting_system()
414
sage: k
415
Rewriting system of Finitely presented group < a, b | a*b*a^-1*b^-1 >
416
with rules:
417
a*b*a^-1*b^-1 ---> 1
418
419
sage: k.reduce(a*b*a*b)
420
(a*b)^2
421
sage: k.make_confluent()
422
sage: k
423
Rewriting system of Finitely presented group < a, b | a*b*a^-1*b^-1 >
424
with rules:
425
b^-1*a^-1 ---> a^-1*b^-1
426
b^-1*a ---> a*b^-1
427
b*a^-1 ---> a^-1*b
428
b*a ---> a*b
429
430
sage: k.reduce(a*b*a*b)
431
a^2*b^2
432
433
.. TODO::
434
435
- Include support for different orderings (currently only shortlex
436
is used).
437
438
- Include the GAP package kbmag for more functionalities, including
439
automatic structures and faster compiled functions.
440
441
AUTHORS:
442
443
- Miguel Angel Marco Buzunariz (2013-12-16)
444
"""
445
def __init__(self, G):
446
"""
447
Initialize ``self``.
448
449
EXAMPLES::
450
451
sage: F.<a,b,c> = FreeGroup()
452
sage: G = F / [a^2, b^3, c^5]
453
sage: k = G.rewriting_system()
454
sage: k
455
Rewriting system of Finitely presented group < a, b, c | a^2, b^3, c^5 >
456
with rules:
457
a^2 ---> 1
458
b^3 ---> 1
459
c^5 ---> 1
460
"""
461
self._free_group = G.free_group()
462
self._fp_group = G
463
self._fp_group_gap = G.gap()
464
self._monoid_isomorphism = self._fp_group_gap.IsomorphismFpMonoid()
465
self._monoid = self._monoid_isomorphism.Image()
466
self._gap = self._monoid.KnuthBendixRewritingSystem()
467
468
def __repr__(self):
469
"""
470
Return a string representation.
471
472
EXAMPLES::
473
474
sage: F.<a> = FreeGroup()
475
sage: G = F / [a^2]
476
sage: k = G.rewriting_system()
477
sage: k
478
Rewriting system of Finitely presented group < a | a^2 >
479
with rules:
480
a^2 ---> 1
481
"""
482
ret = "Rewriting system of {}\nwith rules:".format(self._fp_group)
483
for i in sorted(self.rules().items()): # Make sure they are sorted to the repr is unique
484
ret += "\n {} ---> {}".format(i[0], i[1])
485
return ret
486
487
def free_group(self):
488
"""
489
The free group after which the rewriting system is defined
490
491
EXAMPLES::
492
493
sage: F = FreeGroup(3)
494
sage: G = F / [ [1,2,3], [-1,-2,-3] ]
495
sage: k = G.rewriting_system()
496
sage: k.free_group()
497
Free Group on generators {x0, x1, x2}
498
"""
499
return self._free_group
500
501
def finitely_presented_group(self):
502
"""
503
The finitely presented group where the rewriting system is defined.
504
505
EXAMPLES::
506
507
sage: F = FreeGroup(3)
508
sage: G = F / [ [1,2,3], [-1,-2,-3], [1,1], [2,2] ]
509
sage: k = G.rewriting_system()
510
sage: k.make_confluent()
511
sage: k
512
Rewriting system of Finitely presented group < x0, x1, x2 | x0*x1*x2, x0^-1*x1^-1*x2^-1, x0^2, x1^2 >
513
with rules:
514
x0^-1 ---> x0
515
x1^-1 ---> x1
516
x2^-1 ---> x2
517
x0^2 ---> 1
518
x0*x1 ---> x2
519
x0*x2 ---> x1
520
x1*x0 ---> x2
521
x1^2 ---> 1
522
x1*x2 ---> x0
523
x2*x0 ---> x1
524
x2*x1 ---> x0
525
x2^2 ---> 1
526
sage: k.finitely_presented_group()
527
Finitely presented group < x0, x1, x2 | x0*x1*x2, x0^-1*x1^-1*x2^-1, x0^2, x1^2 >
528
"""
529
return self._fp_group
530
531
def reduce(self, element):
532
"""
533
Applies the rules in the rewriting system to the element, to obtain
534
a reduced form.
535
536
If the rewriting system is confluent, this reduced form is unique
537
for all words representing the same element.
538
539
EXAMPLES::
540
541
sage: F.<a,b> = FreeGroup()
542
sage: G = F/[a^2, b^3, (a*b/a)^3, b*a*b*a]
543
sage: k = G.rewriting_system()
544
sage: k.reduce(b^4)
545
b
546
sage: k.reduce(a*b*a)
547
a*b*a
548
"""
549
eg = self._fp_group(element).gap()
550
egim = self._monoid_isomorphism.Image(eg)
551
red = self.gap().ReducedForm(egim.UnderlyingElement())
552
redfpmon = self._monoid.One().FamilyObj().ElementOfFpMonoid(red)
553
reducfpgr = self._monoid_isomorphism.PreImagesRepresentative(redfpmon)
554
tz = reducfpgr.UnderlyingElement().TietzeWordAbstractWord(self._free_group.gap().GeneratorsOfGroup())
555
return self._fp_group(tz.sage())
556
557
def gap(self):
558
"""
559
The gap representation of the rewriting system.
560
561
EXAMPLES::
562
563
sage: F.<a,b>=FreeGroup()
564
sage: G=F/[a*a,b*b]
565
sage: k=G.rewriting_system()
566
sage: k.gap()
567
Knuth Bendix Rewriting System for Monoid( [ a, A, b, B ], ... ) with rules
568
[ [ a^2, <identity ...> ], [ a*A, <identity ...> ],
569
[ A*a, <identity ...> ], [ b^2, <identity ...> ],
570
[ b*B, <identity ...> ], [ B*b, <identity ...> ] ]
571
"""
572
return self._gap
573
574
def rules(self):
575
"""
576
Return the rules that form the rewritig system.
577
578
OUTPUT:
579
580
A dictionary containing the rules of the rewriting system.
581
Each key is a word in the free group, and its corresponding
582
value is the word to which it is reduced.
583
584
EXAMPLES::
585
586
sage: F.<a,b> = FreeGroup()
587
sage: G = F / [a*a*a,b*b*a*a]
588
sage: k = G.rewriting_system()
589
sage: k
590
Rewriting system of Finitely presented group < a, b | a^3, b^2*a^2 >
591
with rules:
592
a^3 ---> 1
593
b^2*a^2 ---> 1
594
595
sage: k.rules()
596
{b^2*a^2: 1, a^3: 1}
597
sage: k.make_confluent()
598
sage: sorted(k.rules().items())
599
[(a^-2, a), (a^-1*b^-1, a*b), (a^-1*b, b^-1), (a^2, a^-1),
600
(a*b^-1, b), (b^-1*a^-1, a*b), (b^-1*a, b), (b^-2, a^-1),
601
(b*a^-1, b^-1), (b*a, a*b), (b^2, a)]
602
"""
603
dic = {}
604
grules = self.gap().Rules()
605
for i in grules:
606
a, b = i
607
afpmon = self._monoid.One().FamilyObj().ElementOfFpMonoid(a)
608
afg = self._monoid_isomorphism.PreImagesRepresentative(afpmon)
609
atz = afg.UnderlyingElement().TietzeWordAbstractWord(self._free_group.gap().GeneratorsOfGroup())
610
af = self._free_group(atz.sage())
611
if len(af.Tietze()) != 0:
612
bfpmon = self._monoid.One().FamilyObj().ElementOfFpMonoid(b)
613
bfg = self._monoid_isomorphism.PreImagesRepresentative(bfpmon)
614
btz = bfg.UnderlyingElement().TietzeWordAbstractWord(self._free_group.gap().GeneratorsOfGroup())
615
bf = self._free_group(btz.sage())
616
dic[af]=bf
617
return dic
618
619
def is_confluent(self):
620
"""
621
Return ``True`` if the system is confluent and ``False`` otherwise.
622
623
EXAMPLES::
624
625
sage: F = FreeGroup(3)
626
sage: G = F / [F([1,2,1,2,1,3,-1]),F([2,2,2,1,1,2]),F([1,2,3])]
627
sage: k = G.rewriting_system()
628
sage: k.is_confluent()
629
False
630
sage: k
631
Rewriting system of Finitely presented group < x0, x1, x2 | (x0*x1)^2*x0*x2*x0^-1, x1^3*x0^2*x1, x0*x1*x2 >
632
with rules:
633
x0*x1*x2 ---> 1
634
x1^3*x0^2*x1 ---> 1
635
(x0*x1)^2*x0*x2*x0^-1 ---> 1
636
637
sage: k.make_confluent()
638
sage: k.is_confluent()
639
True
640
sage: k
641
Rewriting system of Finitely presented group < x0, x1, x2 | (x0*x1)^2*x0*x2*x0^-1, x1^3*x0^2*x1, x0*x1*x2 >
642
with rules:
643
x0^-1 ---> x0
644
x1^-1 ---> x1
645
x0^2 ---> 1
646
x0*x1 ---> x2^-1
647
x0*x2^-1 ---> x1
648
x1*x0 ---> x2
649
x1^2 ---> 1
650
x1*x2^-1 ---> x0*x2
651
x1*x2 ---> x0
652
x2^-1*x0 ---> x0*x2
653
x2^-1*x1 ---> x0
654
x2^-2 ---> x2
655
x2*x0 ---> x1
656
x2*x1 ---> x0*x2
657
x2^2 ---> x2^-1
658
"""
659
return self._gap.IsConfluent().sage()
660
661
def make_confluent(self):
662
"""
663
Applies Knuth-Bendix algorithm to try to transform the rewriting
664
system into a confluent one.
665
666
Note that this method does not return any object, just changes the
667
rewriting sytem internally.
668
669
.. WARNING:
670
671
This algorithm is not granted to finish. Although it may be useful
672
in some occasions to run it, interrupt it manually after some time
673
and use then the transformed rewriting system. Even if it is not
674
confluent, it could be used to reduce some words.
675
676
ALGORITHM:
677
678
Uses GAP's ``MakeConfluent``.
679
680
EXAMPLES::
681
682
sage: F.<a,b> = FreeGroup()
683
sage: G = F / [a^2,b^3,(a*b/a)^3,b*a*b*a]
684
sage: k = G.rewriting_system()
685
sage: k
686
Rewriting system of Finitely presented group < a, b | a^2, b^3, a*b^3*a^-1, (b*a)^2 >
687
with rules:
688
a^2 ---> 1
689
b^3 ---> 1
690
(b*a)^2 ---> 1
691
a*b^3*a^-1 ---> 1
692
693
sage: k.make_confluent()
694
sage: k
695
Rewriting system of Finitely presented group < a, b | a^2, b^3, a*b^3*a^-1, (b*a)^2 >
696
with rules:
697
a^-1 ---> a
698
a^2 ---> 1
699
b^-1*a ---> a*b
700
b^-2 ---> b
701
b*a ---> a*b^-1
702
b^2 ---> b^-1
703
"""
704
try:
705
self._gap.MakeConfluent()
706
except ValueError:
707
raise ValueError('could not make the system confluent')
708
709
class FinitelyPresentedGroup(GroupMixinLibGAP, UniqueRepresentation,
710
Group, ParentLibGAP):
711
"""
712
A class that wraps GAP's Finitely Presented Groups.
713
714
.. WARNING::
715
716
You should use
717
:meth:`~sage.groups.free_group.FreeGroup_class.quotient` to
718
construct finitely presented groups as quotients of free
719
groups.
720
721
EXAMPLES::
722
723
sage: G.<a,b> = FreeGroup()
724
sage: H = G / [a, b^3]
725
sage: H
726
Finitely presented group < a, b | a, b^3 >
727
sage: H.gens()
728
(a, b)
729
730
sage: F.<a,b> = FreeGroup('a, b')
731
sage: J = F / (F([1]), F([2, 2, 2]))
732
sage: J is H
733
True
734
735
sage: G = FreeGroup(2)
736
sage: H = G / (G([1, 1]), G([2, 2, 2]))
737
sage: H.gens()
738
(x0, x1)
739
sage: H.gen(0)
740
x0
741
sage: H.ngens()
742
2
743
sage: H.gap()
744
<fp group on the generators [ x0, x1 ]>
745
sage: type(_)
746
<type 'sage.libs.gap.element.GapElement'>
747
"""
748
Element = FinitelyPresentedGroupElement
749
750
def __init__(self, free_group, relations):
751
"""
752
The Python constructor.
753
754
TESTS::
755
756
sage: G = FreeGroup('a, b')
757
sage: H = G / (G([1]), G([2])^3)
758
sage: H
759
Finitely presented group < a, b | a, b^3 >
760
761
sage: F = FreeGroup('a, b')
762
sage: J = F / (F([1]), F([2, 2, 2]))
763
sage: J is H
764
True
765
766
sage: TestSuite(H).run()
767
sage: TestSuite(J).run()
768
"""
769
from sage.groups.free_group import is_FreeGroup
770
assert is_FreeGroup(free_group)
771
assert isinstance(relations, tuple)
772
self._free_group = free_group
773
self._relations = relations
774
self._assign_names(free_group.variable_names())
775
parent_gap = free_group.gap() / libgap([ rel.gap() for rel in relations])
776
ParentLibGAP.__init__(self, parent_gap)
777
Group.__init__(self)
778
779
def __reduce__(self):
780
"""
781
Used in pickling.
782
783
TESTS::
784
785
sage: F = FreeGroup(4)
786
sage: F.inject_variables()
787
Defining x0, x1, x2, x3
788
sage: G = F.quotient([x0*x2, x3*x1*x3, x2*x1*x2])
789
sage: G.__reduce__()
790
(<class 'sage.groups.finitely_presented.FinitelyPresentedGroup'>,
791
(Free Group on generators {x0, x1, x2, x3},
792
(x0*x2, x3*x1*x3, x2*x1*x2)))
793
794
sage: F.<a,b,c> = FreeGroup()
795
sage: F.inject_variables()
796
Defining a, b, c
797
sage: G = F / [a*b*c/(b*c*a), a*b*c/(c*a*b)]
798
sage: G.__reduce__()
799
(<class 'sage.groups.finitely_presented.FinitelyPresentedGroup'>,
800
(Free Group on generators {a, b, c},
801
(a*b*c*a^-1*c^-1*b^-1, a*b*c*b^-1*a^-1*c^-1)))
802
"""
803
return (FinitelyPresentedGroup, (self._free_group, self._relations))
804
805
def _repr_(self):
806
"""
807
Return a string representation.
808
809
OUTPUT:
810
811
String.
812
813
TESTS::
814
815
sage: G.<a,b> = FreeGroup()
816
sage: H = G / (G([1]), G([2])^3)
817
sage: H # indirect doctest
818
Finitely presented group < a, b | a, b^3 >
819
sage: H._repr_()
820
'Finitely presented group < a, b | a, b^3 >'
821
"""
822
gens = ', '.join(self.variable_names())
823
rels = ', '.join([ str(r) for r in self.relations() ])
824
return 'Finitely presented group ' + '< '+ gens + ' | ' + rels + ' >'
825
826
def _latex_(self):
827
"""
828
Return a LaTeX representation
829
830
OUTPUT:
831
832
String. A valid LaTeX math command sequence.
833
834
TESTS::
835
836
sage: F=FreeGroup(4)
837
sage: F.inject_variables()
838
Defining x0, x1, x2, x3
839
sage: G=F.quotient([x0*x2, x3*x1*x3, x2*x1*x2])
840
sage: G._latex_()
841
'\\langle x_{0}, x_{1}, x_{2}, x_{3} \\mid x_{0}\\cdot x_{2} , x_{3}\\cdot x_{1}\\cdot x_{3} , x_{2}\\cdot x_{1}\\cdot x_{2}\\rangle'
842
"""
843
r = '\\langle '
844
for i in range(self.ngens()):
845
r = r+self.gen(i)._latex_()
846
if i < self.ngens()-1:
847
r = r+', '
848
r = r+' \\mid '
849
for i in range(len(self._relations)):
850
r = r+(self._relations)[i]._latex_()
851
if i < len(self.relations())-1:
852
r = r+' , '
853
r = r+'\\rangle'
854
return r
855
856
def free_group(self):
857
"""
858
Return the free group (without relations).
859
860
OUTPUT:
861
862
A :func:`~sage.groups.free_group.FreeGroup`.
863
864
EXAMPLES::
865
866
sage: G.<a,b,c> = FreeGroup()
867
sage: H = G / (a^2, b^3, a*b*~a*~b)
868
sage: H.free_group()
869
Free Group on generators {a, b, c}
870
sage: H.free_group() is G
871
True
872
"""
873
return self._free_group
874
875
def relations(self):
876
"""
877
Return the relations of the group.
878
879
OUTPUT:
880
881
The relations as a tuple of elements of :meth:`free_group`.
882
883
EXAMPLES::
884
885
sage: F = FreeGroup(5, 'x')
886
sage: F.inject_variables()
887
Defining x0, x1, x2, x3, x4
888
sage: G = F.quotient([x0*x2, x3*x1*x3, x2*x1*x2])
889
sage: G.relations()
890
(x0*x2, x3*x1*x3, x2*x1*x2)
891
sage: all(rel in F for rel in G.relations())
892
True
893
"""
894
return self._relations
895
896
@cached_method
897
def cardinality(self, limit=4096000):
898
"""
899
Compute the cardinality of ``self``.
900
901
INPUT:
902
903
- ``limit`` -- integer (default: 4096000). The maximal number
904
of cosets before the computation is aborted.
905
906
OUTPUT:
907
908
Integer or ``Infinity``. The number of elements in the group.
909
910
EXAMPLES::
911
912
sage: G.<a,b> = FreeGroup('a, b')
913
sage: H = G / (a^2, b^3, a*b*~a*~b)
914
sage: H.cardinality()
915
6
916
917
sage: F.<a,b,c> = FreeGroup()
918
sage: J = F / (F([1]), F([2, 2, 2]))
919
sage: J.cardinality()
920
+Infinity
921
922
ALGORITHM:
923
924
Uses GAP.
925
926
.. WARNING::
927
928
This is in general not a decidable problem, so it is not
929
guaranteed to give an answer. If the group is infinite, or
930
too big, you should be prepared for a long computation
931
that consumes all the memory without finishing if you do
932
not set a sensible ``limit``.
933
"""
934
with libgap.global_context('CosetTableDefaultMaxLimit', limit):
935
if not libgap.IsFinite(self.gap()):
936
from sage.rings.infinity import Infinity
937
return Infinity
938
try:
939
size = self.gap().Size()
940
except ValueError:
941
raise ValueError('Coset enumeration ran out of memory, is the group finite?')
942
return size.sage()
943
944
order = cardinality
945
946
def as_permutation_group(self, limit=4096000):
947
"""
948
Return an isomorphic permutation group.
949
950
The generators of the resulting group correspond to the images
951
by the isomorphism of the generators of the given group.
952
953
INPUT:
954
955
- ``limit`` -- integer (default: 4096000). The maximal number
956
of cosets before the computation is aborted.
957
958
OUTPUT:
959
960
A Sage
961
:func:`~sage.groups.perm_gps.permgroup.PermutationGroup`. If
962
the number of cosets exceeds the given ``limit``, a
963
``ValueError`` is returned.
964
965
EXAMPLES::
966
967
sage: G.<a,b> = FreeGroup()
968
sage: H = G / (a^2, b^3, a*b*~a*~b)
969
sage: H.as_permutation_group()
970
Permutation Group with generators [(1,2)(3,5)(4,6), (1,3,4)(2,5,6)]
971
972
sage: G.<a,b> = FreeGroup()
973
sage: H = G / [a^3*b]
974
sage: H.as_permutation_group(limit=1000)
975
Traceback (most recent call last):
976
...
977
ValueError: Coset enumeration exceeded limit, is the group finite?
978
979
ALGORITHM:
980
981
Uses GAP's coset enumeration on the trivial subgroup.
982
983
.. WARNING::
984
985
This is in general not a decidable problem (in fact, it is
986
not even posible to check if the group is finite or
987
not). If the group is infinite, or too big, you should be
988
prepared for a long computation that consumes all the
989
memory without finishing if you do not set a sensible
990
``limit``.
991
"""
992
with libgap.global_context('CosetTableDefaultMaxLimit', limit):
993
try:
994
trivial_subgroup = self.gap().TrivialSubgroup()
995
coset_table = self.gap().CosetTable(trivial_subgroup).sage()
996
except ValueError:
997
raise ValueError('Coset enumeration exceeded limit, is the group finite?')
998
from sage.combinat.permutation import Permutation
999
from sage.groups.perm_gps.permgroup import PermutationGroup
1000
return PermutationGroup([
1001
Permutation(coset_table[2*i]) for i in range(len(coset_table)/2)])
1002
1003
def direct_product(self, H, reduced=False, new_names=True):
1004
r"""
1005
Return the direct product of ``self`` with finitely presented
1006
group ``H``.
1007
1008
Calls GAP function ``DirectProduct``, which returns the direct
1009
product of a list of groups of any representation.
1010
1011
From [JohnsonPG90]_ (pg 45, proposition 4): If `G`, `H` are groups
1012
presented by `\langle X \mid R \rangle` and `\langle Y \mid S \rangle`
1013
respectively, then their direct product has the presentation
1014
`\langle X, Y \mid R, S, [X, Y] \rangle` where `[X, Y]` denotes the
1015
set of commutators `\{ x^{-1} y^{-1} x y \mid x \in X, y \in Y \}`.
1016
1017
INPUT:
1018
1019
- ``H`` -- a finitely presented group
1020
1021
- ``reduced`` -- (default: ``False``) boolean; if ``True``, then
1022
attempt to reduce the presentation of the product group
1023
1024
- ``new_names`` -- (default: ``True``) boolean; If ``True``, then
1025
lexicographical variable names are assigned to the generators of
1026
the group to be returned. If ``False``, the group to be returned
1027
keeps the generator names of the two groups forming the direct
1028
product. Note that one cannot ask to reduce the output and ask
1029
to keep the old variable names, as they they may change meaning
1030
in the output group if its presentation is reduced.
1031
1032
OUTPUT:
1033
1034
The direct product of ``self`` with ``H`` as a finitely
1035
presented group.
1036
1037
EXAMPLES::
1038
1039
sage: G = FreeGroup()
1040
sage: C12 = ( G / [G([1,1,1,1])] ).direct_product( G / [G([1,1,1])]); C12
1041
Finitely presented group < a, b | a^4, b^3, a^-1*b^-1*a*b >
1042
sage: C12.order(), C12.as_permutation_group().is_cyclic()
1043
(12, True)
1044
sage: klein = ( G / [G([1,1])] ).direct_product( G / [G([1,1])]); klein
1045
Finitely presented group < a, b | a^2, b^2, a^-1*b^-1*a*b >
1046
sage: klein.order(), klein.as_permutation_group().is_cyclic()
1047
(4, False)
1048
1049
We can keep the variable names from ``self`` and ``H`` to examine how
1050
new relations are formed::
1051
1052
sage: F = FreeGroup("a"); G = FreeGroup("g")
1053
sage: X = G / [G.0^12]; A = F / [F.0^6]
1054
sage: X.direct_product(A, new_names=False)
1055
Finitely presented group < g, a | g^12, a^6, g^-1*a^-1*g*a >
1056
sage: A.direct_product(X, new_names=False)
1057
Finitely presented group < a, g | a^6, g^12, a^-1*g^-1*a*g >
1058
1059
Or we can attempt to reduce the output group presentation::
1060
1061
sage: F = FreeGroup("a"); G = FreeGroup("g")
1062
sage: X = G / [G.0]; A = F / [F.0]
1063
sage: X.direct_product(A, new_names=True)
1064
Finitely presented group < a, b | a, b, a^-1*b^-1*a*b >
1065
sage: X.direct_product(A, reduced=True, new_names=True)
1066
Finitely presented group < | >
1067
1068
But we cannot do both::
1069
1070
sage: K = FreeGroup(['a','b'])
1071
sage: D = K / [K.0^5, K.1^8]
1072
sage: D.direct_product(D, reduced=True, new_names=False)
1073
Traceback (most recent call last):
1074
...
1075
ValueError: cannot reduce output and keep old variable names
1076
1077
TESTS::
1078
1079
sage: G = FreeGroup()
1080
sage: Dp = (G / [G([1,1])]).direct_product( G / [G([1,1,1,1,1,1])] )
1081
sage: Dp.as_permutation_group().is_isomorphic(PermutationGroup(['(1,2)','(3,4,5,6,7,8)']))
1082
True
1083
sage: C7 = G / [G.0**7]; C6 = G / [G.0**6]
1084
sage: C14 = G / [G.0**14]; C3 = G / [G.0**3]
1085
sage: C7.direct_product(C6).is_isomorphic(C14.direct_product(C3))
1086
True
1087
sage: F = FreeGroup(2); D = F / [F([1,1,1,1,1]),F([2,2]),F([1,2])**2]
1088
sage: D.direct_product(D).as_permutation_group().is_isomorphic(
1089
....: direct_product_permgroups([DihedralGroup(5),DihedralGroup(5)]))
1090
True
1091
1092
AUTHORS:
1093
1094
- Davis Shurbert (2013-07-20): initial version
1095
1096
REFERENCES:
1097
1098
.. [JohnsonPG90] D.L. Johnson. *Presentations of Groups*.
1099
Cambridge University Press. (1990).
1100
"""
1101
from sage.groups.free_group import FreeGroup, _lexi_gen
1102
1103
if not isinstance(H, FinitelyPresentedGroup):
1104
raise TypeError("input must be a finitely presented group")
1105
if reduced and not new_names:
1106
raise ValueError("cannot reduce output and keep old variable names")
1107
1108
fp_product = libgap.DirectProduct([self.gap(), H.gap()])
1109
GAP_gens = fp_product.FreeGeneratorsOfFpGroup()
1110
if new_names:
1111
name_itr = _lexi_gen() # Python generator for lexicographical variable names
1112
gen_names = [name_itr.next() for i in GAP_gens]
1113
else:
1114
gen_names= [str(g) for g in self.gens()] + [str(g) for g in H.gens()]
1115
# Build the direct product in Sage for better variable names
1116
ret_F = FreeGroup(gen_names)
1117
ret_rls = tuple([ret_F(rel_word.TietzeWordAbstractWord(GAP_gens).sage())
1118
for rel_word in fp_product.RelatorsOfFpGroup()])
1119
ret_fpg = FinitelyPresentedGroup(ret_F, ret_rls)
1120
if reduced:
1121
ret_fpg = ret_fpg.simplified()
1122
return ret_fpg
1123
1124
def semidirect_product(self, H, hom, check=True, reduced=False):
1125
"""
1126
The semidirect product of ``self`` with ``H`` via ``hom``.
1127
1128
If there exists a homomorphism `\phi` from a group `G` to the
1129
automorphism group of a group `H`, then we can define the semidirect
1130
product of `G` with `H` via `\phi` as the cartesian product of `G`
1131
and `H` with the operation
1132
1133
.. MATH::
1134
1135
(g_1, h_1)(g_2, h_2) = (g_1 g_2, \phi(g_2)(h_1) h_2).
1136
1137
INPUT:
1138
1139
- ``H`` -- Finitely presented group which is implicitly acted on
1140
by ``self`` and can be naturally embedded as a normal subgroup
1141
of the semidirect product.
1142
1143
- ``hom`` -- Homomorphism from ``self`` to the automorphism group
1144
of ``H``. Given as a pair, with generators of ``self`` in the
1145
first slot and the images of the corresponding generators in the
1146
second. These images must be automorphisms of ``H``, given again
1147
as a pair of generators and images.
1148
1149
- ``check`` -- Boolean (default ``True``). If ``False`` the defining
1150
homomorphism and automorphism images are not tested for validity.
1151
This test can be costly with large groups, so it can be bypassed
1152
if the user is confident that his morphisms are valid.
1153
1154
- ``reduced`` -- Boolean (default ``False``). If ``True`` then the
1155
method attempts to reduce the presentation of the output group.
1156
1157
OUTPUT:
1158
1159
The semidirect product of ``self`` with ``H`` via ``hom`` as a
1160
finitely presented group. See
1161
:meth:`PermutationGroup_generic.semidirect_product
1162
<sage.groups.perm_gps.permgroup.PermutationGroup_generic.semidirect_product>`
1163
for a more in depth explanation of a semidirect product.
1164
1165
AUTHORS:
1166
1167
- Davis Shurbert (8-1-2013)
1168
1169
EXAMPLES:
1170
1171
Group of order 12 as two isomorphic semidirect products::
1172
1173
sage: D4 = groups.presentation.Dihedral(4)
1174
sage: C3 = groups.presentation.Cyclic(3)
1175
sage: alpha1 = ([C3.gen(0)],[C3.gen(0)])
1176
sage: alpha2 = ([C3.gen(0)],[C3([1,1])])
1177
sage: S1 = D4.semidirect_product(C3, ([D4.gen(1), D4.gen(0)],[alpha1,alpha2]))
1178
sage: C2 = groups.presentation.Cyclic(2)
1179
sage: Q = groups.presentation.DiCyclic(3)
1180
sage: a = Q([1]); b = Q([-2])
1181
sage: alpha = (Q.gens(), [a,b])
1182
sage: S2 = C2.semidirect_product(Q, ([C2.0],[alpha]))
1183
sage: S1.is_isomorphic(S2)
1184
True
1185
1186
Dihedral groups can be constructed as semidirect products
1187
of cyclic groups::
1188
1189
sage: C2 = groups.presentation.Cyclic(2)
1190
sage: C8 = groups.presentation.Cyclic(8)
1191
sage: hom = (C2.gens(), [ ([C8([1])], [C8([-1])]) ])
1192
sage: D = C2.semidirect_product(C8, hom)
1193
sage: D.as_permutation_group().is_isomorphic(DihedralGroup(8))
1194
True
1195
1196
You can attempt to reduce the presentation of the output group::
1197
1198
sage: D = C2.semidirect_product(C8, hom); D
1199
Finitely presented group < a, b, c, d |
1200
a^2, b^-1*a^-1*b*a*d^-1*c^-1, c^-1*a^-1*c*a*d^-1, d^-1*a^-1*d*a,
1201
b^2*c^-1, c^-1*b^-1*c*b, d^-1*b^-1*d*b, c^2*d^-1, d^-1*c^-1*d*c, d^2 >
1202
sage: D = C2.semidirect_product(C8, hom, reduced=True); D
1203
Finitely presented group < a, b | a^2, (a*b)^2, b^8 >
1204
1205
sage: C3 = groups.presentation.Cyclic(3)
1206
sage: C4 = groups.presentation.Cyclic(4)
1207
sage: hom = (C3.gens(), [(C4.gens(), C4.gens())])
1208
sage: C3.semidirect_product(C4, hom)
1209
Finitely presented group < a, b, c |
1210
a^3, b^-1*a^-1*b*a, c^-1*a^-1*c*a, b^2*c^-1, c^-1*b^-1*c*b, c^2 >
1211
sage: D = C3.semidirect_product(C4, hom, reduced=True); D
1212
Finitely presented group < a, b | a^3, b^4, b^-1*a^-1*b*a >
1213
sage: D.as_permutation_group().is_cyclic()
1214
True
1215
1216
You can turn off the checks for the validity of the input morphisms.
1217
This check is expensive but behavior is unpredictable if inputs are
1218
invalid and are not caught by these tests. Due to a failure in GAP
1219
to list elements of an automorphism group in some cases, this check
1220
may cause the method to timeout or raise a GAP error. For example,
1221
if ``H`` is the cyclic group of order 6, then ``semidirect_product``
1222
appears to fall into an infinite loop due to this failure.::
1223
1224
sage: C5 = groups.presentation.Cyclic(5)
1225
sage: C12 = groups.presentation.Cyclic(12)
1226
sage: hom = (C5.gens(), [(C12.gens(), C12.gens())])
1227
sage: C5.semidirect_product(C12, hom)
1228
Traceback (most recent call last):
1229
...
1230
ValueError: libGAP: Error, <elm> is not contained in the source group
1231
sage: sp = C5.semidirect_product(C12, hom, check=False); sp
1232
Finitely presented group < a, b, c, d |
1233
a^5, b^-1*a^-1*b*a, c^-1*a^-1*c*a, d^-1*a^-1*d*a, b^2*d^-1,
1234
c^-1*b^-1*c*b, d^-1*b^-1*d*b, c^3, d^-1*c^-1*d*c, d^2 >
1235
sage: sp.as_permutation_group().is_cyclic(), sp.order()
1236
(True, 60)
1237
1238
TESTS::
1239
1240
sage: C = groups.presentation.Cyclic(7)
1241
sage: D = groups.presentation.Dihedral(5)
1242
sage: id1 = ([C.0], [(D.gens(),D.gens())])
1243
sage: Se1 = C.semidirect_product(D, id1)
1244
sage: id2 = (D.gens(), [(C.gens(),C.gens()),(C.gens(),C.gens())])
1245
sage: Se2 = D.semidirect_product(C ,id2)
1246
sage: Dp1 = C.direct_product(D);
1247
sage: Dp1.is_isomorphic(Se1), Dp1.is_isomorphic(Se2)
1248
(True, True)
1249
1250
Most checks for validity of input are left to GAP to handle::
1251
1252
sage: bad_aut = ([C.0], [(D.gens(),[D.0, D.0])])
1253
sage: C.semidirect_product(D, bad_aut)
1254
Traceback (most recent call last):
1255
...
1256
ValueError: images of input homomorphism must be automorphisms
1257
sage: bad_hom = ([D.0, D.1], [(C.gens(),C.gens())])
1258
sage: D.semidirect_product(C, bad_hom)
1259
Traceback (most recent call last):
1260
...
1261
ValueError: libGAP: Error, <gens> and <imgs> must be lists of same length
1262
"""
1263
from sage.groups.free_group import FreeGroup, _lexi_gen
1264
1265
if not isinstance(H, FinitelyPresentedGroup):
1266
raise TypeError("input must be a finitely presented group")
1267
1268
GAP_self = self.gap(); GAP_H = H.gap()
1269
auto_grp = libgap.AutomorphismGroup(H.gap())
1270
self_gens = [h.gap() for h in hom[0]]
1271
# construct image automorphisms in GAP
1272
GAP_aut_imgs = [ libgap.GroupHomomorphismByImages(GAP_H, GAP_H, [g.gap() for g in gns],
1273
[i.gap() for i in img]) for (gns, img) in hom[1] ]
1274
1275
# check for automorphism validity in images of operation defining homomorphism,
1276
# and construct the defining homomorphism.
1277
if check:
1278
if not all([a in libgap.List(libgap.AutomorphismGroup(GAP_H)) for a in GAP_aut_imgs]):
1279
raise ValueError("images of input homomorphism must be automorphisms")
1280
GAP_def_hom = libgap.GroupHomomorphismByImages(GAP_self, auto_grp, self_gens, GAP_aut_imgs)
1281
else:
1282
GAP_def_hom = GAP_self.GroupHomomorphismByImagesNC( auto_grp, self_gens, GAP_aut_imgs)
1283
1284
prod = libgap.SemidirectProduct(GAP_self, GAP_def_hom, GAP_H)
1285
# Convert pc group to fp group
1286
if prod.IsPcGroup():
1287
prod = libgap.Image(libgap.IsomorphismFpGroupByPcgs(prod.FamilyPcgs() , 'x'))
1288
if not prod.IsFpGroup():
1289
raise NotImplementedError("unable to convert GAP output to equivalent Sage fp group")
1290
1291
# Convert GAP group object to Sage via Tietze
1292
# lists for readability of variable names
1293
GAP_gens = prod.FreeGeneratorsOfFpGroup()
1294
name_itr = _lexi_gen() # Python generator for lexicographical variable names
1295
ret_F = FreeGroup([name_itr.next() for i in GAP_gens])
1296
ret_rls = tuple([ret_F(rel_word.TietzeWordAbstractWord(GAP_gens).sage())
1297
for rel_word in prod.RelatorsOfFpGroup()])
1298
ret_fpg = FinitelyPresentedGroup(ret_F, ret_rls)
1299
if reduced:
1300
ret_fpg = ret_fpg.simplified()
1301
return ret_fpg
1302
1303
def _element_constructor_(self, *args, **kwds):
1304
"""
1305
Construct an element of ``self``.
1306
1307
TESTS::
1308
1309
sage: G.<a,b> = FreeGroup()
1310
sage: H = G / (G([1]), G([2, 2, 2]))
1311
sage: H([1, 2, 1, -1]) # indirect doctest
1312
a*b
1313
sage: H([1, 2, 1, -2]) # indirect doctest
1314
a*b*a*b^-1
1315
"""
1316
if len(args)!=1:
1317
return self.element_class(self, *args, **kwds)
1318
x = args[0]
1319
if x==1:
1320
return self.one()
1321
try:
1322
P = x.parent()
1323
except AttributeError:
1324
return self.element_class(self, x, **kwds)
1325
if P is self._free_group:
1326
return self.element_class(self, x.Tietze(), **kwds)
1327
return self.element_class(self, x, **kwds)
1328
1329
@cached_method
1330
def abelian_invariants(self):
1331
r"""
1332
Return the abelian invariants of ``self``.
1333
1334
The abelian invariants are given by a list of integers
1335
`(i_1, \ldots, i_j)`, such that the abelianization of the group is
1336
isomorphic to `\ZZ / (i_1) \times \cdots \times \ZZ / (i_j)`.
1337
1338
EXAMPLES::
1339
1340
sage: G = FreeGroup(4, 'g')
1341
sage: G.inject_variables()
1342
Defining g0, g1, g2, g3
1343
sage: H = G.quotient([g1^2, g2*g1*g2^(-1)*g1^(-1), g1*g3^(-2), g0^4])
1344
sage: H.abelian_invariants()
1345
(0, 4, 4)
1346
1347
ALGORITHM:
1348
1349
Uses GAP.
1350
"""
1351
invariants = self.gap().AbelianInvariants()
1352
return tuple( i.sage() for i in invariants )
1353
1354
def simplification_isomorphism(self):
1355
"""
1356
Return an isomorphism from ``self`` to a finitely presented group with
1357
a (hopefully) simpler presentation.
1358
1359
EXAMPLES::
1360
1361
sage: G.<a,b,c> = FreeGroup()
1362
sage: H = G / [a*b*c, a*b^2, c*b/c^2]
1363
sage: I = H.simplification_isomorphism()
1364
sage: I
1365
Generic morphism:
1366
From: Finitely presented group < a, b, c | a*b*c, a*b^2, c*b*c^-2 >
1367
To: Finitely presented group < b | >
1368
sage: I(a)
1369
b^-2
1370
sage: I(b)
1371
b
1372
sage: I(c)
1373
b
1374
1375
TESTS::
1376
1377
sage: F = FreeGroup(1)
1378
sage: G = F.quotient([F.0])
1379
sage: G.simplification_isomorphism()
1380
Generic morphism:
1381
From: Finitely presented group < x | x >
1382
To: Finitely presented group < | >
1383
1384
ALGORITM:
1385
1386
Uses GAP.
1387
"""
1388
I = self.gap().IsomorphismSimplifiedFpGroup()
1389
domain = self
1390
codomain = wrap_FpGroup(I.Range())
1391
phi = lambda x: codomain(I.ImageElm(x.gap()))
1392
return self.hom(phi, codomain)
1393
1394
def simplified(self):
1395
"""
1396
Return an isomorphic group with a (hopefully) simpler presentation.
1397
1398
OUTPUT:
1399
1400
A new finitely presented group. Use
1401
:meth:`simplification_isomorphism` if you want to know the
1402
isomorphism.
1403
1404
EXAMPLES::
1405
1406
sage: G.<x,y> = FreeGroup()
1407
sage: H = G / [x ^5, y ^4, y*x*y^3*x ^3]
1408
sage: H
1409
Finitely presented group < x, y | x^5, y^4, y*x*y^3*x^3 >
1410
sage: H.simplified()
1411
Finitely presented group < x, y | y^4, y*x*y^-1*x^-2, x^5 >
1412
1413
A more complicate example::
1414
1415
sage: G.<e0, e1, e2, e3, e4, e5, e6, e7, e8, e9> = FreeGroup()
1416
sage: rels = [e6, e5, e3, e9, e4*e7^-1*e6, e9*e7^-1*e0,
1417
... e0*e1^-1*e2, e5*e1^-1*e8, e4*e3^-1*e8, e2]
1418
sage: H = G.quotient(rels); H
1419
Finitely presented group < e0, e1, e2, e3, e4, e5, e6, e7, e8, e9 |
1420
e6, e5, e3, e9, e4*e7^-1*e6, e9*e7^-1*e0, e0*e1^-1*e2, e5*e1^-1*e8, e4*e3^-1*e8, e2 >
1421
sage: H.simplified()
1422
Finitely presented group < e0 | e0^2 >
1423
"""
1424
return self.simplification_isomorphism().codomain()
1425
1426
def alexander_matrix(self):
1427
"""
1428
Return the Alexander matrix of the group.
1429
1430
This matrix is given by the fox derivatives of the relations
1431
with respect to the generators.
1432
1433
OUTPUT:
1434
1435
A group algebra-valued matrix. It depends on the (fixed)
1436
choice of presentation.
1437
1438
EXAMPLES::
1439
1440
sage: G.<a,b,c> = FreeGroup()
1441
sage: H = G.quotient([a*b/a/b, a*c/a/c, c*b/c/b])
1442
sage: H.alexander_matrix()
1443
[ B[1] - B[a*b*a^-1] B[a] - B[a*b*a^-1*b^-1] 0]
1444
[ B[1] - B[a*c*a^-1] 0 B[a] - B[a*c*a^-1*c^-1]]
1445
[ 0 B[c] - B[c*b*c^-1*b^-1] B[1] - B[c*b*c^-1]]
1446
1447
sage: G.<a,b,c,d,e> = FreeGroup()
1448
sage: H = G.quotient([a*b/a/b, a*c/a/c, a*d/a/d, b*c*d/(c*d*b), b*c*d/(d*b*c)])
1449
sage: H.alexander_matrix()
1450
[ B[1] - B[a*b*a^-1] B[a] - B[a*b*a^-1*b^-1] 0 0 0]
1451
[ B[1] - B[a*c*a^-1] 0 B[a] - B[a*c*a^-1*c^-1] 0 0]
1452
[ B[1] - B[a*d*a^-1] 0 0 B[a] - B[a*d*a^-1*d^-1] 0]
1453
[ 0 B[1] - B[b*c*d*b^-1] B[b] - B[b*c*d*b^-1*d^-1*c^-1] B[b*c] - B[b*c*d*b^-1*d^-1] 0]
1454
[ 0 B[1] - B[b*c*d*c^-1*b^-1] B[b] - B[b*c*d*c^-1] B[b*c] - B[b*c*d*c^-1*b^-1*d^-1] 0]
1455
"""
1456
rel = self.relations()
1457
gen = self._free_group.gens()
1458
return matrix(len(rel), len(gen),
1459
lambda i,j: rel[i].fox_derivative(gen[j]))
1460
1461
def rewriting_system(self):
1462
"""
1463
Return the rewriting system corresponding to the finitely presented
1464
group. This rewriting system can be used to reduce words with respect
1465
to the relations.
1466
1467
If the rewriting system is transformed into a confluent one, the
1468
reduction process will give as a result the (unique) reduced form
1469
of an element.
1470
1471
EXAMPLES::
1472
1473
sage: F.<a,b> = FreeGroup()
1474
sage: G = F / [a^2,b^3,(a*b/a)^3,b*a*b*a]
1475
sage: k = G.rewriting_system()
1476
sage: k
1477
Rewriting system of Finitely presented group < a, b | a^2, b^3, a*b^3*a^-1, (b*a)^2 >
1478
with rules:
1479
a^2 ---> 1
1480
b^3 ---> 1
1481
(b*a)^2 ---> 1
1482
a*b^3*a^-1 ---> 1
1483
1484
sage: G([1,1,2,2,2])
1485
a^2*b^3
1486
sage: k.reduce(G([1,1,2,2,2]))
1487
1
1488
sage: k.reduce(G([2,2,1]))
1489
b^2*a
1490
sage: k.make_confluent()
1491
sage: k.reduce(G([2,2,1]))
1492
a*b
1493
"""
1494
return RewritingSystem(self)
1495
1496
1497