Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/free_group.py
8814 views
1
"""
2
Free Groups
3
4
Free groups and finitely presented groups are implemented as a wrapper
5
over the corresponing GAP objects.
6
7
A free group can be created by giving the number of generators, or their names.
8
It is also possible to create indexed generators::
9
10
sage: G.<x,y,z> = FreeGroup(); G
11
Free Group on generators {x, y, z}
12
sage: FreeGroup(3)
13
Free Group on generators {x0, x1, x2}
14
sage: FreeGroup('a,b,c')
15
Free Group on generators {a, b, c}
16
sage: FreeGroup(3,'t')
17
Free Group on generators {t0, t1, t2}
18
19
The elements can be created by operating with the generators, or by passing a list
20
with the indices of the letters to the group:
21
22
EXAMPLES::
23
24
sage: G.<a,b,c> = FreeGroup()
25
sage: a*b*c*a
26
a*b*c*a
27
sage: G([1,2,3,1])
28
a*b*c*a
29
sage: a * b / c * b^2
30
a*b*c^-1*b^2
31
sage: G([1,1,2,-1,-3,2])
32
a^2*b*a^-1*c^-1*b
33
34
You can use call syntax to replace the generators with a set of
35
arbitrary ring elements::
36
37
sage: g = a * b / c * b^2
38
sage: g(1,2,3)
39
8/3
40
sage: M1 = identity_matrix(2)
41
sage: M2 = matrix([[1,1],[0,1]])
42
sage: M3 = matrix([[0,1],[1,0]])
43
sage: g([M1, M2, M3])
44
[1 3]
45
[1 2]
46
47
AUTHORS:
48
49
- Miguel Angel Marco Buzunariz
50
- Volker Braun
51
"""
52
53
##############################################################################
54
# Copyright (C) 2012 Miguel Angel Marco Buzunariz <[email protected]>
55
#
56
# Distributed under the terms of the GNU General Public License (GPL)
57
#
58
# The full text of the GPL is available at:
59
#
60
# http://www.gnu.org/licenses/
61
##############################################################################
62
63
64
from sage.groups.group import Group
65
from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP
66
from sage.structure.unique_representation import UniqueRepresentation
67
from sage.libs.gap.libgap import libgap
68
from sage.libs.gap.element import GapElement
69
from sage.rings.integer import Integer
70
from sage.rings.integer_ring import IntegerRing
71
from sage.misc.cachefunc import cached_method
72
73
74
def is_FreeGroup(x):
75
"""
76
Test whether ``x`` is a :class:`FreeGroup_class`.
77
78
INPUT:
79
80
- ``x`` -- anything.
81
82
OUTPUT:
83
84
Boolean.
85
86
EXAMPLES::
87
88
sage: from sage.groups.free_group import is_FreeGroup
89
sage: is_FreeGroup('a string')
90
False
91
sage: is_FreeGroup(FreeGroup(0))
92
True
93
"""
94
return isinstance(x, FreeGroup_class)
95
96
def _lexi_gen(zeroes=False):
97
"""
98
Return a generator object that produces variable names suitable for the
99
generators of a free group.
100
101
INPUT:
102
103
- ``zeroes`` -- Boolean defaulting as ``False``. If ``True``, the
104
integers appended to the output string begin at zero at the
105
first iteration through the alphabet.
106
107
OUTPUT:
108
109
Python generator object which outputs a character from the alphabet on each
110
``.next()`` call in lexicographical order. The integer `i` is appended
111
to the output string on the `i^{th}` iteration through the alphabet.
112
113
EXAMPLES::
114
115
sage: from sage.groups.free_group import _lexi_gen
116
sage: itr = _lexi_gen()
117
sage: F = FreeGroup([itr.next() for i in [1..10]]); F
118
Free Group on generators {a, b, c, d, e, f, g, h, i, j}
119
sage: it = _lexi_gen()
120
sage: [it.next() for i in range(10)]
121
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']
122
sage: itt = _lexi_gen(True)
123
sage: [itt.next() for i in range(10)]
124
['a0', 'b0', 'c0', 'd0', 'e0', 'f0', 'g0', 'h0', 'i0', 'j0']
125
sage: test = _lexi_gen()
126
sage: ls = [test.next() for i in range(3*26)]
127
sage: ls[2*26:3*26]
128
['a2', 'b2', 'c2', 'd2', 'e2', 'f2', 'g2', 'h2', 'i2', 'j2', 'k2', 'l2', 'm2',
129
'n2', 'o2', 'p2', 'q2', 'r2', 's2', 't2', 'u2', 'v2', 'w2', 'x2', 'y2', 'z2']
130
131
TESTS::
132
133
sage: from sage.groups.free_group import _lexi_gen
134
sage: test = _lexi_gen()
135
sage: ls = [test.next() for i in range(500)]
136
sage: ls[234], ls[260]
137
('a9', 'a10')
138
139
"""
140
count = Integer(0)
141
while True:
142
mwrap, ind = count.quo_rem(26)
143
if mwrap == 0 and not(zeroes):
144
name = ''
145
else:
146
name = str(mwrap)
147
name = chr(ord('a') + ind) + name
148
yield name
149
count = count + 1
150
151
class FreeGroupElement(ElementLibGAP):
152
"""
153
A wrapper of GAP's Free Group elements.
154
155
INPUT:
156
157
- ``x`` -- something that determines the group element. Either a
158
:class:`~sage.libs.gap.element.GapElement` or the Tietze list
159
(see :meth:`Tietze`) of the group element.
160
161
- ``parent`` -- the parent :class:`FreeGroup`.
162
163
EXAMPLES::
164
165
sage: G = FreeGroup('a, b')
166
sage: x = G([1, 2, -1, -2])
167
sage: x
168
a*b*a^-1*b^-1
169
sage: y = G([2, 2, 2, 1, -2, -2, -2])
170
sage: y
171
b^3*a*b^-3
172
sage: x*y
173
a*b*a^-1*b^2*a*b^-3
174
sage: y*x
175
b^3*a*b^-3*a*b*a^-1*b^-1
176
sage: x^(-1)
177
b*a*b^-1*a^-1
178
sage: x == x*y*y^(-1)
179
True
180
"""
181
182
def __init__(self, parent, x):
183
"""
184
The Python constructor.
185
186
See :class:`FreeGroupElement` for details.
187
188
TESTS::
189
190
sage: G.<a,b> = FreeGroup()
191
sage: x = G([1, 2, -1, -1])
192
sage: x # indirect doctest
193
a*b*a^-2
194
sage: y = G([2, 2, 2, 1, -2, -2, -1])
195
sage: y # indirect doctest
196
b^3*a*b^-2*a^-1
197
198
sage: TestSuite(G).run()
199
sage: TestSuite(x).run()
200
"""
201
if not isinstance(x, GapElement):
202
try:
203
l = x.Tietze()
204
except AttributeError:
205
l = list(x)
206
if len(l)>0:
207
if min(l) < -parent.ngens() or parent.ngens() < max(l):
208
raise ValueError('generators not in the group')
209
if 0 in l:
210
raise ValueError('zero does not denote a generator')
211
i=0
212
while i<len(l)-1:
213
if l[i]==-l[i+1]:
214
l.pop(i)
215
l.pop(i)
216
if i>0:
217
i=i-1
218
else:
219
i=i+1
220
AbstractWordTietzeWord = libgap.eval('AbstractWordTietzeWord')
221
x = AbstractWordTietzeWord(l, parent._gap_gens())
222
ElementLibGAP.__init__(self, parent, x)
223
224
def _latex_(self):
225
"""
226
Return a LaTeX representation
227
228
OUTPUT:
229
230
String. A valid LaTeX math command sequence.
231
232
EXAMPLES::
233
234
sage: F.<a,b,c> = FreeGroup()
235
sage: f = F([1, 2, 2, -3, -1]) * c^15 * a^(-23)
236
sage: f._latex_()
237
'a\\cdot b^{2}\\cdot c^{-1}\\cdot a^{-1}\\cdot c^{15}\\cdot a^{-23}'
238
239
sage: F = FreeGroup(3)
240
sage: f = F([1, 2, 2, -3, -1]) * F.gen(2)^11 * F.gen(0)^(-12)
241
sage: f._latex_()
242
'x_{0}\\cdot x_{1}^{2}\\cdot x_{2}^{-1}\\cdot x_{0}^{-1}\\cdot x_{2}^{11}\\cdot x_{0}^{-12}'
243
244
sage: F.<a,b,c> = FreeGroup()
245
sage: G = F / (F([1, 2, 1, -3, 2, -1]), F([2, -1]))
246
sage: f = G([1, 2, 2, -3, -1]) * G.gen(2)^15 * G.gen(0)^(-23)
247
sage: f._latex_()
248
'a\\cdot b^{2}\\cdot c^{-1}\\cdot a^{-1}\\cdot c^{15}\\cdot a^{-23}'
249
250
sage: F = FreeGroup(4)
251
sage: G = F.quotient((F([1, 2, 4, -3, 2, -1]), F([2, -1])))
252
sage: f = G([1, 2, 2, -3, -1]) * G.gen(3)^11 * G.gen(0)^(-12)
253
sage: f._latex_()
254
'x_{0}\\cdot x_{1}^{2}\\cdot x_{2}^{-1}\\cdot x_{0}^{-1}\\cdot x_{3}^{11}\\cdot x_{0}^{-12}'
255
"""
256
import re
257
s = self._repr_()
258
s = re.sub('([a-z]|[A-Z])([0-9]+)', '\g<1>_{\g<2>}', s)
259
s = re.sub('(\^)(-)([0-9]+)', '\g<1>{\g<2>\g<3>}', s)
260
s = re.sub('(\^)([0-9]+)', '\g<1>{\g<2>}', s)
261
s = s.replace('*', '\cdot ')
262
return s
263
264
def __reduce__(self):
265
"""
266
Implement pickling.
267
268
TESTS::
269
270
sage: F.<a,b> = FreeGroup()
271
sage: a.__reduce__()
272
(Free Group on generators {a, b}, ((1,),))
273
sage: (a*b*a^-1).__reduce__()
274
(Free Group on generators {a, b}, ((1, 2, -1),))
275
"""
276
return (self.parent(), (self.Tietze(),))
277
278
@cached_method
279
def Tietze(self):
280
"""
281
Return the Tietze list of the element.
282
283
The Tietze list of a word is a list of integers that represent
284
the letters in the word. A positive integer `i` represents
285
the letter corresponding to the `i`-th generator of the group.
286
Negative integers represent the inverses of generators.
287
288
OUTPUT:
289
290
A tuple of integers.
291
292
EXAMPLES::
293
294
sage: G.<a,b> = FreeGroup()
295
sage: a.Tietze()
296
(1,)
297
sage: x = a^2 * b^(-3) * a^(-2)
298
sage: x.Tietze()
299
(1, 1, -2, -2, -2, -1, -1)
300
301
TESTS::
302
303
sage: type(a.Tietze())
304
<type 'tuple'>
305
sage: type(a.Tietze()[0])
306
<type 'sage.rings.integer.Integer'>
307
"""
308
tl = self.gap().TietzeWordAbstractWord()
309
return tuple(tl.sage())
310
311
def fox_derivative(self, gen):
312
"""
313
Return the Fox derivative of self with respect to a given generator.
314
315
INPUT:
316
317
- ``gen`` : the generator with respect to which the derivative will be computed.
318
319
OUTPUT:
320
321
An element of the group algebra with integer coefficients.
322
323
EXAMPLES::
324
325
sage: G = FreeGroup(5)
326
sage: G.inject_variables()
327
Defining x0, x1, x2, x3, x4
328
sage: (~x0*x1*x0*x2*~x0).fox_derivative(x0)
329
-B[x0^-1] + B[x0^-1*x1] - B[x0^-1*x1*x0*x2*x0^-1]
330
sage: (~x0*x1*x0*x2*~x0).fox_derivative(x1)
331
B[x0^-1]
332
sage: (~x0*x1*x0*x2*~x0).fox_derivative(x2)
333
B[x0^-1*x1*x0]
334
sage: (~x0*x1*x0*x2*~x0).fox_derivative(x3)
335
0
336
"""
337
if not gen in self.parent().generators():
338
raise ValueError("Fox derivative can only be computed with respect to generators of the group")
339
l = list(self.Tietze())
340
R = self.parent().algebra(IntegerRing())
341
i = gen.Tietze()[0]
342
a = R.zero()
343
while len(l)>0:
344
b = l.pop(-1)
345
if b == i:
346
p = R(self.parent()(l))
347
a += p
348
elif b == -i:
349
p = R(self.parent()(l+[b]))
350
a -= p
351
return a
352
353
@cached_method
354
def syllables(self):
355
r"""
356
Return the syllables of the word.
357
358
Consider a free group element `g = x_1^{n_1} x_2^{n_2} \cdots
359
x_k^{n_k}`. The uniquely-determined subwords `x_i^{e_i}`
360
consisting only of powers of a single generator are called the
361
syllables of `g`.
362
363
OUTPUT:
364
365
The tuple of syllables. Each syllable is given as a pair
366
`(x_i, e_i)` consisting of a generator and a non-zero integer.
367
368
EXAMPLES::
369
370
sage: G.<a,b> = FreeGroup()
371
sage: w = a^2 * b^-1 * a^3
372
sage: w.syllables()
373
((a, 2), (b, -1), (a, 3))
374
"""
375
g = self.gap().UnderlyingElement()
376
k = g.NumberSyllables().sage()
377
gen = self.parent().gen
378
exponent_syllable = libgap.eval('ExponentSyllable')
379
generator_syllable = libgap.eval('GeneratorSyllable')
380
result = []
381
gen = self.parent().gen
382
for i in range(k):
383
exponent = exponent_syllable(g, i+1).sage()
384
generator = gen(generator_syllable(g, i+1).sage() - 1)
385
result.append( (generator, exponent) )
386
return tuple(result)
387
388
def __call__(self, *values):
389
"""
390
Replace the generators of the free group.
391
392
INPUT:
393
394
- ``*values`` -- a sequence of values, or a
395
list/tuple/iterable of the same length as the number of
396
generators.
397
398
OUTPUT:
399
400
The product of ``values`` in the order and with exponents
401
specified by ``self``.
402
403
EXAMPLES::
404
405
sage: G.<a,b> = FreeGroup()
406
sage: w = a^2 * b^-1 * a^3
407
sage: w(1, 2)
408
1/2
409
sage: w(2, 1)
410
32
411
sage: w.subs(b=1, a=2) # indirect doctest
412
32
413
414
TESTS::
415
416
sage: w([1, 2])
417
1/2
418
sage: w((1, 2))
419
1/2
420
sage: w(i+1 for i in range(2))
421
1/2
422
"""
423
if len(values) == 1:
424
try:
425
values = list(values[0])
426
except TypeError:
427
pass
428
G = self.parent()
429
if len(values) != G.ngens():
430
raise ValueError('number of values has to match the number of generators')
431
replace = dict(zip(G.gens(), values))
432
from sage.misc.all import prod
433
return prod( replace[gen] ** power for gen, power in self.syllables() )
434
435
436
def FreeGroup(n=None, names='x'):
437
"""
438
Construct a Free Group
439
440
INPUT:
441
442
- ``n`` -- integer or ``None`` (default). The nnumber of
443
generators. If not specified the ``names`` are counted.
444
445
- ``names`` -- string or list/tuple/iterable of strings (default:
446
``'x'``). The generator names or name prefix.
447
448
EXAMPLES::
449
450
sage: G.<a,b> = FreeGroup(); G
451
Free Group on generators {a, b}
452
sage: H = FreeGroup('a, b')
453
sage: G is H
454
True
455
sage: FreeGroup(0)
456
Free Group on generators {}
457
458
The entry can be either a string with the names of the generators,
459
or the number of generators and the prefix of the names to be
460
given. The default prefix is ``'x'`` ::
461
462
sage: FreeGroup(3)
463
Free Group on generators {x0, x1, x2}
464
sage: FreeGroup(3, 'g')
465
Free Group on generators {g0, g1, g2}
466
sage: FreeGroup()
467
Free Group on generators {x}
468
469
TESTS::
470
471
sage: G1 = FreeGroup(2, 'a,b')
472
sage: G2 = FreeGroup('a,b')
473
sage: G3.<a,b> = FreeGroup()
474
sage: G1 is G2, G2 is G3
475
(True, True)
476
"""
477
# Support Freegroup('a,b') syntax
478
if n is not None:
479
try:
480
n = Integer(n)
481
except TypeError:
482
names = n
483
n = None
484
# derive n from counting names
485
if n is None:
486
if isinstance(names, basestring):
487
n = len(names.split(','))
488
else:
489
names = list(names)
490
n = len(names)
491
from sage.structure.parent import normalize_names
492
names = tuple(normalize_names(n, names))
493
return FreeGroup_class(names)
494
495
496
def wrap_FreeGroup(libgap_free_group):
497
"""
498
Wrap a LibGAP free group.
499
500
This function changes the comparison method of
501
``libgap_free_group`` to comparison by Python ``id``. If you want
502
to put the LibGAP free group into a container (set, dict) then you
503
should understand the implications of
504
:meth:`~sage.libs.gap.element.GapElement._set_compare_by_id`. To
505
be safe, it is recommended that you just work with the resulting
506
Sage :class:`FreeGroup_class`.
507
508
INPUT:
509
510
- ``libgap_free_group`` -- a LibGAP free group.
511
512
OUTPUT:
513
514
A Sage :class:`FreeGroup_class`.
515
516
EXAMPLES:
517
518
First construct a LibGAP free group::
519
520
sage: F = libgap.FreeGroup(['a', 'b'])
521
sage: type(F)
522
<type 'sage.libs.gap.element.GapElement'>
523
524
Now wrap it::
525
526
sage: from sage.groups.free_group import wrap_FreeGroup
527
sage: wrap_FreeGroup(F)
528
Free Group on generators {a, b}
529
530
TESTS:
531
532
Check that we can do it twice (see :trac:`12339`) ::
533
534
sage: G = libgap.FreeGroup(['a', 'b'])
535
sage: wrap_FreeGroup(G)
536
Free Group on generators {a, b}
537
"""
538
assert libgap_free_group.IsFreeGroup()
539
libgap_free_group._set_compare_by_id()
540
names = tuple( str(g) for g in libgap_free_group.GeneratorsOfGroup() )
541
return FreeGroup_class(names, libgap_free_group)
542
543
544
class FreeGroup_class(UniqueRepresentation, Group, ParentLibGAP):
545
"""
546
A class that wraps GAP's FreeGroup
547
548
See :func:`FreeGroup` for details.
549
550
TESTS::
551
552
sage: G = FreeGroup('a, b')
553
sage: TestSuite(G).run()
554
"""
555
Element = FreeGroupElement
556
557
def __init__(self, generator_names, libgap_free_group=None):
558
"""
559
Python constructor.
560
561
INPUT:
562
563
- ``generator_names`` -- a tuple of strings. The names of the
564
generators.
565
566
- ``libgap_free_group`` -- a LibGAP free group or ``None``
567
(default). The LibGAP free group to wrap. If ``None``, a
568
suitable group will be constructed.
569
570
TESTS::
571
572
sage: G.<a,b> = FreeGroup() # indirect doctest
573
sage: G
574
Free Group on generators {a, b}
575
sage: G.variable_names()
576
('a', 'b')
577
"""
578
n = len(generator_names)
579
self._assign_names(generator_names)
580
if libgap_free_group is None:
581
libgap_free_group = libgap.FreeGroup(generator_names)
582
ParentLibGAP.__init__(self, libgap_free_group)
583
Group.__init__(self)
584
585
def _repr_(self):
586
"""
587
TESTS::
588
589
sage: G = FreeGroup('a, b')
590
sage: G # indirect doctest
591
Free Group on generators {a, b}
592
sage: G._repr_()
593
'Free Group on generators {a, b}'
594
"""
595
return 'Free Group on generators {'+ ', '.join(self.variable_names()) + '}'
596
597
def rank(self):
598
"""
599
Return the number of generators of self.
600
601
Alias for :meth:`ngens`.
602
603
OUTPUT:
604
605
Integer.
606
607
EXAMPLES::
608
609
sage: G = FreeGroup('a, b'); G
610
Free Group on generators {a, b}
611
sage: G.rank()
612
2
613
sage: H = FreeGroup(3, 'x')
614
sage: H
615
Free Group on generators {x0, x1, x2}
616
sage: H.rank()
617
3
618
"""
619
return self.ngens()
620
621
def _gap_init_(self):
622
"""
623
Return the string used to construct the object in gap.
624
625
EXAMPLES::
626
627
sage: G = FreeGroup(3)
628
sage: G._gap_init_()
629
'FreeGroup(["x0", "x1", "x2"])'
630
"""
631
gap_names = [ '"' + s + '"' for s in self.variable_names() ]
632
gen_str = ', '.join(gap_names)
633
return 'FreeGroup(['+gen_str+'])'
634
635
def _element_constructor_(self, *args, **kwds):
636
"""
637
TESTS::
638
639
sage: G.<a,b> = FreeGroup()
640
sage: G([1, 2, 1]) # indirect doctest
641
a*b*a
642
sage: G([1, 2, -2, 1, 1, -2]) # indirect doctest
643
a^3*b^-1
644
645
sage: G( G._gap_gens()[0] )
646
a
647
sage: type(_)
648
<class 'sage.groups.free_group.FreeGroup_class_with_category.element_class'>
649
"""
650
if len(args)!=1:
651
return self.element_class(self, *args, **kwds)
652
x = args[0]
653
if x==1:
654
return self.one()
655
try:
656
P = x.parent()
657
except AttributeError:
658
return self.element_class(self, x, **kwds)
659
if hasattr(P, '_freegroup_'):
660
if P.FreeGroup() is self:
661
return self.element_class(self, x.Tietze(), **kwds)
662
return self.element_class(self, x, **kwds)
663
664
def abelian_invariants(self):
665
r"""
666
Return the Abelian invariants of self.
667
668
The Abelian invariants are given by a list of integers `i_1 \dots i_j`, such that the
669
abelianization of the group is isomorphic to
670
671
.. math::
672
673
\ZZ / (i_1) \times \dots \times \ZZ / (i_j)
674
675
EXAMPLES::
676
677
sage: F.<a,b> = FreeGroup()
678
sage: F.abelian_invariants()
679
(0, 0)
680
"""
681
inv = self.gap().AbelianInvariants()
682
return tuple(inv.sage())
683
684
def quotient(self, relations):
685
"""
686
Return the quotient of self by the normal subgroup generated
687
by the given elements.
688
689
This quotient is a finitely presented groups with the same
690
generators as ``self``, and relations given by the elements of
691
``relations``.
692
693
INPUT:
694
695
- ``relations`` -- A list/tuple/iterable with the elements of
696
the free group.
697
698
OUTPUT:
699
700
A finitely presented group, with generators corresponding to
701
the generators of the free group, and relations corresponding
702
to the elements in ``relations``.
703
704
EXAMPLES::
705
706
sage: F.<a,b> = FreeGroup()
707
sage: F.quotient([a*b^2*a, b^3])
708
Finitely presented group < a, b | a*b^2*a, b^3 >
709
710
Division is shorthand for :meth:`quotient` ::
711
712
sage: F / [a*b^2*a, b^3]
713
Finitely presented group < a, b | a*b^2*a, b^3 >
714
715
Relations are converted to the free group, even if they are not
716
elements of it (if possible) ::
717
718
sage: F1.<a,b,c,d>=FreeGroup()
719
sage: F2.<a,b>=FreeGroup()
720
sage: r=a*b/a
721
sage: r.parent()
722
Free Group on generators {a, b}
723
sage: F1/[r]
724
Finitely presented group < a, b, c, d | a*b*a^-1 >
725
726
"""
727
from sage.groups.finitely_presented import FinitelyPresentedGroup
728
return FinitelyPresentedGroup(self, tuple(map(self, relations) ) )
729
730
__div__ = quotient
731
732