passagemath-groups 10.6.41__cp314-cp314t-musllinux_1_2_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_groups/__init__.py +3 -0
- passagemath_groups-10.6.41.dist-info/METADATA +113 -0
- passagemath_groups-10.6.41.dist-info/RECORD +36 -0
- passagemath_groups-10.6.41.dist-info/WHEEL +5 -0
- passagemath_groups-10.6.41.dist-info/top_level.txt +3 -0
- sage/all__sagemath_groups.py +21 -0
- sage/geometry/all__sagemath_groups.py +1 -0
- sage/geometry/palp_normal_form.cpython-314t-x86_64-linux-musl.so +0 -0
- sage/geometry/palp_normal_form.pyx +401 -0
- sage/groups/abelian_gps/all.py +25 -0
- sage/groups/all.py +5 -0
- sage/groups/all__sagemath_groups.py +32 -0
- sage/groups/artin.py +1074 -0
- sage/groups/braid.py +3806 -0
- sage/groups/cactus_group.py +1001 -0
- sage/groups/cubic_braid.py +2052 -0
- sage/groups/finitely_presented.py +1896 -0
- sage/groups/finitely_presented_catalog.py +27 -0
- sage/groups/finitely_presented_named.py +592 -0
- sage/groups/fqf_orthogonal.py +579 -0
- sage/groups/free_group.py +944 -0
- sage/groups/group_exp.py +360 -0
- sage/groups/group_semidirect_product.py +504 -0
- sage/groups/kernel_subgroup.py +231 -0
- sage/groups/lie_gps/all.py +1 -0
- sage/groups/lie_gps/catalog.py +8 -0
- sage/groups/lie_gps/nilpotent_lie_group.py +945 -0
- sage/groups/misc_gps/all.py +1 -0
- sage/groups/misc_gps/misc_groups.py +11 -0
- sage/groups/misc_gps/misc_groups_catalog.py +33 -0
- sage/groups/raag.py +866 -0
- sage/groups/semimonomial_transformations/all.py +1 -0
- sage/groups/semimonomial_transformations/semimonomial_transformation.cpython-314t-x86_64-linux-musl.so +0 -0
- sage/groups/semimonomial_transformations/semimonomial_transformation.pxd +9 -0
- sage/groups/semimonomial_transformations/semimonomial_transformation.pyx +346 -0
- sage/groups/semimonomial_transformations/semimonomial_transformation_group.py +512 -0
|
@@ -0,0 +1,1896 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-groups
|
|
2
|
+
"""
|
|
3
|
+
Finitely Presented Groups
|
|
4
|
+
|
|
5
|
+
Finitely presented groups are constructed as quotients of
|
|
6
|
+
:mod:`~sage.groups.free_group`::
|
|
7
|
+
|
|
8
|
+
sage: F.<a,b,c> = FreeGroup()
|
|
9
|
+
sage: G = F / [a^2, b^2, c^2, a*b*c*a*b*c]
|
|
10
|
+
sage: G
|
|
11
|
+
Finitely presented group < a, b, c | a^2, b^2, c^2, (a*b*c)^2 >
|
|
12
|
+
|
|
13
|
+
One can create their elements by multiplying the generators or by
|
|
14
|
+
specifying a Tietze list (see
|
|
15
|
+
:meth:`~sage.groups.finitely_presented.FinitelyPresentedGroupElement.Tietze`)
|
|
16
|
+
as in the case of free groups::
|
|
17
|
+
|
|
18
|
+
sage: G.gen(0) * G.gen(1)
|
|
19
|
+
a*b
|
|
20
|
+
sage: G([1,2,-1])
|
|
21
|
+
a*b*a^-1
|
|
22
|
+
sage: a.parent()
|
|
23
|
+
Free Group on generators {a, b, c}
|
|
24
|
+
sage: G.inject_variables()
|
|
25
|
+
Defining a, b, c
|
|
26
|
+
sage: a.parent()
|
|
27
|
+
Finitely presented group < a, b, c | a^2, b^2, c^2, (a*b*c)^2 >
|
|
28
|
+
|
|
29
|
+
Notice that, even if they are represented in the same way, the
|
|
30
|
+
elements of a finitely presented group and the elements of the
|
|
31
|
+
corresponding free group are not the same thing. However, they can be
|
|
32
|
+
converted from one parent to the other::
|
|
33
|
+
|
|
34
|
+
sage: F.<a,b,c> = FreeGroup()
|
|
35
|
+
sage: G = F / [a^2,b^2,c^2,a*b*c*a*b*c]
|
|
36
|
+
sage: F([1])
|
|
37
|
+
a
|
|
38
|
+
sage: G([1])
|
|
39
|
+
a
|
|
40
|
+
sage: F([1]) is G([1])
|
|
41
|
+
False
|
|
42
|
+
sage: F([1]) == G([1])
|
|
43
|
+
False
|
|
44
|
+
sage: G(a*b/c)
|
|
45
|
+
a*b*c^-1
|
|
46
|
+
sage: F(G(a*b/c))
|
|
47
|
+
a*b*c^-1
|
|
48
|
+
|
|
49
|
+
Finitely presented groups are implemented via GAP. You can use the
|
|
50
|
+
:meth:`~sage.groups.libgap_wrapper.ParentLibGAP.gap` method to access
|
|
51
|
+
the underlying LibGAP object::
|
|
52
|
+
|
|
53
|
+
sage: G = FreeGroup(2)
|
|
54
|
+
sage: G.inject_variables()
|
|
55
|
+
Defining x0, x1
|
|
56
|
+
sage: H = G / (x0^2, (x0*x1)^2, x1^2)
|
|
57
|
+
sage: H.gap()
|
|
58
|
+
<fp group on the generators [ x0, x1 ]>
|
|
59
|
+
|
|
60
|
+
This can be useful, for example, to use GAP functions that are not yet
|
|
61
|
+
wrapped in Sage::
|
|
62
|
+
|
|
63
|
+
sage: H.gap().LowerCentralSeries()
|
|
64
|
+
[ Group(<fp, no generators known>), Group(<fp, no generators known>) ]
|
|
65
|
+
|
|
66
|
+
The same holds for the group elements::
|
|
67
|
+
|
|
68
|
+
sage: G = FreeGroup(2)
|
|
69
|
+
sage: H = G / (G([1, 1]), G([2, 2, 2]), G([1, 2, -1, -2])); H
|
|
70
|
+
Finitely presented group < x0, x1 | x0^2, x1^3, x0*x1*x0^-1*x1^-1 >
|
|
71
|
+
sage: a = H([1])
|
|
72
|
+
sage: a
|
|
73
|
+
x0
|
|
74
|
+
sage: a.gap()
|
|
75
|
+
x0
|
|
76
|
+
sage: a.gap().Order()
|
|
77
|
+
2
|
|
78
|
+
sage: type(_) # note that the above output is not a Sage integer
|
|
79
|
+
<class 'sage.libs.gap.element.GapElement_Integer'>
|
|
80
|
+
|
|
81
|
+
You can use call syntax to replace the generators with a set of
|
|
82
|
+
arbitrary ring elements. For example, take the free abelian group
|
|
83
|
+
obtained by modding out the commutator subgroup of the free group::
|
|
84
|
+
|
|
85
|
+
sage: G = FreeGroup(2)
|
|
86
|
+
sage: G_ab = G / [G([1, 2, -1, -2])]; G_ab
|
|
87
|
+
Finitely presented group < x0, x1 | x0*x1*x0^-1*x1^-1 >
|
|
88
|
+
sage: a,b = G_ab.gens()
|
|
89
|
+
sage: g = a * b
|
|
90
|
+
sage: M1 = matrix([[1,0],[0,2]])
|
|
91
|
+
sage: M2 = matrix([[0,1],[1,0]])
|
|
92
|
+
sage: g(3, 5)
|
|
93
|
+
15
|
|
94
|
+
sage: g(M1, M1)
|
|
95
|
+
[1 0]
|
|
96
|
+
[0 4]
|
|
97
|
+
sage: M1*M2 == M2*M1 # matrices do not commute
|
|
98
|
+
False
|
|
99
|
+
sage: g(M1, M2)
|
|
100
|
+
Traceback (most recent call last):
|
|
101
|
+
...
|
|
102
|
+
ValueError: the values do not satisfy all relations of the group
|
|
103
|
+
|
|
104
|
+
.. WARNING::
|
|
105
|
+
|
|
106
|
+
Some methods are not guaranteed to finish since the word problem
|
|
107
|
+
for finitely presented groups is, in general, undecidable. In
|
|
108
|
+
those cases the process may run until the available memory is
|
|
109
|
+
exhausted.
|
|
110
|
+
|
|
111
|
+
REFERENCES:
|
|
112
|
+
|
|
113
|
+
- :wikipedia:`Presentation_of_a_group`
|
|
114
|
+
|
|
115
|
+
- :wikipedia:`Word_problem_for_groups`
|
|
116
|
+
|
|
117
|
+
AUTHOR:
|
|
118
|
+
|
|
119
|
+
- Miguel Angel Marco Buzunariz
|
|
120
|
+
"""
|
|
121
|
+
|
|
122
|
+
# ****************************************************************************
|
|
123
|
+
# Copyright (C) 2012 Miguel Angel Marco Buzunariz <mmarco@unizar.es>
|
|
124
|
+
#
|
|
125
|
+
# This program is free software: you can redistribute it and/or modify
|
|
126
|
+
# it under the terms of the GNU General Public License as published by
|
|
127
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
128
|
+
# (at your option) any later version.
|
|
129
|
+
# https://www.gnu.org/licenses/
|
|
130
|
+
# ****************************************************************************
|
|
131
|
+
|
|
132
|
+
from sage.arith.misc import GCD as gcd
|
|
133
|
+
from sage.categories.morphism import SetMorphism
|
|
134
|
+
from sage.groups.free_group import FreeGroup
|
|
135
|
+
from sage.groups.free_group import FreeGroupElement
|
|
136
|
+
from sage.groups.group import Group
|
|
137
|
+
from sage.groups.libgap_wrapper import ParentLibGAP, ElementLibGAP
|
|
138
|
+
from sage.groups.libgap_mixin import GroupMixinLibGAP
|
|
139
|
+
from sage.libs.gap.element import GapElement
|
|
140
|
+
from sage.libs.gap.libgap import libgap
|
|
141
|
+
from sage.matrix.constructor import matrix
|
|
142
|
+
from sage.misc.cachefunc import cached_method
|
|
143
|
+
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
|
|
144
|
+
from sage.rings.rational_field import QQ
|
|
145
|
+
from sage.sets.set import Set
|
|
146
|
+
from sage.structure.richcmp import richcmp, richcmp_method
|
|
147
|
+
from sage.structure.unique_representation import CachedRepresentation
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
class GroupMorphismWithGensImages(SetMorphism):
|
|
151
|
+
r"""
|
|
152
|
+
Class used for morphisms from finitely presented groups to
|
|
153
|
+
other groups. It just adds the images of the generators at the
|
|
154
|
+
end of the representation.
|
|
155
|
+
|
|
156
|
+
EXAMPLES::
|
|
157
|
+
|
|
158
|
+
sage: F = FreeGroup(3)
|
|
159
|
+
sage: G = F / [F([1, 2, 3, 1, 2, 3]), F([1, 1, 1])]
|
|
160
|
+
sage: H = AlternatingGroup(3)
|
|
161
|
+
sage: HS = G.Hom(H)
|
|
162
|
+
sage: from sage.groups.finitely_presented import GroupMorphismWithGensImages
|
|
163
|
+
sage: GroupMorphismWithGensImages(HS, lambda a: H.one())
|
|
164
|
+
Generic morphism:
|
|
165
|
+
From: Finitely presented group < x0, x1, x2 | (x0*x1*x2)^2, x0^3 >
|
|
166
|
+
To: Alternating group of order 3!/2 as a permutation group
|
|
167
|
+
Defn: x0 |--> ()
|
|
168
|
+
x1 |--> ()
|
|
169
|
+
x2 |--> ()
|
|
170
|
+
"""
|
|
171
|
+
def _repr_defn(self):
|
|
172
|
+
r"""
|
|
173
|
+
Return the part of the representation that includes the images of the generators.
|
|
174
|
+
|
|
175
|
+
EXAMPLES::
|
|
176
|
+
|
|
177
|
+
sage: F = FreeGroup(3)
|
|
178
|
+
sage: G = F / [F([1,2,3,1,2,3]),F([1,1,1])]
|
|
179
|
+
sage: H = AlternatingGroup(3)
|
|
180
|
+
sage: HS = G.Hom(H)
|
|
181
|
+
sage: from sage.groups.finitely_presented import GroupMorphismWithGensImages
|
|
182
|
+
sage: f = GroupMorphismWithGensImages(HS, lambda a: H.one())
|
|
183
|
+
sage: f._repr_defn()
|
|
184
|
+
'x0 |--> ()\nx1 |--> ()\nx2 |--> ()'
|
|
185
|
+
"""
|
|
186
|
+
return '\n'.join(f'{i} |--> {self(i)}' for i in self.domain().gens())
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class FinitelyPresentedGroupElement(FreeGroupElement):
|
|
190
|
+
"""
|
|
191
|
+
A wrapper of GAP's Finitely Presented Group elements.
|
|
192
|
+
|
|
193
|
+
The elements are created by passing the Tietze list that determines them.
|
|
194
|
+
|
|
195
|
+
EXAMPLES::
|
|
196
|
+
|
|
197
|
+
sage: G = FreeGroup('a, b')
|
|
198
|
+
sage: H = G / [G([1]), G([2, 2, 2])]
|
|
199
|
+
sage: H([1, 2, 1, -1])
|
|
200
|
+
a*b
|
|
201
|
+
sage: H([1, 2, 1, -2])
|
|
202
|
+
a*b*a*b^-1
|
|
203
|
+
sage: x = H([1, 2, -1, -2])
|
|
204
|
+
sage: x
|
|
205
|
+
a*b*a^-1*b^-1
|
|
206
|
+
sage: y = H([2, 2, 2, 1, -2, -2, -2])
|
|
207
|
+
sage: y
|
|
208
|
+
b^3*a*b^-3
|
|
209
|
+
sage: x*y
|
|
210
|
+
a*b*a^-1*b^2*a*b^-3
|
|
211
|
+
sage: x^(-1)
|
|
212
|
+
b*a*b^-1*a^-1
|
|
213
|
+
"""
|
|
214
|
+
|
|
215
|
+
def __init__(self, parent, x, check=True):
|
|
216
|
+
"""
|
|
217
|
+
The Python constructor.
|
|
218
|
+
|
|
219
|
+
See :class:`FinitelyPresentedGroupElement` for details.
|
|
220
|
+
|
|
221
|
+
TESTS::
|
|
222
|
+
|
|
223
|
+
sage: G = FreeGroup('a, b')
|
|
224
|
+
sage: H = G / [G([1]), G([2, 2, 2])]
|
|
225
|
+
sage: H([1, 2, 1, -1])
|
|
226
|
+
a*b
|
|
227
|
+
|
|
228
|
+
sage: TestSuite(G).run()
|
|
229
|
+
sage: TestSuite(H).run()
|
|
230
|
+
sage: G.<a,b> = FreeGroup()
|
|
231
|
+
sage: H = G / (G([1]), G([2, 2, 2]))
|
|
232
|
+
sage: x = H([1, 2, -1, -2])
|
|
233
|
+
sage: TestSuite(x).run()
|
|
234
|
+
sage: TestSuite(G.one()).run()
|
|
235
|
+
"""
|
|
236
|
+
if not isinstance(x, GapElement):
|
|
237
|
+
F = parent.free_group()
|
|
238
|
+
free_element = F(x)
|
|
239
|
+
fp_family = parent.gap().Identity().FamilyObj()
|
|
240
|
+
x = libgap.ElementOfFpGroup(fp_family, free_element.gap())
|
|
241
|
+
ElementLibGAP.__init__(self, parent, x)
|
|
242
|
+
|
|
243
|
+
def __reduce__(self):
|
|
244
|
+
"""
|
|
245
|
+
Used in pickling.
|
|
246
|
+
|
|
247
|
+
TESTS::
|
|
248
|
+
|
|
249
|
+
sage: F.<a,b> = FreeGroup()
|
|
250
|
+
sage: G = F / [a*b, a^2]
|
|
251
|
+
sage: G.inject_variables()
|
|
252
|
+
Defining a, b
|
|
253
|
+
sage: a.__reduce__()
|
|
254
|
+
(Finitely presented group < a, b | a*b, a^2 >, ((1,),))
|
|
255
|
+
sage: (a*b*a^-1).__reduce__()
|
|
256
|
+
(Finitely presented group < a, b | a*b, a^2 >, ((1, 2, -1),))
|
|
257
|
+
|
|
258
|
+
sage: F.<a,b,c> = FreeGroup('a, b, c')
|
|
259
|
+
sage: G = F.quotient([a*b*c/(b*c*a), a*b*c/(c*a*b)])
|
|
260
|
+
sage: G.inject_variables()
|
|
261
|
+
Defining a, b, c
|
|
262
|
+
sage: x = a*b*c
|
|
263
|
+
sage: x.__reduce__()
|
|
264
|
+
(Finitely presented group < a, b, c | a*b*c*a^-1*c^-1*b^-1, a*b*c*b^-1*a^-1*c^-1 >,
|
|
265
|
+
((1, 2, 3),))
|
|
266
|
+
"""
|
|
267
|
+
return (self.parent(), (self.Tietze(),))
|
|
268
|
+
|
|
269
|
+
def _repr_(self):
|
|
270
|
+
"""
|
|
271
|
+
Return a string representation.
|
|
272
|
+
|
|
273
|
+
EXAMPLES::
|
|
274
|
+
|
|
275
|
+
sage: G.<a,b> = FreeGroup()
|
|
276
|
+
sage: H = G / [a^2, b^3]
|
|
277
|
+
sage: H.gen(0)
|
|
278
|
+
a
|
|
279
|
+
sage: H.gen(0)._repr_()
|
|
280
|
+
'a'
|
|
281
|
+
sage: H.one()
|
|
282
|
+
1
|
|
283
|
+
"""
|
|
284
|
+
# computing that an element is actually one can be very expensive
|
|
285
|
+
if self.Tietze() == ():
|
|
286
|
+
return '1'
|
|
287
|
+
else:
|
|
288
|
+
return self.gap()._repr_()
|
|
289
|
+
|
|
290
|
+
@cached_method
|
|
291
|
+
def Tietze(self):
|
|
292
|
+
"""
|
|
293
|
+
Return the Tietze list of the element.
|
|
294
|
+
|
|
295
|
+
The Tietze list of a word is a list of integers that represent
|
|
296
|
+
the letters in the word. A positive integer `i` represents
|
|
297
|
+
the letter corresponding to the `i`-th generator of the group.
|
|
298
|
+
Negative integers represent the inverses of generators.
|
|
299
|
+
|
|
300
|
+
OUTPUT: tuple of integers
|
|
301
|
+
|
|
302
|
+
EXAMPLES::
|
|
303
|
+
|
|
304
|
+
sage: G = FreeGroup('a, b')
|
|
305
|
+
sage: H = G / (G([1]), G([2, 2, 2]))
|
|
306
|
+
sage: H.inject_variables()
|
|
307
|
+
Defining a, b
|
|
308
|
+
sage: a.Tietze()
|
|
309
|
+
(1,)
|
|
310
|
+
sage: x = a^2*b^(-3)*a^(-2)
|
|
311
|
+
sage: x.Tietze()
|
|
312
|
+
(1, 1, -2, -2, -2, -1, -1)
|
|
313
|
+
"""
|
|
314
|
+
tl = self.gap().UnderlyingElement().TietzeWordAbstractWord()
|
|
315
|
+
return tuple(tl.sage())
|
|
316
|
+
|
|
317
|
+
def __call__(self, *values, **kwds):
|
|
318
|
+
"""
|
|
319
|
+
Replace the generators of the free group with ``values``.
|
|
320
|
+
|
|
321
|
+
INPUT:
|
|
322
|
+
|
|
323
|
+
- ``*values`` -- list/tuple/iterable of the same length as
|
|
324
|
+
the number of generators
|
|
325
|
+
|
|
326
|
+
- ``check=True`` -- boolean keyword (default: ``True``); whether to
|
|
327
|
+
verify that ``values`` satisfy the relations in the finitely
|
|
328
|
+
presented group
|
|
329
|
+
|
|
330
|
+
OUTPUT: the product of ``values`` in the order and with exponents
|
|
331
|
+
specified by ``self``
|
|
332
|
+
|
|
333
|
+
EXAMPLES::
|
|
334
|
+
|
|
335
|
+
sage: G.<a,b> = FreeGroup()
|
|
336
|
+
sage: H = G / [a/b]; H
|
|
337
|
+
Finitely presented group < a, b | a*b^-1 >
|
|
338
|
+
sage: H.simplified()
|
|
339
|
+
Finitely presented group < a | >
|
|
340
|
+
|
|
341
|
+
The generator `b` can be eliminated using the relation `a=b`. Any
|
|
342
|
+
values that you plug into a word must satisfy this relation::
|
|
343
|
+
|
|
344
|
+
sage: A, B = H.gens()
|
|
345
|
+
sage: w = A^2 * B
|
|
346
|
+
sage: w(2,2)
|
|
347
|
+
8
|
|
348
|
+
sage: w(3,3)
|
|
349
|
+
27
|
|
350
|
+
sage: w(1,2)
|
|
351
|
+
Traceback (most recent call last):
|
|
352
|
+
...
|
|
353
|
+
ValueError: the values do not satisfy all relations of the group
|
|
354
|
+
sage: w(1, 2, check=False) # result depends on presentation of the group element
|
|
355
|
+
2
|
|
356
|
+
"""
|
|
357
|
+
values = list(values)
|
|
358
|
+
if kwds.get('check', True):
|
|
359
|
+
for rel in self.parent().relations():
|
|
360
|
+
rel = rel(values)
|
|
361
|
+
if rel != 1:
|
|
362
|
+
raise ValueError('the values do not satisfy all relations of the group')
|
|
363
|
+
return super().__call__(values)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
class RewritingSystem:
|
|
367
|
+
"""
|
|
368
|
+
A class that wraps GAP's rewriting systems.
|
|
369
|
+
|
|
370
|
+
A rewriting system is a set of rules that allow to transform
|
|
371
|
+
one word in the group to an equivalent one.
|
|
372
|
+
|
|
373
|
+
If the rewriting system is confluent, then the transformed
|
|
374
|
+
word is a unique reduced form of the element of the group.
|
|
375
|
+
|
|
376
|
+
.. WARNING::
|
|
377
|
+
|
|
378
|
+
Note that the process of making a rewriting system confluent
|
|
379
|
+
might not end.
|
|
380
|
+
|
|
381
|
+
INPUT:
|
|
382
|
+
|
|
383
|
+
- ``G`` -- a group
|
|
384
|
+
|
|
385
|
+
REFERENCES:
|
|
386
|
+
|
|
387
|
+
- :wikipedia:`Knuth-Bendix_completion_algorithm`
|
|
388
|
+
|
|
389
|
+
EXAMPLES::
|
|
390
|
+
|
|
391
|
+
sage: F.<a,b> = FreeGroup()
|
|
392
|
+
sage: G = F / [a*b/a/b]
|
|
393
|
+
sage: k = G.rewriting_system()
|
|
394
|
+
sage: k
|
|
395
|
+
Rewriting system of Finitely presented group < a, b | a*b*a^-1*b^-1 >
|
|
396
|
+
with rules:
|
|
397
|
+
a*b*a^-1*b^-1 ---> 1
|
|
398
|
+
|
|
399
|
+
sage: k.reduce(a*b*a*b)
|
|
400
|
+
(a*b)^2
|
|
401
|
+
sage: k.make_confluent()
|
|
402
|
+
sage: k
|
|
403
|
+
Rewriting system of Finitely presented group < a, b | a*b*a^-1*b^-1 >
|
|
404
|
+
with rules:
|
|
405
|
+
b^-1*a^-1 ---> a^-1*b^-1
|
|
406
|
+
b^-1*a ---> a*b^-1
|
|
407
|
+
b*a^-1 ---> a^-1*b
|
|
408
|
+
b*a ---> a*b
|
|
409
|
+
|
|
410
|
+
sage: k.reduce(a*b*a*b)
|
|
411
|
+
a^2*b^2
|
|
412
|
+
|
|
413
|
+
.. TODO::
|
|
414
|
+
|
|
415
|
+
- Include support for different orderings (currently only shortlex
|
|
416
|
+
is used).
|
|
417
|
+
|
|
418
|
+
- Include the GAP package kbmag for more functionalities, including
|
|
419
|
+
automatic structures and faster compiled functions.
|
|
420
|
+
|
|
421
|
+
AUTHORS:
|
|
422
|
+
|
|
423
|
+
- Miguel Angel Marco Buzunariz (2013-12-16)
|
|
424
|
+
"""
|
|
425
|
+
def __init__(self, G):
|
|
426
|
+
"""
|
|
427
|
+
Initialize ``self``.
|
|
428
|
+
|
|
429
|
+
EXAMPLES::
|
|
430
|
+
|
|
431
|
+
sage: F.<a,b,c> = FreeGroup()
|
|
432
|
+
sage: G = F / [a^2, b^3, c^5]
|
|
433
|
+
sage: k = G.rewriting_system()
|
|
434
|
+
sage: k
|
|
435
|
+
Rewriting system of Finitely presented group < a, b, c | a^2, b^3, c^5 >
|
|
436
|
+
with rules:
|
|
437
|
+
a^2 ---> 1
|
|
438
|
+
b^3 ---> 1
|
|
439
|
+
c^5 ---> 1
|
|
440
|
+
"""
|
|
441
|
+
self._free_group = G.free_group()
|
|
442
|
+
self._fp_group = G
|
|
443
|
+
self._fp_group_gap = G.gap()
|
|
444
|
+
self._monoid_isomorphism = self._fp_group_gap.IsomorphismFpMonoid()
|
|
445
|
+
self._monoid = self._monoid_isomorphism.Image()
|
|
446
|
+
self._gap = self._monoid.KnuthBendixRewritingSystem()
|
|
447
|
+
|
|
448
|
+
def __repr__(self):
|
|
449
|
+
"""
|
|
450
|
+
Return a string representation.
|
|
451
|
+
|
|
452
|
+
EXAMPLES::
|
|
453
|
+
|
|
454
|
+
sage: F.<a> = FreeGroup()
|
|
455
|
+
sage: G = F / [a^2]
|
|
456
|
+
sage: k = G.rewriting_system()
|
|
457
|
+
sage: k
|
|
458
|
+
Rewriting system of Finitely presented group < a | a^2 >
|
|
459
|
+
with rules:
|
|
460
|
+
a^2 ---> 1
|
|
461
|
+
"""
|
|
462
|
+
ret = "Rewriting system of {}\nwith rules:".format(self._fp_group)
|
|
463
|
+
for i in sorted(self.rules().items()): # Make sure they are sorted to the repr is unique
|
|
464
|
+
ret += "\n {} ---> {}".format(i[0], i[1])
|
|
465
|
+
return ret
|
|
466
|
+
|
|
467
|
+
def free_group(self):
|
|
468
|
+
"""
|
|
469
|
+
The free group after which the rewriting system is defined.
|
|
470
|
+
|
|
471
|
+
EXAMPLES::
|
|
472
|
+
|
|
473
|
+
sage: F = FreeGroup(3)
|
|
474
|
+
sage: G = F / [ [1,2,3], [-1,-2,-3] ]
|
|
475
|
+
sage: k = G.rewriting_system()
|
|
476
|
+
sage: k.free_group()
|
|
477
|
+
Free Group on generators {x0, x1, x2}
|
|
478
|
+
"""
|
|
479
|
+
return self._free_group
|
|
480
|
+
|
|
481
|
+
def finitely_presented_group(self):
|
|
482
|
+
"""
|
|
483
|
+
The finitely presented group where the rewriting system is defined.
|
|
484
|
+
|
|
485
|
+
EXAMPLES::
|
|
486
|
+
|
|
487
|
+
sage: F = FreeGroup(3)
|
|
488
|
+
sage: G = F / [ [1,2,3], [-1,-2,-3], [1,1], [2,2] ]
|
|
489
|
+
sage: k = G.rewriting_system()
|
|
490
|
+
sage: k.make_confluent()
|
|
491
|
+
sage: k
|
|
492
|
+
Rewriting system of Finitely presented group < x0, x1, x2 | x0*x1*x2, x0^-1*x1^-1*x2^-1, x0^2, x1^2 >
|
|
493
|
+
with rules:
|
|
494
|
+
x0^-1 ---> x0
|
|
495
|
+
x1^-1 ---> x1
|
|
496
|
+
x2^-1 ---> x2
|
|
497
|
+
x0^2 ---> 1
|
|
498
|
+
x0*x1 ---> x2
|
|
499
|
+
x0*x2 ---> x1
|
|
500
|
+
x1*x0 ---> x2
|
|
501
|
+
x1^2 ---> 1
|
|
502
|
+
x1*x2 ---> x0
|
|
503
|
+
x2*x0 ---> x1
|
|
504
|
+
x2*x1 ---> x0
|
|
505
|
+
x2^2 ---> 1
|
|
506
|
+
sage: k.finitely_presented_group()
|
|
507
|
+
Finitely presented group < x0, x1, x2 | x0*x1*x2, x0^-1*x1^-1*x2^-1, x0^2, x1^2 >
|
|
508
|
+
"""
|
|
509
|
+
return self._fp_group
|
|
510
|
+
|
|
511
|
+
def reduce(self, element):
|
|
512
|
+
"""
|
|
513
|
+
Apply the rules in the rewriting system to the element, to obtain
|
|
514
|
+
a reduced form.
|
|
515
|
+
|
|
516
|
+
If the rewriting system is confluent, this reduced form is unique
|
|
517
|
+
for all words representing the same element.
|
|
518
|
+
|
|
519
|
+
EXAMPLES::
|
|
520
|
+
|
|
521
|
+
sage: F.<a,b> = FreeGroup()
|
|
522
|
+
sage: G = F/[a^2, b^3, (a*b/a)^3, b*a*b*a]
|
|
523
|
+
sage: k = G.rewriting_system()
|
|
524
|
+
sage: k.reduce(b^4)
|
|
525
|
+
b
|
|
526
|
+
sage: k.reduce(a*b*a)
|
|
527
|
+
a*b*a
|
|
528
|
+
"""
|
|
529
|
+
eg = self._fp_group(element).gap()
|
|
530
|
+
egim = self._monoid_isomorphism.Image(eg)
|
|
531
|
+
red = self.gap().ReducedForm(egim.UnderlyingElement())
|
|
532
|
+
redfpmon = self._monoid.One().FamilyObj().ElementOfFpMonoid(red)
|
|
533
|
+
reducfpgr = self._monoid_isomorphism.PreImagesRepresentative(redfpmon)
|
|
534
|
+
tz = reducfpgr.UnderlyingElement().TietzeWordAbstractWord(self._free_group.gap().GeneratorsOfGroup())
|
|
535
|
+
return self._fp_group(tz.sage())
|
|
536
|
+
|
|
537
|
+
def gap(self):
|
|
538
|
+
"""
|
|
539
|
+
The gap representation of the rewriting system.
|
|
540
|
+
|
|
541
|
+
EXAMPLES::
|
|
542
|
+
|
|
543
|
+
sage: F.<a,b> = FreeGroup()
|
|
544
|
+
sage: G = F/[a*a,b*b]
|
|
545
|
+
sage: k = G.rewriting_system()
|
|
546
|
+
sage: k.gap()
|
|
547
|
+
Knuth Bendix Rewriting System for Monoid( [ a, A, b, B ] ) with rules
|
|
548
|
+
[ [ a*A, <identity ...> ], [ A*a, <identity ...> ],
|
|
549
|
+
[ b*B, <identity ...> ], [ B*b, <identity ...> ],
|
|
550
|
+
[ a^2, <identity ...> ], [ b^2, <identity ...> ] ]
|
|
551
|
+
"""
|
|
552
|
+
return self._gap
|
|
553
|
+
|
|
554
|
+
def rules(self):
|
|
555
|
+
"""
|
|
556
|
+
Return the rules that form the rewriting system.
|
|
557
|
+
|
|
558
|
+
OUTPUT:
|
|
559
|
+
|
|
560
|
+
A dictionary containing the rules of the rewriting system.
|
|
561
|
+
Each key is a word in the free group, and its corresponding
|
|
562
|
+
value is the word to which it is reduced.
|
|
563
|
+
|
|
564
|
+
EXAMPLES::
|
|
565
|
+
|
|
566
|
+
sage: F.<a,b> = FreeGroup()
|
|
567
|
+
sage: G = F / [a*a*a,b*b*a*a]
|
|
568
|
+
sage: k = G.rewriting_system()
|
|
569
|
+
sage: k
|
|
570
|
+
Rewriting system of Finitely presented group < a, b | a^3, b^2*a^2 >
|
|
571
|
+
with rules:
|
|
572
|
+
a^3 ---> 1
|
|
573
|
+
b^2*a^2 ---> 1
|
|
574
|
+
|
|
575
|
+
sage: k.rules()
|
|
576
|
+
{a^3: 1, b^2*a^2: 1}
|
|
577
|
+
sage: k.make_confluent()
|
|
578
|
+
sage: sorted(k.rules().items())
|
|
579
|
+
[(a^-2, a), (a^-1*b^-1, a*b), (a^-1*b, b^-1), (a^2, a^-1),
|
|
580
|
+
(a*b^-1, b), (b^-1*a^-1, a*b), (b^-1*a, b), (b^-2, a^-1),
|
|
581
|
+
(b*a^-1, b^-1), (b*a, a*b), (b^2, a)]
|
|
582
|
+
"""
|
|
583
|
+
dic = {}
|
|
584
|
+
grules = self.gap().Rules()
|
|
585
|
+
for i in grules:
|
|
586
|
+
a, b = i
|
|
587
|
+
afpmon = self._monoid.One().FamilyObj().ElementOfFpMonoid(a)
|
|
588
|
+
afg = self._monoid_isomorphism.PreImagesRepresentative(afpmon)
|
|
589
|
+
atz = afg.UnderlyingElement().TietzeWordAbstractWord(self._free_group.gap().GeneratorsOfGroup())
|
|
590
|
+
af = self._free_group(atz.sage())
|
|
591
|
+
if len(af.Tietze()) != 0:
|
|
592
|
+
bfpmon = self._monoid.One().FamilyObj().ElementOfFpMonoid(b)
|
|
593
|
+
bfg = self._monoid_isomorphism.PreImagesRepresentative(bfpmon)
|
|
594
|
+
btz = bfg.UnderlyingElement().TietzeWordAbstractWord(self._free_group.gap().GeneratorsOfGroup())
|
|
595
|
+
bf = self._free_group(btz.sage())
|
|
596
|
+
dic[af] = bf
|
|
597
|
+
return dic
|
|
598
|
+
|
|
599
|
+
def is_confluent(self):
|
|
600
|
+
"""
|
|
601
|
+
Return ``True`` if the system is confluent and ``False`` otherwise.
|
|
602
|
+
|
|
603
|
+
EXAMPLES::
|
|
604
|
+
|
|
605
|
+
sage: F = FreeGroup(3)
|
|
606
|
+
sage: G = F / [F([1,2,1,2,1,3,-1]),F([2,2,2,1,1,2]),F([1,2,3])]
|
|
607
|
+
sage: k = G.rewriting_system()
|
|
608
|
+
sage: k.is_confluent()
|
|
609
|
+
False
|
|
610
|
+
sage: k
|
|
611
|
+
Rewriting system of Finitely presented group < x0, x1, x2 | (x0*x1)^2*x0*x2*x0^-1, x1^3*x0^2*x1, x0*x1*x2 >
|
|
612
|
+
with rules:
|
|
613
|
+
x0*x1*x2 ---> 1
|
|
614
|
+
x1^3*x0^2*x1 ---> 1
|
|
615
|
+
(x0*x1)^2*x0*x2*x0^-1 ---> 1
|
|
616
|
+
|
|
617
|
+
sage: k.make_confluent()
|
|
618
|
+
sage: k.is_confluent()
|
|
619
|
+
True
|
|
620
|
+
sage: k
|
|
621
|
+
Rewriting system of Finitely presented group < x0, x1, x2 | (x0*x1)^2*x0*x2*x0^-1, x1^3*x0^2*x1, x0*x1*x2 >
|
|
622
|
+
with rules:
|
|
623
|
+
x0^-1 ---> x0
|
|
624
|
+
x1^-1 ---> x1
|
|
625
|
+
x0^2 ---> 1
|
|
626
|
+
x0*x1 ---> x2^-1
|
|
627
|
+
x0*x2^-1 ---> x1
|
|
628
|
+
x1*x0 ---> x2
|
|
629
|
+
x1^2 ---> 1
|
|
630
|
+
x1*x2^-1 ---> x0*x2
|
|
631
|
+
x1*x2 ---> x0
|
|
632
|
+
x2^-1*x0 ---> x0*x2
|
|
633
|
+
x2^-1*x1 ---> x0
|
|
634
|
+
x2^-2 ---> x2
|
|
635
|
+
x2*x0 ---> x1
|
|
636
|
+
x2*x1 ---> x0*x2
|
|
637
|
+
x2^2 ---> x2^-1
|
|
638
|
+
"""
|
|
639
|
+
return self._gap.IsConfluent().sage()
|
|
640
|
+
|
|
641
|
+
def make_confluent(self):
|
|
642
|
+
"""
|
|
643
|
+
Apply the Knuth-Bendix algorithm to try to transform the rewriting
|
|
644
|
+
system into a confluent one.
|
|
645
|
+
|
|
646
|
+
Note that this method does not return any object, just changes the
|
|
647
|
+
rewriting system internally.
|
|
648
|
+
|
|
649
|
+
.. WARNING::
|
|
650
|
+
|
|
651
|
+
This algorithm is not granted to finish. Although it may be useful
|
|
652
|
+
in some occasions to run it, interrupt it manually after some time
|
|
653
|
+
and use then the transformed rewriting system. Even if it is not
|
|
654
|
+
confluent, it could be used to reduce some words.
|
|
655
|
+
|
|
656
|
+
ALGORITHM:
|
|
657
|
+
|
|
658
|
+
Uses GAP's ``MakeConfluent``.
|
|
659
|
+
|
|
660
|
+
EXAMPLES::
|
|
661
|
+
|
|
662
|
+
sage: F.<a,b> = FreeGroup()
|
|
663
|
+
sage: G = F / [a^2,b^3,(a*b/a)^3,b*a*b*a]
|
|
664
|
+
sage: k = G.rewriting_system()
|
|
665
|
+
sage: k
|
|
666
|
+
Rewriting system of Finitely presented group < a, b | a^2, b^3, a*b^3*a^-1, (b*a)^2 >
|
|
667
|
+
with rules:
|
|
668
|
+
a^2 ---> 1
|
|
669
|
+
b^3 ---> 1
|
|
670
|
+
(b*a)^2 ---> 1
|
|
671
|
+
a*b^3*a^-1 ---> 1
|
|
672
|
+
|
|
673
|
+
sage: k.make_confluent()
|
|
674
|
+
sage: k
|
|
675
|
+
Rewriting system of Finitely presented group < a, b | a^2, b^3, a*b^3*a^-1, (b*a)^2 >
|
|
676
|
+
with rules:
|
|
677
|
+
a^-1 ---> a
|
|
678
|
+
a^2 ---> 1
|
|
679
|
+
b^-1*a ---> a*b
|
|
680
|
+
b^-2 ---> b
|
|
681
|
+
b*a ---> a*b^-1
|
|
682
|
+
b^2 ---> b^-1
|
|
683
|
+
"""
|
|
684
|
+
try:
|
|
685
|
+
self._gap.MakeConfluent()
|
|
686
|
+
except ValueError:
|
|
687
|
+
raise ValueError('could not make the system confluent')
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
@richcmp_method
|
|
691
|
+
class FinitelyPresentedGroup(GroupMixinLibGAP, CachedRepresentation, Group, ParentLibGAP):
|
|
692
|
+
"""
|
|
693
|
+
A class that wraps GAP's Finitely Presented Groups.
|
|
694
|
+
|
|
695
|
+
.. WARNING::
|
|
696
|
+
|
|
697
|
+
You should use
|
|
698
|
+
:meth:`~sage.groups.free_group.FreeGroup_class.quotient` to
|
|
699
|
+
construct finitely presented groups as quotients of free
|
|
700
|
+
groups. Any class inheriting this one should define
|
|
701
|
+
``__reduce__ = CachedRepresentation.__reduce__``
|
|
702
|
+
after importing ``CachedRepresentation``.
|
|
703
|
+
|
|
704
|
+
EXAMPLES::
|
|
705
|
+
|
|
706
|
+
sage: G.<a,b> = FreeGroup()
|
|
707
|
+
sage: H = G / [a, b^3]
|
|
708
|
+
sage: H
|
|
709
|
+
Finitely presented group < a, b | a, b^3 >
|
|
710
|
+
sage: H.gens()
|
|
711
|
+
(a, b)
|
|
712
|
+
|
|
713
|
+
sage: F.<a,b> = FreeGroup('a, b')
|
|
714
|
+
sage: J = F / (F([1]), F([2, 2, 2]))
|
|
715
|
+
sage: J is H
|
|
716
|
+
True
|
|
717
|
+
|
|
718
|
+
sage: G = FreeGroup(2)
|
|
719
|
+
sage: H = G / (G([1, 1]), G([2, 2, 2]))
|
|
720
|
+
sage: H.gens()
|
|
721
|
+
(x0, x1)
|
|
722
|
+
sage: H.gen(0)
|
|
723
|
+
x0
|
|
724
|
+
sage: H.ngens()
|
|
725
|
+
2
|
|
726
|
+
sage: H.gap()
|
|
727
|
+
<fp group on the generators [ x0, x1 ]>
|
|
728
|
+
sage: type(_)
|
|
729
|
+
<class 'sage.libs.gap.element.GapElement'>
|
|
730
|
+
"""
|
|
731
|
+
Element = FinitelyPresentedGroupElement
|
|
732
|
+
|
|
733
|
+
def __init__(self, free_group, relations, category=None, libgap_fpgroup=None):
|
|
734
|
+
"""
|
|
735
|
+
The Python constructor.
|
|
736
|
+
|
|
737
|
+
TESTS::
|
|
738
|
+
|
|
739
|
+
sage: G = FreeGroup('a, b')
|
|
740
|
+
sage: H = G / (G([1]), G([2])^3)
|
|
741
|
+
sage: H
|
|
742
|
+
Finitely presented group < a, b | a, b^3 >
|
|
743
|
+
|
|
744
|
+
sage: F = FreeGroup('a, b')
|
|
745
|
+
sage: J = F / (F([1]), F([2, 2, 2]))
|
|
746
|
+
sage: J is H
|
|
747
|
+
True
|
|
748
|
+
|
|
749
|
+
sage: A5 = libgap(AlternatingGroup(5))
|
|
750
|
+
sage: A5gapfp = A5.IsomorphismFpGroup().Range()
|
|
751
|
+
sage: A5gapfp
|
|
752
|
+
<fp group of size 60 on the generators [ A_5.1, A_5.2 ]>
|
|
753
|
+
sage: A5sage = A5gapfp.sage(); A5sage;
|
|
754
|
+
Finitely presented group < A_5.1, A_5.2 | A_5.1^5*A_5.2^-5, A_5.1^5*(A_5.2^-1*A_5.1^-1)^2, (A_5.1^-2*A_5.2^2)^2 >
|
|
755
|
+
sage: A5sage.inject_variables()
|
|
756
|
+
Traceback (most recent call last):
|
|
757
|
+
...
|
|
758
|
+
ValueError: variable names have not yet been set using self._assign_names(...)
|
|
759
|
+
|
|
760
|
+
Check that pickling works::
|
|
761
|
+
|
|
762
|
+
sage: G = FreeGroup(2) / [2 * (1, 2, -1, -2)]
|
|
763
|
+
sage: loads(dumps(G))
|
|
764
|
+
Finitely presented group < x0, x1 | (x0*x1*x0^-1*x1^-1)^2 >
|
|
765
|
+
sage: G.__reduce__()[1][1]
|
|
766
|
+
(Free Group on generators {x0, x1}, ((x0*x1*x0^-1*x1^-1)^2,))
|
|
767
|
+
|
|
768
|
+
sage: TestSuite(H).run()
|
|
769
|
+
sage: TestSuite(J).run()
|
|
770
|
+
"""
|
|
771
|
+
from sage.groups.free_group import is_FreeGroup
|
|
772
|
+
assert is_FreeGroup(free_group)
|
|
773
|
+
assert isinstance(relations, tuple)
|
|
774
|
+
self._free_group = free_group
|
|
775
|
+
self._relations = relations
|
|
776
|
+
try:
|
|
777
|
+
self._assign_names(free_group.variable_names())
|
|
778
|
+
except ValueError:
|
|
779
|
+
pass
|
|
780
|
+
if libgap_fpgroup is None:
|
|
781
|
+
libgap_fpgroup = free_group.gap() / libgap([rel.gap() for rel in relations])
|
|
782
|
+
ParentLibGAP.__init__(self, libgap_fpgroup)
|
|
783
|
+
Group.__init__(self, category=category)
|
|
784
|
+
|
|
785
|
+
def __hash__(self):
|
|
786
|
+
"""
|
|
787
|
+
Make hashable.
|
|
788
|
+
|
|
789
|
+
EXAMPLES::
|
|
790
|
+
|
|
791
|
+
sage: G = FreeGroup(2) / [(1, 2, 2, 1)]
|
|
792
|
+
sage: G.__hash__() == hash((G.free_group(), G.relations()))
|
|
793
|
+
True
|
|
794
|
+
"""
|
|
795
|
+
return hash((self._free_group, self._relations))
|
|
796
|
+
|
|
797
|
+
def __richcmp__(self, other, op):
|
|
798
|
+
"""
|
|
799
|
+
Rich comparison of ``self`` and ``other``.
|
|
800
|
+
|
|
801
|
+
EXAMPLES::
|
|
802
|
+
|
|
803
|
+
sage: G1 = FreeGroup(2) / [(1, 2, 2, 1, 2, 1)]
|
|
804
|
+
sage: G2 = libgap(G1).sage()
|
|
805
|
+
sage: G1 == G2
|
|
806
|
+
True
|
|
807
|
+
sage: G1 is G2
|
|
808
|
+
False
|
|
809
|
+
"""
|
|
810
|
+
if not isinstance(other, self.__class__):
|
|
811
|
+
from sage.structure.richcmp import op_NE
|
|
812
|
+
return (op == op_NE)
|
|
813
|
+
self_data = (self._free_group, self._relations)
|
|
814
|
+
other_data = (other._free_group, other._relations)
|
|
815
|
+
return richcmp(self_data, other_data, op)
|
|
816
|
+
|
|
817
|
+
def _repr_(self) -> str:
|
|
818
|
+
"""
|
|
819
|
+
Return a string representation.
|
|
820
|
+
|
|
821
|
+
TESTS::
|
|
822
|
+
|
|
823
|
+
sage: G.<a,b> = FreeGroup()
|
|
824
|
+
sage: H = G / (G([1]), G([2])^3)
|
|
825
|
+
sage: H # indirect doctest
|
|
826
|
+
Finitely presented group < a, b | a, b^3 >
|
|
827
|
+
sage: H._repr_()
|
|
828
|
+
'Finitely presented group < a, b | a, b^3 >'
|
|
829
|
+
"""
|
|
830
|
+
gens = ', '.join(self._free_group._gen_names)
|
|
831
|
+
rels = ', '.join(str(r) for r in self.relations())
|
|
832
|
+
return 'Finitely presented group ' + '< ' + gens + ' | ' + rels + ' >'
|
|
833
|
+
|
|
834
|
+
def _latex_(self):
|
|
835
|
+
"""
|
|
836
|
+
Return a LaTeX representation.
|
|
837
|
+
|
|
838
|
+
OUTPUT: string; a valid LaTeX math command sequence
|
|
839
|
+
|
|
840
|
+
TESTS::
|
|
841
|
+
|
|
842
|
+
sage: F = FreeGroup(4)
|
|
843
|
+
sage: F.inject_variables()
|
|
844
|
+
Defining x0, x1, x2, x3
|
|
845
|
+
sage: G = F.quotient([x0*x2, x3*x1*x3, x2*x1*x2])
|
|
846
|
+
sage: G._latex_()
|
|
847
|
+
'\\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'
|
|
848
|
+
"""
|
|
849
|
+
r = '\\langle '
|
|
850
|
+
for i in range(self.ngens()):
|
|
851
|
+
r = r+self.gen(i)._latex_()
|
|
852
|
+
if i < self.ngens()-1:
|
|
853
|
+
r = r+', '
|
|
854
|
+
r = r+' \\mid '
|
|
855
|
+
for i in range(len(self._relations)):
|
|
856
|
+
r = r+(self._relations)[i]._latex_()
|
|
857
|
+
if i < len(self.relations())-1:
|
|
858
|
+
r = r+' , '
|
|
859
|
+
r = r+'\\rangle'
|
|
860
|
+
return r
|
|
861
|
+
|
|
862
|
+
def free_group(self):
|
|
863
|
+
"""
|
|
864
|
+
Return the free group (without relations).
|
|
865
|
+
|
|
866
|
+
OUTPUT: a :func:`~sage.groups.free_group.FreeGroup`
|
|
867
|
+
|
|
868
|
+
EXAMPLES::
|
|
869
|
+
|
|
870
|
+
sage: G.<a,b,c> = FreeGroup()
|
|
871
|
+
sage: H = G / (a^2, b^3, a*b*~a*~b)
|
|
872
|
+
sage: H.free_group()
|
|
873
|
+
Free Group on generators {a, b, c}
|
|
874
|
+
sage: H.free_group() is G
|
|
875
|
+
True
|
|
876
|
+
"""
|
|
877
|
+
return self._free_group
|
|
878
|
+
|
|
879
|
+
def relations(self):
|
|
880
|
+
"""
|
|
881
|
+
Return the relations of the group.
|
|
882
|
+
|
|
883
|
+
OUTPUT: the relations as a tuple of elements of :meth:`free_group`
|
|
884
|
+
|
|
885
|
+
EXAMPLES::
|
|
886
|
+
|
|
887
|
+
sage: F = FreeGroup(5, 'x')
|
|
888
|
+
sage: F.inject_variables()
|
|
889
|
+
Defining x0, x1, x2, x3, x4
|
|
890
|
+
sage: G = F.quotient([x0*x2, x3*x1*x3, x2*x1*x2])
|
|
891
|
+
sage: G.relations()
|
|
892
|
+
(x0*x2, x3*x1*x3, x2*x1*x2)
|
|
893
|
+
sage: all(rel in F for rel in G.relations())
|
|
894
|
+
True
|
|
895
|
+
"""
|
|
896
|
+
return self._relations
|
|
897
|
+
|
|
898
|
+
@cached_method
|
|
899
|
+
def cardinality(self, limit=4096000):
|
|
900
|
+
"""
|
|
901
|
+
Compute the cardinality of ``self``.
|
|
902
|
+
|
|
903
|
+
INPUT:
|
|
904
|
+
|
|
905
|
+
- ``limit`` -- integer (default: 4096000); the maximal number
|
|
906
|
+
of cosets before the computation is aborted
|
|
907
|
+
|
|
908
|
+
OUTPUT: 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
|
+
:exc:`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 possible 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 [Joh1990]_ (p. 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`` -- boolean (default: ``False``); if ``True``, then
|
|
1022
|
+
attempt to reduce the presentation of the product group
|
|
1023
|
+
|
|
1024
|
+
- ``new_names`` -- boolean (default: ``True``); 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 may change meaning
|
|
1030
|
+
in the output group if its presentation is reduced.
|
|
1031
|
+
|
|
1032
|
+
OUTPUT: the direct product of ``self`` with ``H`` as a finitely
|
|
1033
|
+
presented group
|
|
1034
|
+
|
|
1035
|
+
EXAMPLES::
|
|
1036
|
+
|
|
1037
|
+
sage: G = FreeGroup()
|
|
1038
|
+
sage: C12 = ( G / [G([1,1,1,1])] ).direct_product( G / [G([1,1,1])]); C12
|
|
1039
|
+
Finitely presented group < a, b | a^4, b^3, a^-1*b^-1*a*b >
|
|
1040
|
+
sage: C12.order(), C12.as_permutation_group().is_cyclic()
|
|
1041
|
+
(12, True)
|
|
1042
|
+
sage: klein = ( G / [G([1,1])] ).direct_product( G / [G([1,1])]); klein
|
|
1043
|
+
Finitely presented group < a, b | a^2, b^2, a^-1*b^-1*a*b >
|
|
1044
|
+
sage: klein.order(), klein.as_permutation_group().is_cyclic()
|
|
1045
|
+
(4, False)
|
|
1046
|
+
|
|
1047
|
+
We can keep the variable names from ``self`` and ``H`` to examine how
|
|
1048
|
+
new relations are formed::
|
|
1049
|
+
|
|
1050
|
+
sage: F = FreeGroup("a"); G = FreeGroup("g")
|
|
1051
|
+
sage: X = G / [G.0^12]; A = F / [F.0^6]
|
|
1052
|
+
sage: X.direct_product(A, new_names=False)
|
|
1053
|
+
Finitely presented group < g, a | g^12, a^6, g^-1*a^-1*g*a >
|
|
1054
|
+
sage: A.direct_product(X, new_names=False)
|
|
1055
|
+
Finitely presented group < a, g | a^6, g^12, a^-1*g^-1*a*g >
|
|
1056
|
+
|
|
1057
|
+
Or we can attempt to reduce the output group presentation::
|
|
1058
|
+
|
|
1059
|
+
sage: F = FreeGroup("a"); G = FreeGroup("g")
|
|
1060
|
+
sage: X = G / [G.0]; A = F / [F.0]
|
|
1061
|
+
sage: X.direct_product(A, new_names=True)
|
|
1062
|
+
Finitely presented group < a, b | a, b, a^-1*b^-1*a*b >
|
|
1063
|
+
sage: X.direct_product(A, reduced=True, new_names=True)
|
|
1064
|
+
Finitely presented group < | >
|
|
1065
|
+
|
|
1066
|
+
But we cannot do both::
|
|
1067
|
+
|
|
1068
|
+
sage: K = FreeGroup(['a','b'])
|
|
1069
|
+
sage: D = K / [K.0^5, K.1^8]
|
|
1070
|
+
sage: D.direct_product(D, reduced=True, new_names=False)
|
|
1071
|
+
Traceback (most recent call last):
|
|
1072
|
+
...
|
|
1073
|
+
ValueError: cannot reduce output and keep old variable names
|
|
1074
|
+
|
|
1075
|
+
TESTS::
|
|
1076
|
+
|
|
1077
|
+
sage: G = FreeGroup()
|
|
1078
|
+
sage: Dp = (G / [G([1,1])]).direct_product( G / [G([1,1,1,1,1,1])] )
|
|
1079
|
+
sage: Dp.as_permutation_group().is_isomorphic(PermutationGroup(['(1,2)','(3,4,5,6,7,8)']))
|
|
1080
|
+
True
|
|
1081
|
+
sage: C7 = G / [G.0**7]; C6 = G / [G.0**6]
|
|
1082
|
+
sage: C14 = G / [G.0**14]; C3 = G / [G.0**3]
|
|
1083
|
+
sage: C7.direct_product(C6).is_isomorphic(C14.direct_product(C3))
|
|
1084
|
+
True
|
|
1085
|
+
sage: F = FreeGroup(2); D = F / [F([1,1,1,1,1]),F([2,2]),F([1,2])**2]
|
|
1086
|
+
sage: D.direct_product(D).as_permutation_group().is_isomorphic(
|
|
1087
|
+
....: direct_product_permgroups([DihedralGroup(5),DihedralGroup(5)]))
|
|
1088
|
+
True
|
|
1089
|
+
|
|
1090
|
+
AUTHORS:
|
|
1091
|
+
|
|
1092
|
+
- Davis Shurbert (2013-07-20): initial version
|
|
1093
|
+
"""
|
|
1094
|
+
from sage.groups.free_group import FreeGroup, _lexi_gen
|
|
1095
|
+
|
|
1096
|
+
if not isinstance(H, FinitelyPresentedGroup):
|
|
1097
|
+
raise TypeError("input must be a finitely presented group")
|
|
1098
|
+
if reduced and not new_names:
|
|
1099
|
+
raise ValueError("cannot reduce output and keep old variable names")
|
|
1100
|
+
|
|
1101
|
+
fp_product = libgap.DirectProduct([self.gap(), H.gap()])
|
|
1102
|
+
GAP_gens = fp_product.FreeGeneratorsOfFpGroup()
|
|
1103
|
+
if new_names:
|
|
1104
|
+
name_itr = _lexi_gen() # Python generator for lexicographical variable names
|
|
1105
|
+
gen_names = [next(name_itr) for i in GAP_gens]
|
|
1106
|
+
else:
|
|
1107
|
+
gen_names = [str(g) for g in self.gens()] + [str(g) for g in H.gens()]
|
|
1108
|
+
# Build the direct product in Sage for better variable names
|
|
1109
|
+
ret_F = FreeGroup(gen_names)
|
|
1110
|
+
ret_rls = tuple([ret_F(rel_word.TietzeWordAbstractWord(GAP_gens).sage())
|
|
1111
|
+
for rel_word in fp_product.RelatorsOfFpGroup()])
|
|
1112
|
+
ret_fpg = FinitelyPresentedGroup(ret_F, ret_rls)
|
|
1113
|
+
if reduced:
|
|
1114
|
+
ret_fpg = ret_fpg.simplified()
|
|
1115
|
+
return ret_fpg
|
|
1116
|
+
|
|
1117
|
+
def semidirect_product(self, H, hom, check=True, reduced=False):
|
|
1118
|
+
r"""
|
|
1119
|
+
The semidirect product of ``self`` with ``H`` via ``hom``.
|
|
1120
|
+
|
|
1121
|
+
If there exists a homomorphism `\phi` from a group `G` to the
|
|
1122
|
+
automorphism group of a group `H`, then we can define the semidirect
|
|
1123
|
+
product of `G` with `H` via `\phi` as the Cartesian product of `G`
|
|
1124
|
+
and `H` with the operation
|
|
1125
|
+
|
|
1126
|
+
.. MATH::
|
|
1127
|
+
|
|
1128
|
+
(g_1, h_1)(g_2, h_2) = (g_1 g_2, \phi(g_2)(h_1) h_2).
|
|
1129
|
+
|
|
1130
|
+
INPUT:
|
|
1131
|
+
|
|
1132
|
+
- ``H`` -- finitely presented group which is implicitly acted on
|
|
1133
|
+
by ``self`` and can be naturally embedded as a normal subgroup
|
|
1134
|
+
of the semidirect product
|
|
1135
|
+
|
|
1136
|
+
- ``hom`` -- homomorphism from ``self`` to the automorphism group
|
|
1137
|
+
of ``H``. Given as a pair, with generators of ``self`` in the
|
|
1138
|
+
first slot and the images of the corresponding generators in the
|
|
1139
|
+
second. These images must be automorphisms of ``H``, given again
|
|
1140
|
+
as a pair of generators and images.
|
|
1141
|
+
|
|
1142
|
+
- ``check`` -- boolean (default: ``True``); if ``False`` the defining
|
|
1143
|
+
homomorphism and automorphism images are not tested for validity.
|
|
1144
|
+
This test can be costly with large groups, so it can be bypassed
|
|
1145
|
+
if the user is confident that his morphisms are valid.
|
|
1146
|
+
|
|
1147
|
+
- ``reduced`` -- boolean (default: ``False``); if ``True`` then the
|
|
1148
|
+
method attempts to reduce the presentation of the output group
|
|
1149
|
+
|
|
1150
|
+
OUTPUT:
|
|
1151
|
+
|
|
1152
|
+
The semidirect product of ``self`` with ``H`` via ``hom`` as a
|
|
1153
|
+
finitely presented group. See
|
|
1154
|
+
:meth:`PermutationGroup_generic.semidirect_product
|
|
1155
|
+
<sage.groups.perm_gps.permgroup.PermutationGroup_generic.semidirect_product>`
|
|
1156
|
+
for a more in depth explanation of a semidirect product.
|
|
1157
|
+
|
|
1158
|
+
AUTHORS:
|
|
1159
|
+
|
|
1160
|
+
- Davis Shurbert (8-1-2013)
|
|
1161
|
+
|
|
1162
|
+
EXAMPLES:
|
|
1163
|
+
|
|
1164
|
+
Group of order 12 as two isomorphic semidirect products::
|
|
1165
|
+
|
|
1166
|
+
sage: D4 = groups.presentation.Dihedral(4)
|
|
1167
|
+
sage: C3 = groups.presentation.Cyclic(3)
|
|
1168
|
+
sage: alpha1 = ([C3.gen(0)],[C3.gen(0)])
|
|
1169
|
+
sage: alpha2 = ([C3.gen(0)],[C3([1,1])])
|
|
1170
|
+
sage: S1 = D4.semidirect_product(C3, ([D4.gen(1), D4.gen(0)],[alpha1,alpha2]))
|
|
1171
|
+
sage: C2 = groups.presentation.Cyclic(2)
|
|
1172
|
+
sage: Q = groups.presentation.DiCyclic(3)
|
|
1173
|
+
sage: a = Q([1]); b = Q([-2])
|
|
1174
|
+
sage: alpha = (Q.gens(), [a,b])
|
|
1175
|
+
sage: S2 = C2.semidirect_product(Q, ([C2.0],[alpha]))
|
|
1176
|
+
sage: S1.is_isomorphic(S2)
|
|
1177
|
+
True
|
|
1178
|
+
|
|
1179
|
+
Dihedral groups can be constructed as semidirect products
|
|
1180
|
+
of cyclic groups::
|
|
1181
|
+
|
|
1182
|
+
sage: C2 = groups.presentation.Cyclic(2)
|
|
1183
|
+
sage: C8 = groups.presentation.Cyclic(8)
|
|
1184
|
+
sage: hom = (C2.gens(), [ ([C8([1])], [C8([-1])]) ])
|
|
1185
|
+
sage: D = C2.semidirect_product(C8, hom)
|
|
1186
|
+
sage: D.as_permutation_group().is_isomorphic(DihedralGroup(8))
|
|
1187
|
+
True
|
|
1188
|
+
|
|
1189
|
+
You can attempt to reduce the presentation of the output group::
|
|
1190
|
+
|
|
1191
|
+
sage: D = C2.semidirect_product(C8, hom); D
|
|
1192
|
+
Finitely presented group < a, b | a^2, b^8, a^-1*b*a*b >
|
|
1193
|
+
sage: D = C2.semidirect_product(C8, hom, reduced=True); D
|
|
1194
|
+
Finitely presented group < a, b | a^2, a*b*a*b, b^8 >
|
|
1195
|
+
|
|
1196
|
+
sage: C3 = groups.presentation.Cyclic(3)
|
|
1197
|
+
sage: C4 = groups.presentation.Cyclic(4)
|
|
1198
|
+
sage: hom = (C3.gens(), [(C4.gens(), C4.gens())])
|
|
1199
|
+
sage: C3.semidirect_product(C4, hom)
|
|
1200
|
+
Finitely presented group < a, b | a^3, b^4, a^-1*b*a*b^-1 >
|
|
1201
|
+
sage: D = C3.semidirect_product(C4, hom, reduced=True); D
|
|
1202
|
+
Finitely presented group < a, b | a^3, b^4, a^-1*b*a*b^-1 >
|
|
1203
|
+
sage: D.as_permutation_group().is_cyclic()
|
|
1204
|
+
True
|
|
1205
|
+
|
|
1206
|
+
You can turn off the checks for the validity of the input morphisms.
|
|
1207
|
+
This check is expensive but behavior is unpredictable if inputs are
|
|
1208
|
+
invalid and are not caught by these tests::
|
|
1209
|
+
|
|
1210
|
+
sage: C5 = groups.presentation.Cyclic(5)
|
|
1211
|
+
sage: C12 = groups.presentation.Cyclic(12)
|
|
1212
|
+
sage: hom = (C5.gens(), [(C12.gens(), C12.gens())])
|
|
1213
|
+
sage: sp = C5.semidirect_product(C12, hom, check=False); sp
|
|
1214
|
+
Finitely presented group < a, b | a^5, b^12, a^-1*b*a*b^-1 >
|
|
1215
|
+
sage: sp.as_permutation_group().is_cyclic(), sp.order()
|
|
1216
|
+
(True, 60)
|
|
1217
|
+
|
|
1218
|
+
TESTS:
|
|
1219
|
+
|
|
1220
|
+
The following was fixed in Gap-4.7.2::
|
|
1221
|
+
|
|
1222
|
+
sage: C5.semidirect_product(C12, hom) == sp
|
|
1223
|
+
True
|
|
1224
|
+
|
|
1225
|
+
A more complicated semidirect product::
|
|
1226
|
+
|
|
1227
|
+
sage: C = groups.presentation.Cyclic(7)
|
|
1228
|
+
sage: D = groups.presentation.Dihedral(5)
|
|
1229
|
+
sage: id1 = ([C.0], [(D.gens(),D.gens())])
|
|
1230
|
+
sage: Se1 = C.semidirect_product(D, id1)
|
|
1231
|
+
sage: id2 = (D.gens(), [(C.gens(),C.gens()),(C.gens(),C.gens())])
|
|
1232
|
+
sage: Se2 = D.semidirect_product(C ,id2)
|
|
1233
|
+
sage: Dp1 = C.direct_product(D)
|
|
1234
|
+
sage: Dp1.is_isomorphic(Se1), Dp1.is_isomorphic(Se2)
|
|
1235
|
+
(True, True)
|
|
1236
|
+
|
|
1237
|
+
Most checks for validity of input are left to GAP to handle::
|
|
1238
|
+
|
|
1239
|
+
sage: bad_aut = ([C.0], [(D.gens(),[D.0, D.0])])
|
|
1240
|
+
sage: C.semidirect_product(D, bad_aut)
|
|
1241
|
+
Traceback (most recent call last):
|
|
1242
|
+
...
|
|
1243
|
+
ValueError: images of input homomorphism must be automorphisms
|
|
1244
|
+
sage: bad_hom = ([D.0, D.1], [(C.gens(),C.gens())])
|
|
1245
|
+
sage: D.semidirect_product(C, bad_hom)
|
|
1246
|
+
Traceback (most recent call last):
|
|
1247
|
+
...
|
|
1248
|
+
GAPError: Error, <gens> and <imgs> must be lists of same length
|
|
1249
|
+
"""
|
|
1250
|
+
from sage.groups.free_group import FreeGroup, _lexi_gen
|
|
1251
|
+
|
|
1252
|
+
if not isinstance(H, FinitelyPresentedGroup):
|
|
1253
|
+
raise TypeError("input must be a finitely presented group")
|
|
1254
|
+
|
|
1255
|
+
GAP_self = self.gap()
|
|
1256
|
+
GAP_H = H.gap()
|
|
1257
|
+
auto_grp = libgap.AutomorphismGroup(H.gap())
|
|
1258
|
+
self_gens = [h.gap() for h in hom[0]]
|
|
1259
|
+
# construct image automorphisms in GAP
|
|
1260
|
+
GAP_aut_imgs = [libgap.GroupHomomorphismByImages(GAP_H, GAP_H, [g.gap() for g in gns],
|
|
1261
|
+
[i.gap() for i in img]) for (gns, img) in hom[1]]
|
|
1262
|
+
|
|
1263
|
+
# check for automorphism validity in images of operation defining homomorphism,
|
|
1264
|
+
# and construct the defining homomorphism.
|
|
1265
|
+
if check:
|
|
1266
|
+
if not all(a in libgap.List(libgap.AutomorphismGroup(GAP_H))
|
|
1267
|
+
for a in GAP_aut_imgs):
|
|
1268
|
+
raise ValueError("images of input homomorphism must be automorphisms")
|
|
1269
|
+
GAP_def_hom = libgap.GroupHomomorphismByImages(GAP_self, auto_grp, self_gens, GAP_aut_imgs)
|
|
1270
|
+
else:
|
|
1271
|
+
GAP_def_hom = GAP_self.GroupHomomorphismByImagesNC(auto_grp, self_gens, GAP_aut_imgs)
|
|
1272
|
+
|
|
1273
|
+
prod = libgap.SemidirectProduct(GAP_self, GAP_def_hom, GAP_H)
|
|
1274
|
+
# Convert pc group to fp group
|
|
1275
|
+
if prod.IsPcGroup():
|
|
1276
|
+
prod = libgap.Image(libgap.IsomorphismFpGroupByPcgs(prod.FamilyPcgs(), 'x'))
|
|
1277
|
+
if not prod.IsFpGroup():
|
|
1278
|
+
raise NotImplementedError("unable to convert GAP output to equivalent Sage fp group")
|
|
1279
|
+
|
|
1280
|
+
# Convert GAP group object to Sage via Tietze
|
|
1281
|
+
# lists for readability of variable names
|
|
1282
|
+
GAP_gens = prod.FreeGeneratorsOfFpGroup()
|
|
1283
|
+
name_itr = _lexi_gen() # Python generator for lexicographical variable names
|
|
1284
|
+
ret_F = FreeGroup([next(name_itr) for i in GAP_gens])
|
|
1285
|
+
ret_rls = tuple([ret_F(rel_word.TietzeWordAbstractWord(GAP_gens).sage())
|
|
1286
|
+
for rel_word in prod.RelatorsOfFpGroup()])
|
|
1287
|
+
ret_fpg = FinitelyPresentedGroup(ret_F, ret_rls)
|
|
1288
|
+
if reduced:
|
|
1289
|
+
ret_fpg = ret_fpg.simplified()
|
|
1290
|
+
return ret_fpg
|
|
1291
|
+
|
|
1292
|
+
def _element_constructor_(self, *args, **kwds):
|
|
1293
|
+
"""
|
|
1294
|
+
Construct an element of ``self``.
|
|
1295
|
+
|
|
1296
|
+
TESTS::
|
|
1297
|
+
|
|
1298
|
+
sage: G.<a,b> = FreeGroup()
|
|
1299
|
+
sage: H = G / (G([1]), G([2, 2, 2]))
|
|
1300
|
+
sage: H([1, 2, 1, -1]) # indirect doctest
|
|
1301
|
+
a*b
|
|
1302
|
+
sage: H([1, 2, 1, -2]) # indirect doctest
|
|
1303
|
+
a*b*a*b^-1
|
|
1304
|
+
"""
|
|
1305
|
+
if len(args) != 1:
|
|
1306
|
+
return self.element_class(self, *args, **kwds)
|
|
1307
|
+
x = args[0]
|
|
1308
|
+
if x == 1:
|
|
1309
|
+
return self.one()
|
|
1310
|
+
try:
|
|
1311
|
+
P = x.parent()
|
|
1312
|
+
except AttributeError:
|
|
1313
|
+
return self.element_class(self, x, **kwds)
|
|
1314
|
+
if P is self._free_group:
|
|
1315
|
+
return self.element_class(self, x.Tietze(), **kwds)
|
|
1316
|
+
return self.element_class(self, x, **kwds)
|
|
1317
|
+
|
|
1318
|
+
@cached_method
|
|
1319
|
+
def abelian_invariants(self):
|
|
1320
|
+
r"""
|
|
1321
|
+
Return the abelian invariants of ``self``.
|
|
1322
|
+
|
|
1323
|
+
The abelian invariants are given by a list of integers
|
|
1324
|
+
`(i_1, \ldots, i_j)`, such that the abelianization of the group is
|
|
1325
|
+
isomorphic to `\ZZ / (i_1) \times \cdots \times \ZZ / (i_j)`.
|
|
1326
|
+
|
|
1327
|
+
EXAMPLES::
|
|
1328
|
+
|
|
1329
|
+
sage: G = FreeGroup(4, 'g')
|
|
1330
|
+
sage: G.inject_variables()
|
|
1331
|
+
Defining g0, g1, g2, g3
|
|
1332
|
+
sage: H = G.quotient([g1^2, g2*g1*g2^(-1)*g1^(-1), g1*g3^(-2), g0^4])
|
|
1333
|
+
sage: H.abelian_invariants()
|
|
1334
|
+
(0, 4, 4)
|
|
1335
|
+
|
|
1336
|
+
ALGORITHM:
|
|
1337
|
+
|
|
1338
|
+
Uses GAP.
|
|
1339
|
+
"""
|
|
1340
|
+
invariants = self.gap().AbelianInvariants()
|
|
1341
|
+
return tuple(i.sage() for i in invariants)
|
|
1342
|
+
|
|
1343
|
+
@cached_method
|
|
1344
|
+
def abelianization_map(self):
|
|
1345
|
+
r"""
|
|
1346
|
+
Return the abelianization map of ``self``.
|
|
1347
|
+
|
|
1348
|
+
OUTPUT: the abelianization map of ``self`` as a homomorphism of
|
|
1349
|
+
finitely presented groups
|
|
1350
|
+
|
|
1351
|
+
EXAMPLES::
|
|
1352
|
+
|
|
1353
|
+
sage: G = FreeGroup(4, 'g')
|
|
1354
|
+
sage: G.inject_variables(verbose=False)
|
|
1355
|
+
sage: H = G.quotient([g1^2, g2*g1*g2^(-1)*g1^(-1), g1*g3^(-2), g0^4])
|
|
1356
|
+
sage: H.abelianization_map()
|
|
1357
|
+
Group morphism:
|
|
1358
|
+
From: Finitely presented group < g0, g1, g2, g3 | g1^2, g2*g1*g2^-1*g1^-1, g1*g3^-2, g0^4 >
|
|
1359
|
+
To: Finitely presented group < f1, f2, f3 | f1^4, f2^-1*f1^-1*f2*f1, f2^4, f3^-1*f1^-1*f3*f1, f3^-1*f2^-1*f3*f2 >
|
|
1360
|
+
sage: g = FreeGroup(0) / []
|
|
1361
|
+
sage: g.abelianization_map()
|
|
1362
|
+
Group endomorphism of Finitely presented group < | >
|
|
1363
|
+
"""
|
|
1364
|
+
if not self.generators():
|
|
1365
|
+
return self.hom(codomain=self, im_gens=[])
|
|
1366
|
+
hom_ab_libgap = libgap(self).MaximalAbelianQuotient()
|
|
1367
|
+
ab_libgap = hom_ab_libgap.Range()
|
|
1368
|
+
hom_ab_fp = ab_libgap.IsomorphismFpGroup()
|
|
1369
|
+
ab_libgap_fp = hom_ab_fp.Range()
|
|
1370
|
+
hom_simply = ab_libgap_fp.IsomorphismSimplifiedFpGroup()
|
|
1371
|
+
ab = hom_simply.Range().sage()
|
|
1372
|
+
images = []
|
|
1373
|
+
for f in self.gens():
|
|
1374
|
+
f0 = hom_ab_libgap.Image(f)
|
|
1375
|
+
f1 = hom_ab_fp.Image(f0)
|
|
1376
|
+
f2 = hom_simply.Image(f1)
|
|
1377
|
+
L = f2.UnderlyingElement().LetterRepAssocWord()
|
|
1378
|
+
images.append(ab([int(j) for j in L]))
|
|
1379
|
+
return self.hom(codomain=ab, im_gens=images, check=False)
|
|
1380
|
+
|
|
1381
|
+
@cached_method
|
|
1382
|
+
def abelianization_to_algebra(self, ring=QQ):
|
|
1383
|
+
r"""
|
|
1384
|
+
Return the group algebra of the abelianization of ``self``
|
|
1385
|
+
together with the monomials representing the generators of ``self``.
|
|
1386
|
+
|
|
1387
|
+
INPUT:
|
|
1388
|
+
|
|
1389
|
+
- ``ring`` -- (default: ``QQ``) the base ring for
|
|
1390
|
+
the group algebra of ``self``
|
|
1391
|
+
|
|
1392
|
+
OUTPUT:
|
|
1393
|
+
|
|
1394
|
+
- ``ab`` -- the abelianization of ``self`` as a finitely presented group
|
|
1395
|
+
with a minimal number `n` of generators
|
|
1396
|
+
- ``R`` -- a Laurent polynomial ring with `n` variables with base ring ``ring``
|
|
1397
|
+
- ``ideal`` -- list of generators of an ideal ``I`` in ``R`` such that ``R/I``
|
|
1398
|
+
is the group algebra of the abelianization over ``ring``
|
|
1399
|
+
- ``image`` -- list with the images of the generators of ``self`` in ``R/I``
|
|
1400
|
+
|
|
1401
|
+
EXAMPLES::
|
|
1402
|
+
|
|
1403
|
+
sage: G = FreeGroup(4, 'g')
|
|
1404
|
+
sage: G.inject_variables()
|
|
1405
|
+
Defining g0, g1, g2, g3
|
|
1406
|
+
sage: H = G.quotient([g1^2, g2*g1*g2^(-1)*g1^(-1), g1*g3^(-2), g0^4])
|
|
1407
|
+
sage: H.abelianization_to_algebra()
|
|
1408
|
+
(Finitely presented group < f1, f2, f3 | f1^4, f2^-1*f1^-1*f2*f1, f2^4, f3^-1*f1^-1*f3*f1, f3^-1*f2^-1*f3*f2 >,
|
|
1409
|
+
Multivariate Laurent Polynomial Ring in f1, f2, f3 over Rational Field,
|
|
1410
|
+
[f1^4 - 1, f2^4 - 1],
|
|
1411
|
+
[f1^3*f2^2, f2^2, f3, f2])
|
|
1412
|
+
sage: g=FreeGroup(0) / []
|
|
1413
|
+
sage: g.abelianization_to_algebra()
|
|
1414
|
+
(Finitely presented group < | >, Rational Field, [], [])
|
|
1415
|
+
"""
|
|
1416
|
+
if not self.generators():
|
|
1417
|
+
return self, ring, [], []
|
|
1418
|
+
hom_ab = self.abelianization_map()
|
|
1419
|
+
ab = hom_ab.codomain()
|
|
1420
|
+
R = LaurentPolynomialRing(ring, ab.gens())
|
|
1421
|
+
ideal = []
|
|
1422
|
+
for a in ab.relations():
|
|
1423
|
+
a_T = a.Tietze()
|
|
1424
|
+
a_S = Set(a_T)
|
|
1425
|
+
if a_S.cardinality() == 1:
|
|
1426
|
+
j = a_T[0]
|
|
1427
|
+
m = len(a_T)
|
|
1428
|
+
ideal.append(R.gen(j - 1) ** m - 1)
|
|
1429
|
+
images0 = [hom_ab(g).Tietze() for g in self.gens()]
|
|
1430
|
+
images = []
|
|
1431
|
+
for L in images0:
|
|
1432
|
+
p = R.one()
|
|
1433
|
+
for a in L:
|
|
1434
|
+
if a > 0:
|
|
1435
|
+
p *= R.gen(a - 1)
|
|
1436
|
+
elif a < 0:
|
|
1437
|
+
p /= R.gen(-a - 1)
|
|
1438
|
+
images.append(p)
|
|
1439
|
+
return ab, R, ideal, images
|
|
1440
|
+
|
|
1441
|
+
def simplification_isomorphism(self):
|
|
1442
|
+
"""
|
|
1443
|
+
Return an isomorphism from ``self`` to a finitely presented group with
|
|
1444
|
+
a (hopefully) simpler presentation.
|
|
1445
|
+
|
|
1446
|
+
EXAMPLES::
|
|
1447
|
+
|
|
1448
|
+
sage: G.<a,b,c> = FreeGroup()
|
|
1449
|
+
sage: H = G / [a*b*c, a*b^2, c*b/c^2]
|
|
1450
|
+
sage: I = H.simplification_isomorphism()
|
|
1451
|
+
sage: I
|
|
1452
|
+
Group morphism:
|
|
1453
|
+
From: Finitely presented group < a, b, c | a*b*c, a*b^2, c*b*c^-2 >
|
|
1454
|
+
To: Finitely presented group < b | >
|
|
1455
|
+
sage: I(a)
|
|
1456
|
+
b^-2
|
|
1457
|
+
sage: I(b)
|
|
1458
|
+
b
|
|
1459
|
+
sage: I(c)
|
|
1460
|
+
b
|
|
1461
|
+
|
|
1462
|
+
TESTS::
|
|
1463
|
+
|
|
1464
|
+
sage: F = FreeGroup(1)
|
|
1465
|
+
sage: G = F.quotient([F.0])
|
|
1466
|
+
sage: h = G.simplification_isomorphism(); h
|
|
1467
|
+
Group morphism:
|
|
1468
|
+
From: Finitely presented group < x | x >
|
|
1469
|
+
To: Finitely presented group < | >
|
|
1470
|
+
sage: h(G.gen(0))
|
|
1471
|
+
1
|
|
1472
|
+
|
|
1473
|
+
ALGORITHM:
|
|
1474
|
+
|
|
1475
|
+
Uses GAP.
|
|
1476
|
+
"""
|
|
1477
|
+
II = self.gap().IsomorphismSimplifiedFpGroup()
|
|
1478
|
+
cod = II.Range().sage()
|
|
1479
|
+
phi = [cod(II.ImageElm(x)) for x in self.gap().GeneratorsOfGroup()]
|
|
1480
|
+
return self.hom(codomain=cod, im_gens=phi, check=False)
|
|
1481
|
+
# II = self.gap().IsomorphismSimplifiedFpGroup()
|
|
1482
|
+
# codomain = II.Range().sage()
|
|
1483
|
+
# phi = lambda x: codomain(II.ImageElm(x.gap()))
|
|
1484
|
+
# HS = self.Hom(codomain)
|
|
1485
|
+
# return GroupMorphismWithGensImages(HS, phi)
|
|
1486
|
+
|
|
1487
|
+
def simplified(self):
|
|
1488
|
+
"""
|
|
1489
|
+
Return an isomorphic group with a (hopefully) simpler presentation.
|
|
1490
|
+
|
|
1491
|
+
OUTPUT:
|
|
1492
|
+
|
|
1493
|
+
A new finitely presented group. Use
|
|
1494
|
+
:meth:`simplification_isomorphism` if you want to know the
|
|
1495
|
+
isomorphism.
|
|
1496
|
+
|
|
1497
|
+
EXAMPLES::
|
|
1498
|
+
|
|
1499
|
+
sage: G.<x,y> = FreeGroup()
|
|
1500
|
+
sage: H = G / [x ^5, y ^4, y*x*y^3*x ^3]
|
|
1501
|
+
sage: H
|
|
1502
|
+
Finitely presented group < x, y | x^5, y^4, y*x*y^3*x^3 >
|
|
1503
|
+
sage: H.simplified()
|
|
1504
|
+
Finitely presented group < x, y | y^4, y*x*y^-1*x^-2, x^5 >
|
|
1505
|
+
|
|
1506
|
+
A more complicate example::
|
|
1507
|
+
|
|
1508
|
+
sage: G.<e0, e1, e2, e3, e4, e5, e6, e7, e8, e9> = FreeGroup()
|
|
1509
|
+
sage: rels = [e6, e5, e3, e9, e4*e7^-1*e6, e9*e7^-1*e0,
|
|
1510
|
+
....: e0*e1^-1*e2, e5*e1^-1*e8, e4*e3^-1*e8, e2]
|
|
1511
|
+
sage: H = G.quotient(rels); H
|
|
1512
|
+
Finitely presented group < e0, e1, e2, e3, e4, e5, e6, e7, e8, e9 |
|
|
1513
|
+
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 >
|
|
1514
|
+
sage: H.simplified()
|
|
1515
|
+
Finitely presented group < e0 | e0^2 >
|
|
1516
|
+
"""
|
|
1517
|
+
return self.simplification_isomorphism().codomain()
|
|
1518
|
+
|
|
1519
|
+
def sorted_presentation(self):
|
|
1520
|
+
"""
|
|
1521
|
+
Return the same presentation with the relations sorted to ensure
|
|
1522
|
+
equality.
|
|
1523
|
+
|
|
1524
|
+
OUTPUT: a new finitely presented group with the relations sorted
|
|
1525
|
+
|
|
1526
|
+
EXAMPLES::
|
|
1527
|
+
|
|
1528
|
+
sage: G = FreeGroup(2) / [(1, 2, -1, -2), ()]; G
|
|
1529
|
+
Finitely presented group < x0, x1 | x0*x1*x0^-1*x1^-1, 1 >
|
|
1530
|
+
sage: G.sorted_presentation()
|
|
1531
|
+
Finitely presented group < x0, x1 | 1, x1^-1*x0^-1*x1*x0 >
|
|
1532
|
+
"""
|
|
1533
|
+
F = FreeGroup(self.ngens())
|
|
1534
|
+
L0 = [r.Tietze() for r in self.relations()]
|
|
1535
|
+
L1 = []
|
|
1536
|
+
for rel in L0:
|
|
1537
|
+
C = [rel]
|
|
1538
|
+
C.extend(rel[j + 1:] + rel[:j + 1] for j in range(len(rel) - 1))
|
|
1539
|
+
C1 = [tuple(-j for j in reversed(l)) for l in C]
|
|
1540
|
+
C += C1
|
|
1541
|
+
C.sort()
|
|
1542
|
+
L1.append(C[0])
|
|
1543
|
+
L1.sort()
|
|
1544
|
+
return F / L1
|
|
1545
|
+
|
|
1546
|
+
def epimorphisms(self, H):
|
|
1547
|
+
r"""
|
|
1548
|
+
Return the epimorphisms from ``self`` to `H`, up to automorphism of `H`.
|
|
1549
|
+
|
|
1550
|
+
INPUT:
|
|
1551
|
+
|
|
1552
|
+
- ``H`` -- another group
|
|
1553
|
+
|
|
1554
|
+
EXAMPLES::
|
|
1555
|
+
|
|
1556
|
+
sage: F = FreeGroup(3)
|
|
1557
|
+
sage: G = F / [F([1, 2, 3, 1, 2, 3]), F([1, 1, 1])]
|
|
1558
|
+
sage: H = AlternatingGroup(3)
|
|
1559
|
+
sage: for quo in G.epimorphisms(H):
|
|
1560
|
+
....: for a in G.gens():
|
|
1561
|
+
....: print(a, "|-->", quo(a))
|
|
1562
|
+
....: print("-----")
|
|
1563
|
+
x0 |--> ()
|
|
1564
|
+
x1 |--> (1,3,2)
|
|
1565
|
+
x2 |--> (1,2,3)
|
|
1566
|
+
-----
|
|
1567
|
+
x0 |--> (1,3,2)
|
|
1568
|
+
x1 |--> ()
|
|
1569
|
+
x2 |--> (1,2,3)
|
|
1570
|
+
-----
|
|
1571
|
+
x0 |--> (1,3,2)
|
|
1572
|
+
x1 |--> (1,2,3)
|
|
1573
|
+
x2 |--> ()
|
|
1574
|
+
-----
|
|
1575
|
+
x0 |--> (1,2,3)
|
|
1576
|
+
x1 |--> (1,2,3)
|
|
1577
|
+
x2 |--> (1,2,3)
|
|
1578
|
+
-----
|
|
1579
|
+
|
|
1580
|
+
ALGORITHM:
|
|
1581
|
+
|
|
1582
|
+
Uses libgap's GQuotients function.
|
|
1583
|
+
"""
|
|
1584
|
+
# from sage.misc.misc_c import prod
|
|
1585
|
+
# HomSpace = self.Hom(H)
|
|
1586
|
+
Gg = libgap(self)
|
|
1587
|
+
Hg = libgap(H)
|
|
1588
|
+
gquotients = Gg.GQuotients(Hg)
|
|
1589
|
+
res = []
|
|
1590
|
+
# the following closure is needed to attach a specific value of quo to
|
|
1591
|
+
# each function in the different morphisms
|
|
1592
|
+
# fmap = lambda tup: (lambda a: H(prod(tup[abs(i)-1]**sign(i) for i in a.Tietze())))
|
|
1593
|
+
for quo in gquotients:
|
|
1594
|
+
# tup = tuple(H(quo.ImageElm(i.gap()).sage()) for i in self.gens())
|
|
1595
|
+
# fhom = GroupMorphismWithGensImages(HomSpace, fmap(tup))
|
|
1596
|
+
fhom = self.hom(codomain=H, im_gens=[H(quo.ImageElm(a.gap())) for a in self.gens()])
|
|
1597
|
+
res.append(fhom)
|
|
1598
|
+
return res
|
|
1599
|
+
|
|
1600
|
+
def alexander_matrix(self, im_gens=None):
|
|
1601
|
+
"""
|
|
1602
|
+
Return the Alexander matrix of the group.
|
|
1603
|
+
|
|
1604
|
+
This matrix is given by the fox derivatives of the relations
|
|
1605
|
+
with respect to the generators.
|
|
1606
|
+
|
|
1607
|
+
- ``im_gens`` -- (optional) the images of the generators
|
|
1608
|
+
|
|
1609
|
+
OUTPUT:
|
|
1610
|
+
|
|
1611
|
+
A matrix with coefficients in the group algebra. If ``im_gens`` is
|
|
1612
|
+
given, the coefficients will live in the same algebra as the given
|
|
1613
|
+
values. The result depends on the (fixed) choice of presentation.
|
|
1614
|
+
|
|
1615
|
+
EXAMPLES::
|
|
1616
|
+
|
|
1617
|
+
sage: G.<a,b,c> = FreeGroup()
|
|
1618
|
+
sage: H = G.quotient([a*b/a/b, a*c/a/c, c*b/c/b])
|
|
1619
|
+
sage: H.alexander_matrix()
|
|
1620
|
+
[ 1 - a*b*a^-1 a - a*b*a^-1*b^-1 0]
|
|
1621
|
+
[ 1 - a*c*a^-1 0 a - a*c*a^-1*c^-1]
|
|
1622
|
+
[ 0 c - c*b*c^-1*b^-1 1 - c*b*c^-1]
|
|
1623
|
+
|
|
1624
|
+
If we introduce the images of the generators, we obtain the
|
|
1625
|
+
result in the corresponding algebra.
|
|
1626
|
+
|
|
1627
|
+
::
|
|
1628
|
+
|
|
1629
|
+
sage: G.<a,b,c,d,e> = FreeGroup()
|
|
1630
|
+
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)])
|
|
1631
|
+
sage: H.alexander_matrix()
|
|
1632
|
+
[ 1 - a*b*a^-1 a - a*b*a^-1*b^-1 0 0 0]
|
|
1633
|
+
[ 1 - a*c*a^-1 0 a - a*c*a^-1*c^-1 0 0]
|
|
1634
|
+
[ 1 - a*d*a^-1 0 0 a - a*d*a^-1*d^-1 0]
|
|
1635
|
+
[ 0 1 - b*c*d*b^-1 b - b*c*d*b^-1*d^-1*c^-1 b*c - b*c*d*b^-1*d^-1 0]
|
|
1636
|
+
[ 0 1 - b*c*d*c^-1*b^-1 b - b*c*d*c^-1 b*c - b*c*d*c^-1*b^-1*d^-1 0]
|
|
1637
|
+
sage: R.<t1,t2,t3,t4> = LaurentPolynomialRing(ZZ)
|
|
1638
|
+
sage: H.alexander_matrix([t1,t2,t3,t4])
|
|
1639
|
+
[ -t2 + 1 t1 - 1 0 0 0]
|
|
1640
|
+
[ -t3 + 1 0 t1 - 1 0 0]
|
|
1641
|
+
[ -t4 + 1 0 0 t1 - 1 0]
|
|
1642
|
+
[ 0 -t3*t4 + 1 t2 - 1 t2*t3 - t3 0]
|
|
1643
|
+
[ 0 -t4 + 1 -t2*t4 + t2 t2*t3 - 1 0]
|
|
1644
|
+
"""
|
|
1645
|
+
rel = self.relations()
|
|
1646
|
+
gen = self._free_group.gens()
|
|
1647
|
+
return matrix(len(rel), len(gen),
|
|
1648
|
+
lambda i, j: rel[i].fox_derivative(gen[j], im_gens))
|
|
1649
|
+
|
|
1650
|
+
@cached_method
|
|
1651
|
+
def abelian_alexander_matrix(self, ring=QQ, simplified=True):
|
|
1652
|
+
"""
|
|
1653
|
+
Return the Alexander matrix of the group with values in the group
|
|
1654
|
+
algebra of the abelianized.
|
|
1655
|
+
|
|
1656
|
+
INPUT:
|
|
1657
|
+
|
|
1658
|
+
- ``ring`` -- (default: ``QQ``) the base ring of the
|
|
1659
|
+
group algebra
|
|
1660
|
+
- ``simplified`` -- boolean (default: ``False``); if set to
|
|
1661
|
+
``True`` use Gauss elimination and erase rows and columns
|
|
1662
|
+
|
|
1663
|
+
OUTPUT:
|
|
1664
|
+
|
|
1665
|
+
- ``A`` -- a matrix with coefficients in ``R``
|
|
1666
|
+
- ``ideal`` -- an list of generators of an ideal ``I`` of
|
|
1667
|
+
``R = A.base_ring()`` such that ``R/I`` is the group algebra of the
|
|
1668
|
+
abelianization of ``self``
|
|
1669
|
+
|
|
1670
|
+
EXAMPLES::
|
|
1671
|
+
|
|
1672
|
+
sage: G.<a,b,c> = FreeGroup()
|
|
1673
|
+
sage: H = G.quotient([a*b/a/b, a*c/a/c, c*b/c/b])
|
|
1674
|
+
sage: A, ideal = H.abelian_alexander_matrix()
|
|
1675
|
+
sage: A
|
|
1676
|
+
[-f2 + 1 f1 - 1 0]
|
|
1677
|
+
[-f3 + 1 0 f1 - 1]
|
|
1678
|
+
[ 0 f3 - 1 -f2 + 1]
|
|
1679
|
+
sage: A.base_ring()
|
|
1680
|
+
Multivariate Laurent Polynomial Ring in f1, f2, f3 over Rational Field
|
|
1681
|
+
sage: ideal
|
|
1682
|
+
[]
|
|
1683
|
+
sage: G = FreeGroup(3)/[(2, 1, 1), (1, 2, 2, 3, 3)]
|
|
1684
|
+
sage: A, ideal = G.abelian_alexander_matrix(simplified=True); A
|
|
1685
|
+
[-f1^2 - f1^4 - f1^6 f1^3 + f1^6]
|
|
1686
|
+
sage: g = FreeGroup(1) / []
|
|
1687
|
+
sage: g.abelian_alexander_matrix()
|
|
1688
|
+
([], [])
|
|
1689
|
+
sage: g.abelian_alexander_matrix()[0].base_ring()
|
|
1690
|
+
Univariate Laurent Polynomial Ring in f1 over Rational Field
|
|
1691
|
+
sage: g = FreeGroup(0) / []
|
|
1692
|
+
sage: A, ideal = g.abelian_alexander_matrix(); A
|
|
1693
|
+
[]
|
|
1694
|
+
sage: A.base_ring()
|
|
1695
|
+
Rational Field
|
|
1696
|
+
"""
|
|
1697
|
+
ab, R, ideal, images = self.abelianization_to_algebra(ring=ring)
|
|
1698
|
+
A = self.alexander_matrix(im_gens=images)
|
|
1699
|
+
if A.base_ring() != R:
|
|
1700
|
+
A = A.change_ring(R)
|
|
1701
|
+
if simplified:
|
|
1702
|
+
n, m = A.dimensions()
|
|
1703
|
+
if n == 0 or m == 0:
|
|
1704
|
+
return A, ideal
|
|
1705
|
+
simpli = True
|
|
1706
|
+
while simpli:
|
|
1707
|
+
i = 0
|
|
1708
|
+
j = 0
|
|
1709
|
+
unidad = False
|
|
1710
|
+
while not unidad and i < n and j < m:
|
|
1711
|
+
p = A[i, j]
|
|
1712
|
+
unidad = p.is_unit()
|
|
1713
|
+
if unidad:
|
|
1714
|
+
A.swap_rows(0, i)
|
|
1715
|
+
A.swap_columns(0, j)
|
|
1716
|
+
for k in range(1, n):
|
|
1717
|
+
A.add_multiple_of_row(k, 0, -A[k, 0] * p ** -1)
|
|
1718
|
+
A = A.delete_rows([0]).delete_columns([0])
|
|
1719
|
+
n, m = A.dimensions()
|
|
1720
|
+
else:
|
|
1721
|
+
if j < m - 1:
|
|
1722
|
+
j += 1
|
|
1723
|
+
else:
|
|
1724
|
+
i += 1
|
|
1725
|
+
j = 0
|
|
1726
|
+
simpli = unidad
|
|
1727
|
+
return A, ideal
|
|
1728
|
+
|
|
1729
|
+
def characteristic_varieties(self, ring=QQ, matrix_ideal=None, groebner=False):
|
|
1730
|
+
r"""
|
|
1731
|
+
Return the characteristic varieties of the group ``self``.
|
|
1732
|
+
|
|
1733
|
+
There are several definitions of the characteristic varieties of a
|
|
1734
|
+
group `G`, see e.g. [CS1999a]_. Let `\Lambda` be the group algebra of
|
|
1735
|
+
`G/G'` and `\mathbb{T}` its associated algebraic variety (a torus).
|
|
1736
|
+
Each element `\xi\in\mathbb{T}` defines a local system of coefficients
|
|
1737
|
+
and the `k`-th characteristic variety is
|
|
1738
|
+
|
|
1739
|
+
.. MATH::
|
|
1740
|
+
|
|
1741
|
+
V_k(G) = \{\xi\in\mathbb{T}\mid \dim H^1(G;\xi)\geq k\}.
|
|
1742
|
+
|
|
1743
|
+
These varieties are defined by ideals in `\Lambda`.
|
|
1744
|
+
|
|
1745
|
+
INPUT:
|
|
1746
|
+
|
|
1747
|
+
- ``ring`` -- (default: ``QQ``) the base ring of the group algebra
|
|
1748
|
+
- ``groebner`` -- boolean (default: ``False``); if set to
|
|
1749
|
+
``True`` the minimal associated primes of the ideals and their
|
|
1750
|
+
groebner bases are computed; ignored if the base ring
|
|
1751
|
+
is not a field
|
|
1752
|
+
|
|
1753
|
+
OUTPUT:
|
|
1754
|
+
|
|
1755
|
+
A dictionary with keys the indices of the varieties. If ``groebner`` is ``False``
|
|
1756
|
+
the values are the ideals defining the characteristic varieties.
|
|
1757
|
+
If it is ``True``, lists for Gröbner bases for the ideal of each irreducible
|
|
1758
|
+
component, stopping when the first time a characteristic variety is empty.
|
|
1759
|
+
|
|
1760
|
+
EXAMPLES::
|
|
1761
|
+
|
|
1762
|
+
sage: L = [2*(i, j) + 2* (-i, -j) for i, j in ((1, 2), (2, 3), (3, 1))]
|
|
1763
|
+
sage: G = FreeGroup(3) / L
|
|
1764
|
+
sage: G.characteristic_varieties(groebner=True) # needs sage.libs.singular
|
|
1765
|
+
{0: [(0,)],
|
|
1766
|
+
1: [(f1 - 1, f2 - 1, f3 - 1), (f1*f3 + 1, f2 - 1), (f1*f2 + 1, f3 - 1), (f2*f3 + 1, f1 - 1),
|
|
1767
|
+
(f2*f3 + 1, f1 - f2), (f2*f3 + 1, f1 - f3), (f1*f3 + 1, f2 - f3)],
|
|
1768
|
+
2: [(f1 - 1, f2 - 1, f3 - 1), (f1 + 1, f2 - 1, f3 - 1), (f1 - 1, f2 - 1, f3 + 1),
|
|
1769
|
+
(f3^2 + 1, f1 - f3, f2 - f3), (f1 - 1, f2 + 1, f3 - 1)],
|
|
1770
|
+
3: [(f1 - 1, f2 - 1, f3 - 1)],
|
|
1771
|
+
4: []}
|
|
1772
|
+
sage: G = FreeGroup(2)/[2*(1,2,-1,-2)]
|
|
1773
|
+
sage: G.characteristic_varieties() # needs sage.libs.singular
|
|
1774
|
+
{0: Ideal (0) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field,
|
|
1775
|
+
1: Ideal (f2 - 1, f1 - 1) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field,
|
|
1776
|
+
2: Ideal (f2 - 1, f1 - 1) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field,
|
|
1777
|
+
3: Ideal (1) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field}
|
|
1778
|
+
sage: G.characteristic_varieties(ring=ZZ) # needs sage.libs.singular
|
|
1779
|
+
{0: Ideal (0) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring,
|
|
1780
|
+
1: Ideal (2*f2 - 2, 2*f1 - 2) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring,
|
|
1781
|
+
2: Ideal (f2 - 1, f1 - 1) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring,
|
|
1782
|
+
3: Ideal (1) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring}
|
|
1783
|
+
sage: G = FreeGroup(2)/[(1,2,1,-2,-1,-2)]
|
|
1784
|
+
sage: G.characteristic_varieties() # needs sage.libs.singular
|
|
1785
|
+
{0: Ideal (0) of Univariate Laurent Polynomial Ring in f1 over Rational Field,
|
|
1786
|
+
1: Ideal (-1 + 2*f1 - 2*f1^2 + f1^3) of Univariate Laurent Polynomial Ring in f1 over Rational Field,
|
|
1787
|
+
2: Ideal (1) of Univariate Laurent Polynomial Ring in f1 over Rational Field}
|
|
1788
|
+
sage: G.characteristic_varieties(groebner=True) # needs sage.libs.singular
|
|
1789
|
+
{0: [0], 1: [-1 + f1, 1 - f1 + f1^2], 2: []}
|
|
1790
|
+
sage: G = FreeGroup(2)/[3 * (1, ), 2 * (2, )]
|
|
1791
|
+
sage: G.characteristic_varieties(groebner=True) # needs sage.libs.singular
|
|
1792
|
+
{0: [-1 + F1, 1 + F1, 1 - F1 + F1^2, 1 + F1 + F1^2], 1: [1 - F1 + F1^2], 2: []}
|
|
1793
|
+
sage: G = FreeGroup(2)/[2 * (2, )]
|
|
1794
|
+
sage: G.characteristic_varieties(groebner=True) # needs sage.libs.singular
|
|
1795
|
+
{0: [(f1 + 1,), (f1 - 1,)], 1: [(f1 + 1,), (f1 - 1, f2 - 1)], 2: []}
|
|
1796
|
+
sage: G = (FreeGroup(0) / [])
|
|
1797
|
+
sage: G.characteristic_varieties() # needs sage.libs.singular
|
|
1798
|
+
{0: Principal ideal (0) of Rational Field,
|
|
1799
|
+
1: Principal ideal (1) of Rational Field}
|
|
1800
|
+
sage: G.characteristic_varieties(groebner=True) # needs sage.libs.singular
|
|
1801
|
+
{0: [(0,)], 1: [(1,)]}
|
|
1802
|
+
"""
|
|
1803
|
+
if self.ngens() == 0:
|
|
1804
|
+
if groebner:
|
|
1805
|
+
return {j: [(ring(j),)] for j in (0, 1)}
|
|
1806
|
+
return {j: ring.ideal(j) for j in (0, 1)}
|
|
1807
|
+
A, rels = self.abelian_alexander_matrix(ring=ring, simplified=True)
|
|
1808
|
+
R = A.base_ring()
|
|
1809
|
+
eval_1 = {x: ring(1) for x in R.gens()}
|
|
1810
|
+
A_scalar = A.apply_map(lambda p: p.subs(eval_1))
|
|
1811
|
+
n = A.ncols()
|
|
1812
|
+
n1 = n - A_scalar.rank()
|
|
1813
|
+
ideal_1 = R.ideal([x - 1 for x in R.gens()])
|
|
1814
|
+
S = R.polynomial_ring()
|
|
1815
|
+
K = R.base_ring()
|
|
1816
|
+
id_rels = R.ideal(rels)
|
|
1817
|
+
res = {}
|
|
1818
|
+
bound = n + 1
|
|
1819
|
+
for j in range(bound + 1):
|
|
1820
|
+
J = id_rels + A.fitting_ideal(j)
|
|
1821
|
+
# J = R.ideal(id_rels.gens() + A.fitting_ideal(j).gens())
|
|
1822
|
+
if j <= n1:
|
|
1823
|
+
J1 = K.ideal([K(p.subs(eval_1)) for p in J.gens()])
|
|
1824
|
+
if J1:
|
|
1825
|
+
J *= ideal_1
|
|
1826
|
+
res[j] = R.ideal(J.gens_reduced())
|
|
1827
|
+
if R(1) in res[j].gens():
|
|
1828
|
+
bound = j
|
|
1829
|
+
break
|
|
1830
|
+
if not groebner or not ring.is_field():
|
|
1831
|
+
return res
|
|
1832
|
+
if R.ngens() == 1:
|
|
1833
|
+
res = {j: gcd(S(p) for p in res[j].gens()) for j in range(bound + 1)}
|
|
1834
|
+
char_var = {}
|
|
1835
|
+
strict = True
|
|
1836
|
+
j = 0
|
|
1837
|
+
while strict and j <= bound:
|
|
1838
|
+
if res[j] == 0:
|
|
1839
|
+
char_var[j] = [R(0)]
|
|
1840
|
+
else:
|
|
1841
|
+
fct = [q[0] for q in R(res[j]).factor()]
|
|
1842
|
+
if fct:
|
|
1843
|
+
char_var[j] = fct
|
|
1844
|
+
else:
|
|
1845
|
+
char_var[j] = []
|
|
1846
|
+
strict = False
|
|
1847
|
+
j += 1
|
|
1848
|
+
return char_var
|
|
1849
|
+
char_var = {}
|
|
1850
|
+
strict = True
|
|
1851
|
+
j = 0
|
|
1852
|
+
while strict and j <= bound:
|
|
1853
|
+
LJ = res[j].minimal_associated_primes()
|
|
1854
|
+
fct = [id.groebner_basis() for id in LJ]
|
|
1855
|
+
char_var[j] = fct
|
|
1856
|
+
if not fct:
|
|
1857
|
+
strict = False
|
|
1858
|
+
j += 1
|
|
1859
|
+
return char_var
|
|
1860
|
+
|
|
1861
|
+
def rewriting_system(self):
|
|
1862
|
+
"""
|
|
1863
|
+
Return the rewriting system corresponding to the finitely presented
|
|
1864
|
+
group. This rewriting system can be used to reduce words with respect
|
|
1865
|
+
to the relations.
|
|
1866
|
+
|
|
1867
|
+
If the rewriting system is transformed into a confluent one, the
|
|
1868
|
+
reduction process will give as a result the (unique) reduced form
|
|
1869
|
+
of an element.
|
|
1870
|
+
|
|
1871
|
+
EXAMPLES::
|
|
1872
|
+
|
|
1873
|
+
sage: F.<a,b> = FreeGroup()
|
|
1874
|
+
sage: G = F / [a^2,b^3,(a*b/a)^3,b*a*b*a]
|
|
1875
|
+
sage: k = G.rewriting_system()
|
|
1876
|
+
sage: k
|
|
1877
|
+
Rewriting system of Finitely presented group < a, b | a^2, b^3, a*b^3*a^-1, b*a*b*a >
|
|
1878
|
+
with rules:
|
|
1879
|
+
a^2 ---> 1
|
|
1880
|
+
b^3 ---> 1
|
|
1881
|
+
b*a*b*a ---> 1
|
|
1882
|
+
a*b^3*a^-1 ---> 1
|
|
1883
|
+
|
|
1884
|
+
sage: G([1,1,2,2,2])
|
|
1885
|
+
a^2*b^3
|
|
1886
|
+
sage: k.reduce(G([1,1,2,2,2]))
|
|
1887
|
+
1
|
|
1888
|
+
sage: k.reduce(G([2,2,1]))
|
|
1889
|
+
b^2*a
|
|
1890
|
+
sage: k.make_confluent()
|
|
1891
|
+
sage: k.reduce(G([2,2,1]))
|
|
1892
|
+
a*b
|
|
1893
|
+
"""
|
|
1894
|
+
return RewritingSystem(self)
|
|
1895
|
+
|
|
1896
|
+
from sage.groups.generic import structure_description
|