Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/braid.py
8814 views
1
"""
2
Braid groups
3
4
Braid groups are implemented as a particular case of finitely presented groups,
5
but with a lot of specific methods for braids.
6
7
A braid group can be created by giving the number of strands, and the name of the generators::
8
9
sage: BraidGroup(3)
10
Braid group on 3 strands
11
sage: BraidGroup(3,'a')
12
Braid group on 3 strands
13
sage: BraidGroup(3,'a').gens()
14
(a0, a1)
15
sage: BraidGroup(3,'a,b').gens()
16
(a, b)
17
18
The elements can be created by operating with the generators, or by passing a list
19
with the indices of the letters to the group::
20
21
sage: B.<s0,s1,s2> = BraidGroup(4)
22
sage: s0*s1*s0
23
s0*s1*s0
24
sage: B([1,2,1])
25
s0*s1*s0
26
27
The mapping class action of the braid group over the free group is
28
also implemented, see :class:`MappingClassGroupAction` for an
29
explanation. This action is left multiplication of a free group
30
element by a braid::
31
32
sage: B.<b0,b1,b2> = BraidGroup()
33
sage: F.<f0,f1,f2,f3> = FreeGroup()
34
sage: B.strands() == F.rank() # necessary for the action to be defined
35
True
36
sage: f1 * b1
37
f1*f2*f1^-1
38
sage: f0 * b1
39
f0
40
sage: f1 * b1
41
f1*f2*f1^-1
42
sage: f1^-1 * b1
43
f1*f2^-1*f1^-1
44
45
AUTHOR:
46
47
- Miguel Angel Marco Buzunariz
48
- Volker Braun
49
"""
50
51
##############################################################################
52
# Copyright (C) 2012 Miguel Angel Marco Buzunariz <[email protected]>
53
#
54
# Distributed under the terms of the GNU General Public License (GPL)
55
#
56
# The full text of the GPL is available at:
57
#
58
# http://www.gnu.org/licenses/
59
##############################################################################
60
61
62
from sage.rings.integer import Integer
63
from sage.rings.integer_ring import IntegerRing
64
from sage.misc.cachefunc import cached_method
65
from sage.groups.free_group import FreeGroup, is_FreeGroup
66
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
67
from sage.matrix.constructor import identity_matrix, matrix
68
from sage.combinat.permutation import Permutation
69
from sage.categories.action import Action
70
from sage.sets.set import Set
71
from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement
72
73
74
class Braid(FinitelyPresentedGroupElement):
75
"""
76
Class that models elements of the braid group.
77
78
It is a particular case of element of a finitely presented group.
79
80
EXAMPLES::
81
82
sage: B.<s0,s1,s2> = BraidGroup(4)
83
sage: B
84
Braid group on 4 strands
85
sage: s0*s1/s2/s1
86
s0*s1*s2^-1*s1^-1
87
sage: B((1, 2, -3, -2))
88
s0*s1*s2^-1*s1^-1
89
"""
90
91
def __cmp__(self, other):
92
"""
93
Compare ``self`` and ``other``
94
95
TESTS::
96
97
sage: B = BraidGroup(4)
98
sage: b = B([1, 2, 1])
99
sage: c = B([2, 1, 2])
100
sage: b == c #indirect doctest
101
True
102
sage: b.__cmp__(c^(-1))
103
-1
104
sage: B([]).__cmp__(B.one())
105
0
106
"""
107
if self.Tietze()==other.Tietze():
108
return 0
109
nfself = map(lambda i: i.Tietze(), self.left_normal_form())
110
nfother = map(lambda i: i.Tietze(), other.left_normal_form())
111
return cmp(nfself, nfother)
112
113
def _latex_(self):
114
"""
115
Return a LaTeX representation
116
117
OUTPUT:
118
119
String. A valid LaTeX math command sequence.
120
121
TESTS::
122
123
sage: B = BraidGroup(4)
124
sage: b = B([1, 2, 3, -1, 2, -3])
125
sage: b._latex_()
126
'\\sigma_{1}\\sigma_{2}\\sigma_{3}\\sigma_{1}^{-1}\\sigma_{2}\\sigma_{3}^{-1}'
127
"""
128
latexrepr = ''
129
for i in self.Tietze():
130
if i>0:
131
latexrepr = latexrepr+"\sigma_{%s}"%i
132
if i<0:
133
latexrepr = latexrepr+"\sigma_{%s}^{-1}"%(-i)
134
return latexrepr
135
136
def strands(self):
137
"""
138
Return the number of strands in the braid.
139
140
EXAMPLES::
141
142
sage: B = BraidGroup(4)
143
sage: b = B([1, 2, -1, 3, -2])
144
sage: b.strands()
145
4
146
"""
147
return self.parent().strands()
148
149
def burau_matrix(self, var='t'):
150
"""
151
Return the Burau matrix of the braid.
152
153
INPUT:
154
155
- ``var`` -- string (default: ``'t'``). The name of the
156
variable in the entries of the matrix.
157
158
OUTPUT:
159
160
The Burau matrix of the braid. It is a matrix whose entries
161
are Laurent polynomials in the variable ``var``.
162
163
EXAMPLES::
164
165
sage: B = BraidGroup(4)
166
sage: B.inject_variables()
167
Defining s0, s1, s2
168
sage: b=s0*s1/s2/s1
169
sage: b.burau_matrix()
170
[ -t + 1 0 -t^2 + t t^2]
171
[ 1 0 0 0]
172
[ 0 0 1 0]
173
[ 0 t^-2 t^-1 - t^-2 1 - t^-1]
174
sage: s2.burau_matrix('x')
175
[ 1 0 0 0]
176
[ 0 1 0 0]
177
[ 0 0 -x + 1 x]
178
[ 0 0 1 0]
179
180
REFERENCES:
181
182
http://en.wikipedia.org/wiki/Burau_representation
183
"""
184
R = LaurentPolynomialRing(IntegerRing(), var)
185
t = R.gen()
186
M = identity_matrix(R, self.strands())
187
for i in self.Tietze():
188
A = identity_matrix(R, self.strands())
189
if i>0:
190
A[i-1, i-1] = 1-t
191
A[i, i] = 0
192
A[i, i-1] = 1
193
A[i-1, i] = t
194
if i<0:
195
A[-1-i, -1-i] = 0
196
A[-i, -i] = 1-t**(-1)
197
A[-1-i, -i] = 1
198
A[-i, -1-i] = t**(-1)
199
M=M*A
200
return M
201
202
def permutation(self):
203
"""
204
Return the permutation induced by the braid in its strands.
205
206
OUTPUT:
207
208
A permutation.
209
210
EXAMPLES::
211
212
sage: B.<s0,s1,s2> = BraidGroup()
213
sage: b = s0*s1/s2/s1
214
sage: b.permutation()
215
[4, 1, 3, 2]
216
sage: b.permutation().cycle_string()
217
'(1,4,2)'
218
"""
219
per = Permutation((()))
220
for i in self.Tietze():
221
j = abs(i)
222
per = per*Permutation(((j, j+1)))
223
return per
224
225
def plot(self, color='rainbow', orientation='bottom-top', gap=0.05, aspect_ratio=1, axes=False, **kwds):
226
"""
227
Plot the braid
228
229
The following options are available:
230
231
- ``color`` -- (default: ``'rainbow'``) the color of the
232
strands. Possible values are:
233
234
* ``'rainbow'``, uses :meth:`~sage.plot.colors.rainbow`
235
according to the number of strands.
236
237
* a valid color name for :meth:`~sage.plot.bezier_path`
238
and :meth:`~sage.plot.line`. Used for all strands.
239
240
* a list or a tuple of colors for each individual strand.
241
242
- ``orientation`` -- (default: ``'bottom-top'``) determines how
243
the braid is printed. The possible values are:
244
245
* ``'bottom-top'``, the braid is printed from bottom to top
246
247
* ``'top-bottom'``, the braid is printed from top to bottom
248
249
* ``'left-right'``, the braid is printed from left to right
250
251
- ``gap`` -- floating point number (default: 0.05). determines
252
the size of the gap left when a strand goes under another.
253
254
- ``aspect_ratio`` -- floating point number (default:
255
``1``). The aspect ratio.
256
257
- ``**kwds`` -- other keyword options that are passed to
258
:meth:`~sage.plot.bezier_path` and :meth:`~sage.plot.line`.
259
260
EXAMPLES::
261
262
sage: B = BraidGroup(4, 's')
263
sage: b = B([1, 2, 3, 1, 2, 1])
264
sage: b.plot()
265
sage: b.plot(color=["red", "blue", "red", "blue"])
266
267
sage: B.<s,t> = BraidGroup(3)
268
sage: b = t^-1*s^2
269
sage: b.plot(orientation="left-right", color="red")
270
"""
271
from sage.plot.bezier_path import bezier_path
272
from sage.plot.plot import Graphics, line
273
from sage.plot.colors import rainbow
274
if orientation=='top-bottom':
275
orx = 0
276
ory = -1
277
nx = 1
278
ny = 0
279
elif orientation=='left-right':
280
orx = 1
281
ory = 0
282
nx = 0
283
ny = -1
284
elif orientation=='bottom-top':
285
orx = 0
286
ory = 1
287
nx = 1
288
ny = 0
289
else:
290
raise ValueError('unknown value for "orientation"')
291
n = self.strands()
292
if isinstance(color, (list, tuple)):
293
if len(color) != n:
294
raise TypeError("color (=%s) must contain exactly %d colors" % (color, n))
295
col = list(color)
296
elif color == "rainbow":
297
col = rainbow(n)
298
else:
299
col = [color]*n
300
braid = self.Tietze()
301
a = Graphics()
302
op = gap
303
for i, m in enumerate(braid):
304
for j in range(n):
305
if m==j+1:
306
a += bezier_path([[(j*nx+i*orx, i*ory+j*ny), (j*nx+orx*(i+0.25), j*ny+ory*(i+0.25)),
307
(nx*(j+0.5)+orx*(i+0.5), ny*(j+0.5)+ory*(i+0.5))],
308
[(nx*(j+1)+orx*(i+0.75), ny*(j+1)+ory*(i+0.75)),
309
(nx*(j+1)+orx*(i+1), ny*(j+1)+ory*(i+1))]], color=col[j], **kwds)
310
elif m==j:
311
a += bezier_path([[(nx*j+orx*i, ny*j+ory*i), (nx*j+orx*(i+0.25), ny*j+ory*(i+0.25)),
312
(nx*(j-0.5+4*op)+orx*(i+0.5-2*op), ny*(j-0.5+4*op)+ory*(i+0.5-2*op)),
313
(nx*(j-0.5+2*op)+orx*(i+0.5-op), ny*(j-0.5+2*op)+ory*(i+0.5-op))]],
314
color=col[j], **kwds)
315
a += bezier_path([[(nx*(j-0.5-2*op)+orx*(i+0.5+op), ny*(j-0.5-2*op)+ory*(i+0.5+op)),
316
(nx*(j-0.5-4*op)+orx*(i+0.5+2*op), ny*(j-0.5-4*op)+ory*(i+0.5+2*op)),
317
(nx*(j-1)+orx*(i+0.75), ny*(j-1)+ory*(i+0.75)),
318
(nx*(j-1)+orx*(i+1), ny*(j-1)+ory*(i+1))]], color=col[j], **kwds)
319
col[j], col[j-1] = col[j-1], col[j]
320
elif -m==j+1:
321
a += bezier_path([[(nx*j+orx*i, ny*j+ory*i), (nx*j+orx*(i+0.25), ny*j+ory*(i+0.25)),
322
(nx*(j+0.5-4*op)+orx*(i+0.5-2*op), ny*(j+0.5-4*op)+ory*(i+0.5-2*op)),
323
(nx*(j+0.5-2*op)+orx*(i+0.5-op), ny*(j+0.5-2*op)+ory*(i+0.5-op))]],
324
color=col[j], **kwds)
325
a += bezier_path([[(nx*(j+0.5+2*op)+orx*(i+0.5+op), ny*(j+0.5+2*op)+ory*(i+0.5+op)),
326
(nx*(j+0.5+4*op)+orx*(i+0.5+2*op), ny*(j+0.5+4*op)+ory*(i+0.5+2*op)),
327
(nx*(j+1)+orx*(i+0.75), ny*(j+1)+ory*(i+0.75)),
328
(nx*(j+1)+orx*(i+1), ny*(j+1)+ory*(i+1))]], color=col[j], **kwds)
329
elif -m==j:
330
a += bezier_path([[(nx*j+orx*i, ny*j+ory*i), (nx*j+orx*(i+0.25), ny*j+ory*(i+0.25)),
331
(nx*(j-0.5)+orx*(i+0.5), ny*(j-0.5)+ory*(i+0.5))],
332
[(nx*(j-1)+orx*(i+0.75), ny*(j-1)+ory*(i+0.75)),
333
(nx*(j-1)+orx*(i+1), ny*(j-1)+ory*(i+1))]], color=col[j], **kwds)
334
col[j], col[j-1] = col[j-1], col[j]
335
else:
336
a += line([(nx*j+orx*i, ny*j+ory*i), (nx*j+orx*(i+1), ny*j+ory*(i+1))], color=col[j], **kwds)
337
a.set_aspect_ratio(aspect_ratio)
338
a.axes(axes)
339
return a
340
341
def plot3d(self, color='rainbow'):
342
"""
343
Plots the braid in 3d.
344
345
The following option is available:
346
347
- ``color`` -- (default: ``'rainbow'``) the color of the
348
strands. Possible values are:
349
350
* ``'rainbow'``, uses :meth:`~sage.plot.colors.rainbow`
351
according to the number of strands.
352
353
* a valid color name for :meth:`~sage.plot.plot3d.bezier3d`.
354
Used for all strands.
355
356
* a list or a tuple of colors for each individual strand.
357
358
EXAMPLES::
359
360
sage: B = BraidGroup(4, 's')
361
sage: b = B([1, 2, 3, 1, 2, 1])
362
sage: b.plot3d()
363
sage: b.plot3d(color="red")
364
sage: b.plot3d(color=["red", "blue", "red", "blue"])
365
"""
366
from sage.plot.plot3d.shapes2 import bezier3d
367
from sage.plot.colors import rainbow
368
b = []
369
n = self.strands()
370
if isinstance(color, (list, tuple)):
371
if len(color) != n:
372
raise TypeError("color (=%s) must contain exactly %d colors" % (color, n))
373
col = list(color)
374
elif color == "rainbow":
375
col = rainbow(n)
376
else:
377
col = [color]*n
378
braid = self.Tietze()
379
380
for i, m in enumerate(braid):
381
for j in range(n):
382
if m==j+1:
383
b.append(bezier3d([[(0, j, i), (0, j, i+0.25), (0.25, j, i+0.25), (0.25, j+0.5, i+0.5)],
384
[(0.25, j+1, i+0.75), (0, j+1, i+0.75), (0, j+1, i+1)]], color=col[j]))
385
elif -m==j+1:
386
b.append(bezier3d([[(0, j, i), (0, j, i+0.25), (-0.25, j, i+0.25), (-0.25, j+0.5, i+0.5)],
387
[(-0.25, j+1, i+0.75), (0, j+1, i+0.75), (0, j+1, i+1)]], color=col[j]))
388
elif m==j:
389
b.append(bezier3d([[(0, j, i), (0, j, i+0.25), (-0.25, j, i+0.25), (-0.25, j-0.5, i+0.5)],
390
[(-0.25, j-1, i+0.75), (0, j-1, i+0.75), (0, j-1, i+1)]], color=col[j]))
391
col[j], col[j-1] = col[j-1], col[j]
392
elif -m==j:
393
b.append(bezier3d([[(0, j, i), (0, j, i+0.25), (0.25, j, i+0.25), (0.25, j-0.5, i+0.5)],
394
[(0.25, j-1, i+0.75), (0, j-1, i+0.75), (0, j-1, i+1)]], color=col[j]))
395
col[j], col[j-1] = col[j-1], col[j]
396
else:
397
b.append(bezier3d([[(0, j, i), (0, j, i+1)]], color=col[j]))
398
return sum(b)
399
400
def LKB_matrix(self, variables='x,y'):
401
"""
402
Return the Lawence-Krammer-Bigelow representation matrix.
403
404
The matrix is expressed in the basis $\{e_{i, j} \mid 1\\leq i
405
< j \leq n\}$, where the indices are ordered
406
lexicographically. It is a matrix whose entries are in the
407
ring of Laurent polynomials on the given variables. By
408
default, the variables are ``'x'`` and ``'y'``.
409
410
INPUT:
411
412
- ``variables`` -- string (default: ``'x,y'``). A string
413
containing the names of the variables, separated by a comma.
414
415
OUTPUT:
416
417
The matrix corresponding to the Lawence-Krammer-Bigelow representation of the braid.
418
419
EXAMPLES::
420
421
sage: B = BraidGroup(3)
422
sage: b = B([1, 2, 1])
423
sage: b.LKB_matrix()
424
[ 0 -x^4*y + x^3*y -x^4*y]
425
[ 0 -x^3*y 0]
426
[ -x^2*y x^3*y - x^2*y 0]
427
sage: c = B([2, 1, 2])
428
sage: c.LKB_matrix()
429
[ 0 -x^4*y + x^3*y -x^4*y]
430
[ 0 -x^3*y 0]
431
[ -x^2*y x^3*y - x^2*y 0]
432
433
REFERENCES:
434
435
.. [Bigelow] Bigelow, Stephen J. The Lawrence-Krammer representation. arXiv:math/0204057v1
436
"""
437
return self.parent()._LKB_matrix_(self.Tietze(), variab=variables)
438
439
def tropical_coordinates(self):
440
r"""
441
Return the tropical coordinates of ``self`` in the braid group `B_n`.
442
443
OUTPUT:
444
445
- a list of `2n` tropical integers
446
447
EXAMPLES::
448
449
sage: B = BraidGroup(3)
450
sage: b = B([1])
451
sage: tc = b.tropical_coordinates(); tc
452
[1, 0, 0, 2, 0, 1]
453
sage: tc[0].parent()
454
Tropical semiring over Integer Ring
455
456
sage: b = B([-2, -2, -1, -1, 2, 2, 1, 1])
457
sage: b.tropical_coordinates()
458
[1, -19, -12, 9, 0, 13]
459
460
REFERENCES:
461
462
.. [Dynnikov07] I. Dynnikov and B. Wiest, On the complexity of braids,
463
J. Europ. Math. Soc. 9 (2007)
464
.. [Dehornoy] P. Dehornoy, Le probleme d'isotopie des tresses, in
465
lecons de mathematiques d'aujourd'hui vol. 4
466
"""
467
coord = [0, 1] * self.strands()
468
for s in self.Tietze():
469
k = 2*(abs(s)-1)
470
x1, y1, x2, y2 = coord[k:k+4]
471
if s > 0:
472
sign = 1
473
z = x1 - min(y1, 0) - x2 + max(y2, 0)
474
coord[k+1] = y2 - max(z, 0)
475
coord[k+3] = y1 + max(z, 0)
476
else:
477
sign = -1
478
z = x1 + min(y1, 0) - x2 - max(y2, 0)
479
coord[k+1] = y2 + min(z, 0)
480
coord[k+3] = y1 - min(z, 0)
481
482
coord[k] = x1 + sign*(max(y1, 0) + max(max(y2, 0) - sign*z, 0))
483
coord[k+2] = x2 + sign*(min(y2, 0) + min(min(y1, 0) + sign*z, 0))
484
485
from sage.rings.semirings.tropical_semiring import TropicalSemiring
486
T = TropicalSemiring(IntegerRing())
487
return map(T, coord)
488
489
@cached_method
490
def left_normal_form(self):
491
"""
492
Return the left normal form of the braid.
493
494
OUTPUT:
495
496
A tuple of braid generators in the left normal form. The first
497
element is a power of $\Delta$, and the rest are permutation
498
braids.
499
500
EXAMPLES::
501
502
sage: B = BraidGroup(4)
503
sage: b = B([1, 2, 3, -1, 2, -3])
504
sage: b.left_normal_form()
505
(s0^-1*s1^-1*s2^-1*s0^-1*s1^-1*s0^-1, s0*s1*s2*s1*s0, s0*s2*s1)
506
sage: c = B([1])
507
sage: c.left_normal_form()
508
(1, s0)
509
"""
510
lnfp = self._left_normal_form_perm_()
511
a = lnfp[0]
512
l = lnfp[1:]
513
n = self.strands()
514
delta = Permutation([n-i for i in range(n)])
515
P = self.parent()
516
return tuple( [P._permutation_braid(delta).__pow__(a)] +
517
map(lambda i:P._permutation_braid(i), l) )
518
519
def _left_normal_form_perm_(self):
520
"""
521
Return the left normal form of the braid, in permutation form.
522
523
OUTPUT:
524
525
A tuple whose first element is the power of $\Delta$, and the
526
rest are the permutations corresponding to the simple factors.
527
528
EXAMPLES::
529
530
sage: B = BraidGroup(12)
531
sage: B([2, 2, 2, 3, 1, 2, 3, 2, 1, -2])._left_normal_form_perm_()
532
(-1,
533
[12, 11, 10, 9, 8, 7, 6, 5, 2, 4, 3, 1],
534
[4, 1, 3, 2, 5, 6, 7, 8, 9, 10, 11, 12],
535
[2, 3, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12],
536
[3, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12],
537
[2, 3, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12])
538
sage: C=BraidGroup(6)
539
sage: C([2, 3, -4, 2, 3, -5, 1, -2, 3, 4, 1, -2])._left_normal_form_perm_()
540
(-2, [3, 5, 4, 2, 6, 1], [1, 6, 3, 5, 2, 4], [5, 6, 2, 4, 1, 3],
541
[3, 2, 4, 1, 5, 6], [1, 5, 2, 3, 4, 6])
542
"""
543
n = self.parent().strands()
544
delta = 0
545
Delta = Permutation([n-i for i in range(n)])
546
l = self.Tietze()
547
if l==():
548
return (0,)
549
form = []
550
for i in l:
551
if i>0:
552
form.append(Permutation((i, i+1)))
553
else:
554
delta = delta+1
555
form = map(lambda a: Delta*a*Delta, form)
556
form.append(Delta*Permutation((-i, -i+1)))
557
i = j = 0
558
while j<len(form):
559
while i<len(form)-j-1:
560
e = form[i].inverse().descents()
561
s = form[i+1].descents()
562
S = set(s).difference(set(e))
563
while S!=set([]):
564
a = list(S)[0]
565
form[i] = form[i]*Permutation((a+1, a+2))
566
form[i+1] = Permutation((a+1, a+2))*form[i+1]
567
e = form[i].inverse().descents()
568
s = form[i+1].descents()
569
S = set(s).difference(set(e))
570
if form[i+1].length()==0:
571
form.pop(i+1)
572
i = 0
573
else:
574
i += 1
575
j += 1
576
i = 0
577
form = filter(lambda a: a.length()>0, form)
578
while form!=[] and form[0]==Delta:
579
form.pop(0)
580
delta = delta-1
581
return tuple([-delta]+form)
582
583
584
class BraidGroup_class(FinitelyPresentedGroup):
585
"""
586
The braid group on `n` strands.
587
588
EXAMPLES::
589
590
sage: B1 = BraidGroup(5)
591
sage: B1
592
Braid group on 5 strands
593
sage: B2 = BraidGroup(3)
594
sage: B1==B2
595
False
596
sage: B2 is BraidGroup(3)
597
True
598
"""
599
Element = Braid
600
601
def __init__(self, names):
602
"""
603
Python constructor.
604
605
INPUT:
606
607
- ``names`` -- a tuple of strings. The names of the
608
generators.
609
610
TESTS::
611
612
sage: B1 = BraidGroup(5) # indirect doctest
613
sage: B1
614
Braid group on 5 strands
615
616
Check that :trac:`14081` is fixed::
617
618
sage: BraidGroup(2)
619
Braid group on 2 strands
620
sage: BraidGroup(('a',))
621
Braid group on 2 strands
622
623
Check that :trac:`15505` is fixed::
624
625
sage: B=BraidGroup(4)
626
sage: B.relations()
627
(s0*s1*s0*s1^-1*s0^-1*s1^-1, s0*s2*s0^-1*s2^-1, s1*s2*s1*s2^-1*s1^-1*s2^-1)
628
sage: B=BraidGroup('a,b,c,d,e,f')
629
sage: B.relations()
630
(a*b*a*b^-1*a^-1*b^-1,
631
a*c*a^-1*c^-1,
632
a*d*a^-1*d^-1,
633
a*e*a^-1*e^-1,
634
a*f*a^-1*f^-1,
635
b*c*b*c^-1*b^-1*c^-1,
636
b*d*b^-1*d^-1,
637
b*e*b^-1*e^-1,
638
b*f*b^-1*f^-1,
639
c*d*c*d^-1*c^-1*d^-1,
640
c*e*c^-1*e^-1,
641
c*f*c^-1*f^-1,
642
d*e*d*e^-1*d^-1*e^-1,
643
d*f*d^-1*f^-1,
644
e*f*e*f^-1*e^-1*f^-1)
645
"""
646
n = len(names)
647
if n<1: #n is the number of generators, not the number of strands (see ticket 14081)
648
raise ValueError("the number of strands must be an integer bigger than one")
649
free_group = FreeGroup(names)
650
rels = []
651
for i in range(1, n):
652
rels.append(free_group([i, i+1, i, -i-1, -i, -i-1]))
653
for j in range(i+2, n+1):
654
rels.append(free_group([i, j, -i, -j]))
655
FinitelyPresentedGroup.__init__(self, free_group, tuple(rels))
656
self._nstrands_ = n+1
657
658
def __reduce__(self):
659
"""
660
TESTS::
661
662
sage: B = BraidGroup(3)
663
sage: B.__reduce__()
664
(<class 'sage.groups.braid.BraidGroup_class'>, (('s0', 's1'),))
665
sage: B = BraidGroup(3, 'sigma')
666
sage: B.__reduce__()
667
(<class 'sage.groups.braid.BraidGroup_class'>, (('sigma0', 'sigma1'),))
668
"""
669
return (BraidGroup_class, (self.variable_names(), ))
670
671
def _repr_(self):
672
"""
673
Return a string representation
674
675
OUTPUT:
676
677
String.
678
679
TESTS::
680
681
sage: B1 = BraidGroup(5)
682
sage: B1 # indirect doctest
683
Braid group on 5 strands
684
"""
685
return "Braid group on %s strands"%self._nstrands_
686
687
def cardinality(self):
688
"""
689
Return the number of group elements.
690
691
OUTPUT:
692
693
Infinity.
694
695
TESTS::
696
697
sage: B1 = BraidGroup(5)
698
sage: B1.cardinality()
699
+Infinity
700
"""
701
from sage.rings.infinity import Infinity
702
return Infinity
703
704
order = cardinality
705
706
def as_permutation_group(self):
707
"""
708
Return an isomorphic permutation group.
709
710
OUTPUT:
711
712
Raises a ``ValueError`` error since braid groups are infinite.
713
714
TESTS::
715
716
sage: B = BraidGroup(4, 'g')
717
sage: B.as_permutation_group()
718
Traceback (most recent call last):
719
...
720
ValueError: the group is infinite
721
"""
722
raise ValueError("the group is infinite")
723
724
def strands(self):
725
"""
726
Return the number of strands.
727
728
OUTPUT:
729
730
Integer.
731
732
EXAMPLES::
733
734
sage: B = BraidGroup(4)
735
sage: B.strands()
736
4
737
"""
738
return self._nstrands_
739
740
def _element_constructor_(self, x):
741
"""
742
TESTS::
743
744
sage: B = BraidGroup(4)
745
sage: B([1, 2, 3]) # indirect doctest
746
s0*s1*s2
747
"""
748
return Braid(self, x)
749
750
def _permutation_braid_Tietze(self, p):
751
"""
752
Helper for :meth:`_permutation_braid`.
753
754
INPUT:
755
756
- ``p`` -- a permutation.
757
758
OUTPUT:
759
760
The lexicographically smallest word that represents the braid,
761
in Tietze list form.
762
763
EXAMPLES::
764
765
sage: B=BraidGroup(5)
766
sage: P=Permutation([5, 3, 1, 2, 4])
767
sage: B._permutation_braid_Tietze(P)
768
(1, 2, 1, 3, 2, 4)
769
"""
770
if p.length() == 0:
771
return ()
772
pl = p
773
l = []
774
while pl.length()>0:
775
i = 1
776
while i<max(pl):
777
if pl(i)>pl(i+1):
778
l.append(i)
779
pl = Permutation([(i, i+1)])*pl
780
i = 1
781
else:
782
i = i+1
783
return tuple(l)
784
785
@cached_method
786
def _permutation_braid(self, p):
787
"""
788
Return the braid that corresponds to the given permutation.
789
790
It is the only braid with the following properties:
791
792
- The braid induces the given permutation.
793
794
- The braid is positive (that is, it can be writen without using the inverses of the generators).
795
796
- Every two strands cross each other at most once.
797
798
INPUT:
799
800
- ``p`` -- a permutation.
801
802
OUTPUT:
803
804
The braid that corresponds to the permutation.
805
806
EXAMPLES::
807
808
sage: B = BraidGroup(5)
809
sage: P = Permutation([5, 3, 1, 2, 4])
810
sage: B._permutation_braid(P)
811
s0*s1*s0*s2*s1*s3
812
"""
813
return self(self._permutation_braid_Tietze(p))
814
815
@cached_method
816
def _LKB_matrix_(self, braid, variab):
817
"""
818
Compute the Lawrence-Krammer-Bigelow representation matrix.
819
820
The variables of the matrix must be given. This actual
821
computation is done in this helper method for caching
822
purposes.
823
824
INPUT:
825
826
- ``braid`` -- tuple of integers. The Tietze list of the
827
braid.
828
829
- ``variab`` -- string. the names of the variables that will
830
appear in the matrix. They must be given as a string,
831
separated by a comma
832
833
OUTPUT:
834
835
The LKB matrix of the braid, with respect to the variables.
836
837
TESTS::
838
839
sage: B=BraidGroup(3)
840
sage: B._LKB_matrix_((2, 1, 2), 'x, y')
841
[ 0 -x^4*y + x^3*y -x^4*y]
842
[ 0 -x^3*y 0]
843
[ -x^2*y x^3*y - x^2*y 0]
844
sage: B._LKB_matrix_((1, 2, 1), 'x, y')
845
[ 0 -x^4*y + x^3*y -x^4*y]
846
[ 0 -x^3*y 0]
847
[ -x^2*y x^3*y - x^2*y 0]
848
sage: B._LKB_matrix_((-1, -2, -1, 2, 1, 2), 'x, y')
849
[1 0 0]
850
[0 1 0]
851
[0 0 1]
852
"""
853
n = self.strands()
854
if len(braid)>1:
855
A = self._LKB_matrix_(braid[:1], variab)
856
for i in braid[1:]:
857
A = A*self._LKB_matrix_((i,), variab)
858
return A
859
l = list(Set(range(n)).subsets(2))
860
R = LaurentPolynomialRing(IntegerRing(), variab)
861
q = R.gens()[0]
862
t = R.gens()[1]
863
if len(braid)==0:
864
return identity_matrix(R, len(l), sparse=True)
865
A = matrix(R, len(l), sparse=True)
866
if braid[0]>0:
867
i = braid[0]-1
868
for m in range(len(l)):
869
j = min(l[m])
870
k = max(l[m])
871
if i==j-1:
872
A[l.index(Set([i, k])), m] = q
873
A[l.index(Set([i, j])), m] = q*q-q
874
A[l.index(Set([j, k])), m] = 1-q
875
elif i==j and not j==k-1:
876
A[l.index(Set([j, k])), m] = 0
877
A[l.index(Set([j+1, k])), m] = 1
878
elif k-1==i and not k-1==j:
879
A[l.index(Set([j, i])), m] = q
880
A[l.index(Set([j, k])), m] = 1-q
881
A[l.index(Set([i, k])), m] = (1-q)*q*t
882
elif i==k:
883
A[l.index(Set([j, k])), m] = 0
884
A[l.index(Set([j, k+1])), m] = 1
885
elif i==j and j==k-1:
886
A[l.index(Set([j, k])), m] = -t*q*q
887
else:
888
A[l.index(Set([j, k])), m] = 1
889
return A
890
else:
891
i = -braid[0]-1
892
for m in range(len(l)):
893
j = min(l[m])
894
k = max(l[m])
895
if i==j-1:
896
A[l.index(Set([j-1, k])), m] = 1
897
elif i==j and not j==k-1:
898
A[l.index(Set([j+1, k])), m] = q**(-1)
899
A[l.index(Set([j, k])), m] = 1-q**(-1)
900
A[l.index(Set([j, j+1])), m] = t**(-1)*q**(-1)-t**(-1)*q**(-2)
901
elif k-1==i and not k-1==j:
902
A[l.index(Set([j, k-1])), m] = 1
903
elif i==k:
904
A[l.index(Set([j, k+1])), m] = q**(-1)
905
A[l.index(Set([j, k])), m] = 1-q**(-1)
906
A[l.index(Set([k, k+1])), m] = -q**(-1)+q**(-2)
907
elif i==j and j==k-1:
908
A[l.index(Set([j, k])), m] = -t**(-1)*q**(-2)
909
else:
910
A[l.index(Set([j, k])), m] = 1
911
return A
912
913
def mapping_class_action(self, F):
914
"""
915
Return the action of self in the free group F as mapping class group.
916
917
This action corresponds to the action of the braid over the
918
punctured disk, whose fundamental group is the free group on
919
as many generators as strands.
920
921
In Sage, this action is the result of multiplying a free group
922
element with a braid. So you generally do not have to
923
construct this action yourself.
924
925
OUTPUT:
926
927
A :class:`MappingClassGroupAction`.
928
929
EXAMPLES ::
930
931
sage: B = BraidGroup(3)
932
sage: B.inject_variables()
933
Defining s0, s1
934
sage: F.<a,b,c> = FreeGroup(3)
935
sage: A = B.mapping_class_action(F)
936
sage: A(a,s0)
937
a*b*a^-1
938
sage: a * s0 # simpler notation
939
a*b*a^-1
940
"""
941
return MappingClassGroupAction(self, F)
942
943
def _get_action_(self, S, op, self_on_left):
944
"""
945
Let the coercion system discover actions of the braid group on free groups.
946
947
sage: B.<b0,b1,b2> = BraidGroup()
948
sage: F.<f0,f1,f2,f3> = FreeGroup()
949
sage: f1 * b1
950
f1*f2*f1^-1
951
952
sage: from sage.structure.all import get_coercion_model
953
sage: cm = get_coercion_model()
954
sage: cm.explain(f1, b1, operator.mul)
955
Action discovered.
956
Right action by Braid group on 4 strands on Free Group on generators {f0, f1, f2, f3}
957
Result lives in Free Group on generators {f0, f1, f2, f3}
958
Free Group on generators {f0, f1, f2, f3}
959
sage: cm.explain(b1, f1, operator.mul)
960
Will try _r_action and _l_action
961
Unknown result parent.
962
"""
963
import operator
964
if is_FreeGroup(S) and op==operator.mul and not self_on_left:
965
return self.mapping_class_action(S)
966
return None
967
968
969
970
def BraidGroup(n=None, names='s'):
971
"""
972
Construct a Braid Group
973
974
INPUT:
975
976
- ``n`` -- integer or ``None`` (default). The number of
977
strands. If not specified the ``names`` are counted and the
978
group is assumed to have one more strand than generators.
979
980
- ``names`` -- string or list/tuple/iterable of strings (default:
981
``'x'``). The generator names or name prefix.
982
983
EXAMPLES::
984
985
sage: B.<a,b> = BraidGroup(); B
986
Braid group on 3 strands
987
sage: H = BraidGroup('a, b')
988
sage: B is H
989
True
990
sage: BraidGroup(3)
991
Braid group on 3 strands
992
993
The entry can be either a string with the names of the generators,
994
or the number of generators and the prefix of the names to be
995
given. The default prefix is ``'s'`` ::
996
997
sage: B=BraidGroup(3); B.generators()
998
(s0, s1)
999
sage: BraidGroup(3, 'g').generators()
1000
(g0, g1)
1001
1002
TESTS::
1003
1004
sage: G1 = BraidGroup(3, 'a,b')
1005
sage: G2 = BraidGroup('a,b')
1006
sage: G3.<a,b> = BraidGroup()
1007
sage: G1 is G2, G2 is G3
1008
(True, True)
1009
"""
1010
# Support Freegroup('a,b') syntax
1011
if n is not None:
1012
try:
1013
n = Integer(n)-1
1014
except TypeError:
1015
names = n
1016
n = None
1017
# derive n from counting names
1018
if n is None:
1019
if isinstance(names, basestring):
1020
n = len(names.split(','))
1021
else:
1022
names = list(names)
1023
n = len(names)
1024
from sage.structure.parent import normalize_names
1025
names = tuple(normalize_names(n, names))
1026
return BraidGroup_class(names)
1027
1028
1029
1030
class MappingClassGroupAction(Action):
1031
r"""
1032
The action of the braid group the free group as the mapping class
1033
group of the punctured disk.
1034
1035
That is, this action is the action of the braid over the punctured
1036
disk, whose fundamental group is the free group on as many
1037
generators as strands.
1038
1039
This action is defined as follows:
1040
1041
.. MATH::
1042
1043
x_j \cdot \sigma_i=\begin{cases}
1044
x_{j}\cdot x_{j+1}\cdot {x_j}^{-1} & \text{if $i=j$} \\
1045
x_{j-1} & \text{if $i=j-1$} \\
1046
x_{j} & \text{otherwise}
1047
\end{cases},
1048
1049
where $\sigma_i$ are the generators of the braid group on $n$
1050
strands, and $x_j$ the generators of the free group of rank $n$.
1051
1052
You should left multiplication of the free group element by the
1053
braid to compute the action. Alternatively, use the
1054
:meth:`~sage.groups.braid.BraidGroup_class.mapping_class_action`
1055
method of the braid group to constuct this action.
1056
1057
EXAMPLES::
1058
1059
sage: B.<s0,s1,s2> = BraidGroup(4)
1060
sage: F.<x0,x1,x2,x3> = FreeGroup(4)
1061
sage: x0 * s1
1062
x0
1063
sage: x1 * s1
1064
x1*x2*x1^-1
1065
sage: x1^-1 * s1
1066
x1*x2^-1*x1^-1
1067
1068
sage: A = B.mapping_class_action(F)
1069
sage: A
1070
Right action by Braid group on 4 strands on Free Group on generators {x0, x1, x2, x3}
1071
sage: A(x0, s1)
1072
x0
1073
sage: A(x1, s1)
1074
x1*x2*x1^-1
1075
sage: A(x1^-1, s1)
1076
x1*x2^-1*x1^-1
1077
"""
1078
def __init__(self, G, M, is_left=0):
1079
"""
1080
TESTS::
1081
1082
sage: B = BraidGroup(3)
1083
sage: G = FreeGroup('a, b, c')
1084
sage: B.mapping_class_action(G) # indirect doctest
1085
Right action by Braid group on 3 strands on Free Group on generators {a, b, c}
1086
"""
1087
import operator
1088
Action.__init__(self, G, M, is_left, operator.mul)
1089
1090
def _call_(self, x, b):
1091
"""
1092
Return the action of ``b`` on ``x``.
1093
1094
INPUT:
1095
1096
- ``x`` -- a free group element.
1097
1098
- ``b`` -- a braid.
1099
1100
OUTPUT:
1101
1102
A new braid.
1103
1104
TESTS::
1105
1106
sage: B = BraidGroup(3)
1107
sage: G = FreeGroup('a, b, c')
1108
sage: A = B.mapping_class_action(G)
1109
sage: A(G.0, B.0) # indirect doctest
1110
a*b*a^-1
1111
sage: A(G.1, B.0) # indirect doctest
1112
a
1113
"""
1114
t = x.Tietze()
1115
for j in b.Tietze():
1116
s=[]
1117
for i in t:
1118
if j==i and i>0:
1119
s += [i, i+1, -i]
1120
elif j==-i and i<0:
1121
s += [-i, i-1, i]
1122
elif j==-i and i>0:
1123
s += [i+1]
1124
elif j==i and i<0:
1125
s += [i-1]
1126
elif i>0 and j==i-1:
1127
s += [i-1]
1128
elif i<0 and j==-i-1:
1129
s += [i+1]
1130
elif i>0 and -j==i-1:
1131
s += [-i, i-1, i]
1132
elif i<0 and j==i+1:
1133
s += [i, i+1, -i]
1134
else:
1135
s += [i]
1136
t = s
1137
return self.codomain()(t)
1138
1139