passagemath-groups 10.6.45__cp314-cp314-macosx_13_0_arm64.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.
Files changed (40) hide show
  1. passagemath_groups/.dylibs/libgap.10.dylib +0 -0
  2. passagemath_groups/.dylibs/libgmp.10.dylib +0 -0
  3. passagemath_groups/.dylibs/libreadline.8.2.dylib +0 -0
  4. passagemath_groups/.dylibs/libz.1.3.1.dylib +0 -0
  5. passagemath_groups/__init__.py +3 -0
  6. passagemath_groups-10.6.45.dist-info/METADATA +113 -0
  7. passagemath_groups-10.6.45.dist-info/RECORD +40 -0
  8. passagemath_groups-10.6.45.dist-info/WHEEL +6 -0
  9. passagemath_groups-10.6.45.dist-info/top_level.txt +3 -0
  10. sage/all__sagemath_groups.py +21 -0
  11. sage/geometry/all__sagemath_groups.py +1 -0
  12. sage/geometry/palp_normal_form.cpython-314-darwin.so +0 -0
  13. sage/geometry/palp_normal_form.pyx +401 -0
  14. sage/groups/abelian_gps/all.py +25 -0
  15. sage/groups/all.py +5 -0
  16. sage/groups/all__sagemath_groups.py +32 -0
  17. sage/groups/artin.py +1074 -0
  18. sage/groups/braid.py +3806 -0
  19. sage/groups/cactus_group.py +1001 -0
  20. sage/groups/cubic_braid.py +2052 -0
  21. sage/groups/finitely_presented.py +1896 -0
  22. sage/groups/finitely_presented_catalog.py +27 -0
  23. sage/groups/finitely_presented_named.py +592 -0
  24. sage/groups/fqf_orthogonal.py +579 -0
  25. sage/groups/free_group.py +944 -0
  26. sage/groups/group_exp.py +360 -0
  27. sage/groups/group_semidirect_product.py +504 -0
  28. sage/groups/kernel_subgroup.py +231 -0
  29. sage/groups/lie_gps/all.py +1 -0
  30. sage/groups/lie_gps/catalog.py +8 -0
  31. sage/groups/lie_gps/nilpotent_lie_group.py +945 -0
  32. sage/groups/misc_gps/all.py +1 -0
  33. sage/groups/misc_gps/misc_groups.py +11 -0
  34. sage/groups/misc_gps/misc_groups_catalog.py +33 -0
  35. sage/groups/raag.py +866 -0
  36. sage/groups/semimonomial_transformations/all.py +1 -0
  37. sage/groups/semimonomial_transformations/semimonomial_transformation.cpython-314-darwin.so +0 -0
  38. sage/groups/semimonomial_transformations/semimonomial_transformation.pxd +9 -0
  39. sage/groups/semimonomial_transformations/semimonomial_transformation.pyx +346 -0
  40. sage/groups/semimonomial_transformations/semimonomial_transformation_group.py +512 -0
@@ -0,0 +1,2052 @@
1
+ # sage_setup: distribution = sagemath-groups
2
+ # sage.doctest: needs sage.combinat
3
+ r"""
4
+ Cubic Braid Groups
5
+
6
+ This module is devoted to factor groups of the Artin braid groups, such
7
+ that the images `s_i` of the braid generators have order three:
8
+
9
+ .. MATH::
10
+
11
+ s_i^3 = 1.
12
+
13
+ In general these groups have firstly been investigated by Coxeter, H.S.M.
14
+ in: "Factor groups of the braid groups, Proceedings of the Fourth Canadian
15
+ Mathematical Congress (Vancouver 1957), pp. 95-122".
16
+
17
+ Coxeter showed, that these groups are finite as long as the number of
18
+ strands is less than 6 and infinite else-wise. More explicitly the factor
19
+ group on three strand braids is isomorphic to `SL(2,3)`, on four strand
20
+ braids to `GU(3,2)` and on five strand braids to `Sp(4,3) \times C_3`.
21
+ Today, these finite groups are known as irreducible complex reflection groups
22
+ enumerated in the Shephard-Todd classification as `G_{4}`, `G_{25}` and
23
+ `G_{32}`.
24
+
25
+ Coxeter realized these groups as subgroups of unitary groups with respect
26
+ to a certain Hermitian form over the complex numbers (in fact over `\QQ`
27
+ adjoined with a primitive `12`-th root of unity).
28
+
29
+ In "Einige endliche Faktorgruppen der Zopfgruppen" (Math. Z., 163 (1978),
30
+ 291-302) J. Assion considered two series `S(m)` and `U(m)` of finite
31
+ factors of these groups. The additional relations on the braid group
32
+ generators `\{ s_1, \cdots, s_{m-1}\}` are
33
+
34
+ .. MATH::
35
+
36
+ \begin{array}{lll}
37
+ \mbox{S:} & s_3 s_1 t_2 s_1 t_2^{-1} t_3 t_2 s_1 t_2^{-1} t_3^{-1} = 1
38
+ & \mbox{ for } m >= 5 \mbox{ in case } S(m)\\
39
+ \mbox{U:} & t_1 t_3 = 1
40
+ & \mbox{ for } m >= 5 \mbox{ in case } U(m)
41
+ \end{array}
42
+
43
+ where `t_i = (s_i s_{i+1})^3`. He showed that each series of finite cubic
44
+ braid group factors must be an epimorphic image of one of his two series,
45
+ as long as the groups with less than 5 strands are the full cubic braid
46
+ groups, whereas the group on 5 strands is not. He realized the groups `S(m)`
47
+ as symplectic groups over `GF(3)` (resp. subgroups therein) and `U(m)` as
48
+ general unitary groups over `GF(4)` (resp. subgroups therein).
49
+
50
+ All the groups considered by Coxeter and Assion are considered as finitely
51
+ presented groups together with the classical realizations. It also allows
52
+ for the conversion maps between the two realizations. In addition, we can
53
+ construct other realizations and maps to matrix groups with help of the
54
+ Burau representation. In case ``gap3`` and ``CHEVIE`` are installed, the
55
+ reflection groups (via the ``gap3`` interface) are available, too. This can
56
+ be done using the methods :meth:`as_classical_group`, :meth:`as_matrix_group`,
57
+ :meth:`as_permutation_group`, and :meth:`as_reflection_group`.
58
+
59
+ TESTS::
60
+
61
+ sage: CubicBraidGroup(4).category()
62
+ Category of shephard groups
63
+ sage: CubicBraidGroup(6).category()
64
+ Category of infinite groups
65
+
66
+ REFERENCES:
67
+
68
+ - [Cox1957]_
69
+ - [Ass1978]_
70
+
71
+ AUTHORS:
72
+
73
+ - Sebastian Oehms 2019-02-16, initial version.
74
+ """
75
+ # ****************************************************************************
76
+ # Copyright (C) 2019 Sebastian Oehms <seb.oehms@gmail.com>
77
+ #
78
+ # This program is free software: you can redistribute it and/or modify
79
+ # it under the terms of the GNU General Public License as published by
80
+ # the Free Software Foundation, either version 2 of the License, or
81
+ # (at your option) any later version.
82
+ # https://www.gnu.org/licenses/
83
+ # ****************************************************************************
84
+
85
+ from enum import Enum
86
+
87
+ import sage.rings.abc
88
+
89
+ from sage.categories.groups import Groups
90
+ from sage.categories.shephard_groups import ShephardGroups
91
+ from sage.groups.free_group import FreeGroup
92
+ from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement
93
+ from sage.groups.braid import BraidGroup
94
+ from sage.misc.cachefunc import cached_method
95
+ from sage.rings.integer import Integer
96
+ from sage.structure.unique_representation import UniqueRepresentation
97
+
98
+
99
+ try:
100
+ from sage.libs.gap.element import GapElement
101
+ except ImportError:
102
+ GapElement = ()
103
+
104
+
105
+ ##############################################################################
106
+ #
107
+ # helper functions
108
+ #
109
+ ##############################################################################
110
+
111
+ def _reduce_tietze(tietze_list):
112
+ r"""
113
+ Reduce the length of a list representing a cubic braid as much as it is
114
+ easily possible using the second braid relation and degree reduction.
115
+
116
+ EXAMPLES::
117
+
118
+ sage: from sage.groups.cubic_braid import _reduce_tietze
119
+ sage: _reduce_tietze((2, 2, -3, 5, 3, 1, 1, 5))
120
+ [-2, -5, -1]
121
+ """
122
+ def eliminate_item(tietze_list):
123
+ """
124
+ This sub method searches for an item in the Tietze expression such
125
+ that it together with the first entry gives a pair which can be
126
+ replaced by the second braid relation and the generators degree
127
+ reduction. If no such pair exists, it returns ``None``. Otherwise
128
+ the reduced tietze list is returned.
129
+ """
130
+ l = len(tietze_list)
131
+ if l < 2:
132
+ return None
133
+ first = tietze_list[0]
134
+ second = None
135
+ for i in range(1, l):
136
+ if tietze_list[i] in (first, -first):
137
+ if i == 1:
138
+ second = tietze_list[i]
139
+ break
140
+ if all(abs(abs(tietze_list[j])-abs(first)) > 1 for j in range(1, i)):
141
+ # the entry on position i can be moved right to the first entry
142
+ # by the second braid relation
143
+ second = tietze_list[i]
144
+ break
145
+ if second is None:
146
+ return None
147
+ middle = tietze_list[1:i]
148
+ end = tietze_list[i+1:l]
149
+ if first == second:
150
+ return [-first] + middle + end
151
+ else:
152
+ return middle + end
153
+
154
+ tietze_list = list(tietze_list)
155
+ l = len(tietze_list)
156
+ for i in range(l):
157
+ end = tietze_list[i:l]
158
+ tietze_list_red = eliminate_item(end)
159
+ if tietze_list_red is not None:
160
+ start = tietze_list[:i]
161
+ return start + _reduce_tietze(tietze_list_red)
162
+ return tietze_list
163
+
164
+
165
+ ##############################################################################
166
+ #
167
+ # Functions to create Instances of the CubicBraidGroup
168
+ #
169
+ ##############################################################################
170
+
171
+
172
+ # ----------------------------------------------------------------------------------
173
+ # Short-Hands for Assions groups:
174
+ # ----------------------------------------------------------------------------------
175
+ def AssionGroupS(n=None, names='s'):
176
+ r"""
177
+ Construct cubic braid groups :class:`CubicBraidGroup` which have been
178
+ investigated by J.Assion using the notation S(m). This function is a short hand cut
179
+ for setting the construction arguments ``cbg_type=CubicBraidGroup.type.AssionS``
180
+ and default ``names='s'``.
181
+
182
+ INPUT:
183
+
184
+ - ``n`` -- integer (optional); the number of strands
185
+ - ``names`` -- (default: ``'s'``) string or list/tuple/iterable of strings
186
+
187
+ .. SEEALSO::
188
+
189
+ :class:`CubicBraidGroup`
190
+
191
+ EXAMPLES::
192
+
193
+ sage: S3 = AssionGroupS(3); S3
194
+ Assion group on 3 strands of type S
195
+ sage: S3x = CubicBraidGroup(3, names='s', cbg_type=CubicBraidGroup.type.AssionS); S3x
196
+ Assion group on 3 strands of type S
197
+ sage: S3 == S3x
198
+ True
199
+ """
200
+ return CubicBraidGroup(n=n, names=names, cbg_type=CubicBraidGroup.type.AssionS)
201
+
202
+
203
+ def AssionGroupU(n=None, names='u'):
204
+ r"""
205
+ Construct cubic braid groups as instance of :class:`CubicBraidGroup` which have been
206
+ investigated by J.Assion using the notation U(m). This function is a short hand cut
207
+ for setting the construction arguments ``cbg_type=CubicBraidGroup.type.AssionU``
208
+ and default ``names='u'``.
209
+
210
+ INPUT:
211
+
212
+ - ``n`` -- integer (optional); the number of strands
213
+ - ``names`` -- (default: ``'s'``) string or list/tuple/iterable of strings
214
+
215
+ .. SEEALSO::
216
+
217
+ :class:`CubicBraidGroup`
218
+
219
+ EXAMPLES::
220
+
221
+ sage: U3 = AssionGroupU(3); U3
222
+ Assion group on 3 strands of type U
223
+ sage: U3x = CubicBraidGroup(3, names='u', cbg_type=CubicBraidGroup.type.AssionU); U3x
224
+ Assion group on 3 strands of type U
225
+ sage: U3 == U3x
226
+ True
227
+ """
228
+ return CubicBraidGroup(n=n, names=names, cbg_type=CubicBraidGroup.type.AssionU)
229
+
230
+
231
+ ##############################################################################
232
+ #
233
+ # Class CubicBraidElement (for elements)
234
+ #
235
+ ##############################################################################
236
+
237
+ class CubicBraidElement(FinitelyPresentedGroupElement):
238
+ r"""
239
+ Elements of cubic factor groups of the braid group.
240
+
241
+ For more information see :class:`CubicBraidGroup`.
242
+
243
+ EXAMPLES::
244
+
245
+ sage: C4.<c1, c2, c3> = CubicBraidGroup(4); C4
246
+ Cubic Braid group on 4 strands
247
+ sage: ele1 = c1*c2*c3^-1*c2^-1
248
+ sage: ele2 = C4((1, 2, -3, -2))
249
+ sage: ele1 == ele2
250
+ True
251
+ """
252
+ def __init__(self, parent, x, check=True):
253
+ """
254
+ Initialize ``self``.
255
+
256
+ EXAMPLES::
257
+
258
+ sage: C6 = CubicBraidGroup(6)
259
+ sage: C6.inject_variables()
260
+ Defining c0, c1, c2, c3, c4
261
+ sage: c1**2*~c2*c4*c0**2*c4 # indirect doctest
262
+ c1^-1*c2^-1*c4^-1*c0^-1
263
+ """
264
+ if type(x) in (tuple, list):
265
+ x = tuple(_reduce_tietze(tuple(x)))
266
+ elif isinstance(x, GapElement):
267
+ tietze_list = x.UnderlyingElement().TietzeWordAbstractWord().sage()
268
+ tietze_red = _reduce_tietze(tietze_list)
269
+ if tietze_red != tietze_list:
270
+ x = tuple(tietze_red)
271
+ super().__init__(parent, x, check=check)
272
+
273
+ def _richcmp_(self, other, op):
274
+ """
275
+ Rich comparison of ``self`` with ``other``.
276
+
277
+ Overwrite comparison since the inherited one from
278
+ :class:`FinitelyPresentedGroupElement` (via Gap) does not terminate
279
+ in the case of more than 5 strands (not only infinite cases).
280
+ On less than 5 strands comparison is not assumed to be deterministic
281
+ (see the :issue:`33498` and :gap:`section 47.3-2 of the
282
+ Gap Reference manual <chap47>`).
283
+
284
+ Therefore, the comparison is done via the Burau representation.
285
+
286
+ EXAMPLES::
287
+
288
+ sage: CBG3 = CubicBraidGroup(3)
289
+ sage: sorted(CBG3) # indirect doctest, random output # needs sage.rings.number_field
290
+ [(c0*c1^-1)^2, c0*c1^-1*c0, c0^-1*c1*c0^-1, c0^-1*c1^-1*c0,
291
+ c1*c0^-1*c1, c0^-1*c1^-1*c0^-1, c0^-1*c1^-1, c1^-1*c0*c1^-1,
292
+ c0*c1^-1*c0^-1, c0^-1*c1, c0^-1*c1*c0, c0*c1^-1, c1*c0^-1,
293
+ c1^-1*c0^-1, c1^-1*c0, c1*c0, c0^-1, c0*c1*c0^-1, c0*c1*c0,
294
+ c0, c0*c1, c1^-1, c1, 1]
295
+
296
+ sage: C6.<c1, c2, c3, c4, c5> = CubicBraidGroup(6)
297
+ sage: ele1 = c1*c2*c3*c4*c5
298
+ sage: ele2 = c1*c2*c4*c5
299
+ sage: ele1 == ele2 # indirect doctest
300
+ False
301
+ sage: ele1 > ele2 # indirect doctest
302
+ True
303
+
304
+ TESTS::
305
+
306
+ sage: S7 = AssionGroupS(7)
307
+ sage: all(S7(rel).is_one() for rel in S7.relations())
308
+ True
309
+ """
310
+ smat = self._matrix_()
311
+ omat = other._matrix_()
312
+ return smat._richcmp_(omat, op)
313
+
314
+ def __hash__(self):
315
+ r"""
316
+ Return a hash value for ``self``.
317
+
318
+ EXAMPLES::
319
+
320
+ sage: C3.<c1, c2> = CubicBraidGroup(3)
321
+ sage: hash(~c1) == hash(c1**2) # needs sage.rings.number_field
322
+ True
323
+ """
324
+ return hash(self._matrix_())
325
+
326
+ @cached_method
327
+ def _matrix_(self):
328
+ r"""
329
+ Return ``self`` as a matrix according to its parent's default matrix
330
+ group.
331
+
332
+ So far, this method returns the same results as :meth:`burau_matrix`
333
+ in the default case.
334
+
335
+ EXAMPLES::
336
+
337
+ sage: S3.<s1, s2> = AssionGroupS(3)
338
+ sage: matrix(S3.an_element()) # needs sage.libs.pari
339
+ [2 1 1]
340
+ [1 0 0]
341
+ [0 1 0]
342
+ """
343
+ mat_grp = self.parent().as_matrix_group()
344
+ return mat_grp(self).matrix()
345
+
346
+ def braid(self):
347
+ r"""
348
+ Return the canonical braid preimage of ``self`` as a :class:`Braid`.
349
+
350
+ EXAMPLES::
351
+
352
+ sage: C3.<c1, c2> = CubicBraidGroup(3)
353
+ sage: c1.parent()
354
+ Cubic Braid group on 3 strands
355
+ sage: c1.braid().parent()
356
+ Braid group on 3 strands
357
+ """
358
+ braid_group = self.parent().braid_group()
359
+ return braid_group(self)
360
+
361
+ @cached_method
362
+ def burau_matrix(self, root_bur=None, domain=None, characteristic=None,
363
+ var='t', reduced=False):
364
+ r"""
365
+ Return the Burau matrix of the cubic braid coset.
366
+
367
+ This method uses the same method belonging to :class:`Braid`, but
368
+ reduces the indeterminate to a primitive sixth (resp. twelfth in case
369
+ ``reduced='unitary'``) root of unity.
370
+
371
+ INPUT (all arguments are optional keywords):
372
+
373
+ - ``root_bur`` -- six (resp. twelfth) root of unity in some field
374
+ (default: root of unity over `\QQ`)
375
+ - ``domain`` -- (default: cyclotomic field of order 3 and degree 2, resp.
376
+ the domain of ``root_bur`` if given) base ring for the Burau matrix
377
+ - ``characteristic`` -- integer giving the characteristic of the
378
+ domain (default: 0 or the characteristic of ``domain`` if given)
379
+ - ``var`` -- string used for the indeterminate name in case ``root_bur``
380
+ must be constructed in a splitting field
381
+ - ``reduced`` -- boolean or string (default: ``False``); for more
382
+ information see the documentation of :meth:`burau_matrix` of
383
+ :class:`Braid`
384
+
385
+ OUTPUT:
386
+
387
+ The Burau matrix of the cubic braid coset with entries in the
388
+ domain given by the options. In case the option ``reduced='unitary'``
389
+ is given a triple consisting of the Burau matrix, its adjoined and
390
+ the Hermitian form is returned.
391
+
392
+ EXAMPLES::
393
+
394
+ sage: C3.<c1, c2> = CubicBraidGroup(3)
395
+ sage: ele = c1*c2*c1
396
+
397
+ sage: # needs sage.rings.number_field
398
+ sage: BuMa = ele.burau_matrix(); BuMa
399
+ [ -zeta3 1 zeta3]
400
+ [ -zeta3 zeta3 + 1 0]
401
+ [ 1 0 0]
402
+ sage: BuMa.base_ring()
403
+ Cyclotomic Field of order 3 and degree 2
404
+ sage: BuMa == ele.burau_matrix(characteristic=0)
405
+ True
406
+ sage: BuMa = ele.burau_matrix(domain=QQ); BuMa
407
+ [-t + 1 1 t - 1]
408
+ [-t + 1 t 0]
409
+ [ 1 0 0]
410
+ sage: BuMa.base_ring()
411
+ Number Field in t with defining polynomial t^2 - t + 1
412
+ sage: BuMa = ele.burau_matrix(domain = QQ[I, sqrt(3)]); BuMa # needs sage.symbolic
413
+ [ 1/2*sqrt3*I + 1/2 1 -1/2*sqrt3*I - 1/2]
414
+ [ 1/2*sqrt3*I + 1/2 -1/2*sqrt3*I + 1/2 0]
415
+ [ 1 0 0]
416
+ sage: BuMa.base_ring() # needs sage.symbolic
417
+ Number Field in I with defining polynomial x^2 + 1 over its base field
418
+
419
+ sage: # needs sage.libs.pari
420
+ sage: BuMa = ele.burau_matrix(characteristic=7); BuMa
421
+ [3 1 4]
422
+ [3 5 0]
423
+ [1 0 0]
424
+ sage: BuMa.base_ring()
425
+ Finite Field of size 7
426
+
427
+ sage: # needs sage.rings.finite_rings
428
+ sage: BuMa = ele.burau_matrix(characteristic=2); BuMa
429
+ [t + 1 1 t + 1]
430
+ [t + 1 t 0]
431
+ [ 1 0 0]
432
+ sage: BuMa.base_ring()
433
+ Finite Field in t of size 2^2
434
+ sage: F4.<r64> = GF(4)
435
+ sage: BuMa = ele.burau_matrix(root_bur=r64); BuMa
436
+ [r64 + 1 1 r64 + 1]
437
+ [r64 + 1 r64 0]
438
+ [ 1 0 0]
439
+ sage: BuMa.base_ring()
440
+ Finite Field in r64 of size 2^2
441
+ sage: BuMa = ele.burau_matrix(domain=GF(5)); BuMa
442
+ [2*t + 2 1 3*t + 3]
443
+ [2*t + 2 3*t + 4 0]
444
+ [ 1 0 0]
445
+ sage: BuMa.base_ring()
446
+ Finite Field in t of size 5^2
447
+
448
+ sage: # needs sage.rings.number_field
449
+ sage: BuMa, BuMaAd, H = ele.burau_matrix(reduced='unitary'); BuMa
450
+ [ 0 zeta12^3]
451
+ [zeta12^3 0]
452
+ sage: BuMa * H * BuMaAd == H
453
+ True
454
+ sage: BuMa.base_ring()
455
+ Cyclotomic Field of order 12 and degree 4
456
+ sage: BuMa, BuMaAd, H = ele.burau_matrix(domain=QQ[I, sqrt(3)], # needs sage.symbolic
457
+ ....: reduced='unitary'); BuMa
458
+ [0 I]
459
+ [I 0]
460
+ sage: BuMa.base_ring() # needs sage.symbolic
461
+ Number Field in I with defining polynomial x^2 + 1 over its base field
462
+ """
463
+ braid = self.braid()
464
+
465
+ from sage.misc.functional import cyclotomic_polynomial
466
+ min_pol_root_bur = cyclotomic_polynomial(6, var=var)
467
+ unitary = False
468
+ if isinstance(reduced, str):
469
+ if reduced == 'unitary':
470
+ unitary = True
471
+ min_pol_root_bur = cyclotomic_polynomial(12, var=var)
472
+
473
+ burau_ori = braid.burau_matrix(reduced=reduced)
474
+
475
+ if unitary:
476
+ burau_ori, burau_ori_adj, herm_form_ori = burau_ori
477
+
478
+ if domain is not None:
479
+ if isinstance(domain, sage.rings.abc.UniversalCyclotomicField):
480
+ if root_bur is None:
481
+ if unitary:
482
+ root_bur = domain.gen(12)
483
+ else:
484
+ root_bur = domain.gen(6)
485
+
486
+ if root_bur is None:
487
+ def find_root(domain):
488
+ min_pol = min_pol_root_bur.change_ring(domain)
489
+ root_list = min_pol.roots()
490
+ if not root_list:
491
+ domain = min_pol.splitting_field(min_pol_root_bur.variable_name())
492
+ min_pol = min_pol_root_bur.change_ring(domain)
493
+ root_list = min_pol.roots()
494
+ for root in root_list:
495
+ if root[0] == 0:
496
+ continue
497
+ root_bur = root[0]
498
+ if root[1] == 1:
499
+ break
500
+ return root_bur
501
+
502
+ if domain is None:
503
+ if characteristic is None:
504
+ # --------------------------------------------------------------------
505
+ # setting the default characteristic in order to achieve the according
506
+ # representations being well defined
507
+ # --------------------------------------------------------------------
508
+ cbg_type = self.parent()._cbg_type
509
+ if cbg_type == CubicBraidGroup.type.AssionS:
510
+ characteristic = 3 # making Assion type S relations vanish
511
+ elif cbg_type == CubicBraidGroup.type.AssionU:
512
+ characteristic = 2 # making Assion type U relations vanish
513
+ else:
514
+ characteristic = 0
515
+ try:
516
+ characteristic = Integer(characteristic)
517
+ except ValueError:
518
+ raise ValueError('characteristic must be in integer')
519
+
520
+ if not characteristic.is_zero() and not characteristic.is_prime():
521
+ raise ValueError('characteristic must be a prime')
522
+ if characteristic.is_zero():
523
+ from sage.rings.number_field.number_field import CyclotomicField
524
+ if unitary:
525
+ domain = CyclotomicField(12)
526
+ else:
527
+ domain = CyclotomicField(3)
528
+ else:
529
+ from sage.rings.finite_rings.finite_field_constructor import GF
530
+ domain = GF(characteristic)
531
+ root_bur = find_root(domain)
532
+ domain = root_bur.parent()
533
+
534
+ else: # domain is not None
535
+ root_bur = find_root(domain)
536
+
537
+ else: # root_bur is not None
538
+ if domain is None:
539
+ domain = root_bur.parent()
540
+
541
+ if 1 not in domain:
542
+ raise ValueError('root_bur must belong to a domain containing 1')
543
+
544
+ min_pol_root_bur = min_pol_root_bur.change_ring(domain)
545
+ if not min_pol_root_bur(root_bur).is_zero():
546
+ raise ValueError('root_bur must vanish on %s' % (min_pol_root_bur))
547
+
548
+ def conv2domain(laur_pol):
549
+ l1, l2 = laur_pol.polynomial_construction()
550
+ p1 = l1.change_ring(domain)
551
+ p2 = root_bur**(l2)
552
+ res = p1(root_bur) * p2
553
+ return res
554
+
555
+ from sage.matrix.constructor import matrix
556
+
557
+ d1, d2 = burau_ori.dimensions()
558
+ burau_mat = matrix(d1, d2, lambda i, j: conv2domain(burau_ori[i, j]))
559
+
560
+ if unitary:
561
+ burau_mat_adj = matrix(d1, d2, lambda i, j: conv2domain(burau_ori_adj[i, j]))
562
+ herm_form = matrix(d1, d2, lambda i, j: conv2domain(herm_form_ori[i, j]))
563
+ return burau_mat, burau_mat_adj, herm_form
564
+
565
+ return burau_mat
566
+
567
+
568
+ ##############################################################################
569
+ #
570
+ # Class CubicBraidGroup
571
+ #
572
+ ##############################################################################
573
+ class CubicBraidGroup(UniqueRepresentation, FinitelyPresentedGroup):
574
+ r"""
575
+ Factor groups of the Artin braid group mapping their generators to elements
576
+ of order 3.
577
+
578
+ These groups are implemented as a particular case of finitely presented
579
+ groups similar to the :class:`BraidGroup_class`.
580
+
581
+ A cubic braid group can be created by giving the number of strands, and
582
+ the name of the generators in a similar way as it works for the
583
+ :class:`BraidGroup_class`.
584
+
585
+ INPUT:
586
+
587
+ - ``names`` -- see the corresponding documentation of :class:`BraidGroup_class`
588
+ - ``cbg_type`` -- (default: ``CubicBraidGroup.type.Coxeter``;
589
+ see explanation below) enum type :class:`CubicBraidGroup.type`
590
+
591
+ Setting the keyword ``cbg_type`` to one on the values
592
+ ``CubicBraidGroup.type.AssionS`` or ``CubicBraidGroup.type.AssionU``,
593
+ the additional relations due to Assion are added:
594
+
595
+ .. MATH::
596
+
597
+ \begin{array}{lll}
598
+ \mbox{S:} & s_3 s_1 t_2 s_1 t_2^{-1} t_3 t_2 s_1 t_2^{-1} t_3^{-1} = 1
599
+ & \mbox{ for } m >= 5 \mbox{ in case } S(m), \\
600
+ \mbox{U:} & t_1 t_3 = 1
601
+ & \mbox{ for } m >= 5 \mbox{ in case } U(m),
602
+ \end{array}
603
+
604
+ where `t_i = (s_i s_{i+1})^3`. If ``cbg_type == CubicBraidGroup.type.Coxeter``
605
+ (default) only the cubic relation on the generators is active (Coxeter's
606
+ case of investigation). Note that for `n = 2, 3, 4`, the groups do not
607
+ differ between the three possible values of ``cbg_type`` (as finitely
608
+ presented groups). However, the ``CubicBraidGroup.type.Coxeter``,
609
+ ``CubicBraidGroup.type.AssionS`` and ``CubicBraidGroup.type.AssionU``
610
+ are different, so they have different classical realizations implemented.
611
+
612
+ .. SEEALSO::
613
+
614
+ Instances can also be constructed more easily by using
615
+ :func:`CubicBraidGroup`, :func:`AssionGroupS` and :func:`AssionGroupU`.
616
+
617
+ EXAMPLES::
618
+
619
+ sage: U3 = CubicBraidGroup(3, cbg_type=CubicBraidGroup.type.AssionU); U3
620
+ Assion group on 3 strands of type U
621
+ sage: U3.gens()
622
+ (c0, c1)
623
+
624
+ Alternative possibilities defining ``U3``::
625
+
626
+ sage: U3 = AssionGroupU(3); U3
627
+ Assion group on 3 strands of type U
628
+ sage: U3.gens()
629
+ (u0, u1)
630
+ sage: U3.<u1,u2> = AssionGroupU(3); U3
631
+ Assion group on 3 strands of type U
632
+ sage: U3.gens()
633
+ (u1, u2)
634
+
635
+ Alternates naming the generators::
636
+
637
+ sage: U3 = AssionGroupU(3, 'a, b'); U3
638
+ Assion group on 3 strands of type U
639
+ sage: U3.gens()
640
+ (a, b)
641
+ sage: C3 = CubicBraidGroup(3, 't'); C3
642
+ Cubic Braid group on 3 strands
643
+ sage: C3.gens()
644
+ (t0, t1)
645
+ sage: U3.is_isomorphic(C3)
646
+ True
647
+ sage: U3.as_classical_group() # needs sage.rings.number_field
648
+ Subgroup generated by [(1,7,6)(3,19,14)(4,15,10)(5,11,18)(12,16,20),
649
+ (1,12,13)(2,15,19)(4,9,14)(5,18,8)(6,21,16)]
650
+ of (The projective general unitary group of degree 3 over Finite Field of size 2)
651
+ sage: C3.as_classical_group() # needs sage.rings.number_field
652
+ Subgroup with 2 generators (
653
+ [ E(3)^2 0] [ 1 -E(12)^7]
654
+ [-E(12)^7 1], [ 0 E(3)^2]
655
+ ) of General Unitary Group of degree 2 over Universal Cyclotomic Field
656
+ with respect to positive definite hermitian form
657
+ [-E(12)^7 + E(12)^11 -1]
658
+ [ -1 -E(12)^7 + E(12)^11]
659
+
660
+ REFERENCES:
661
+
662
+ - [Cox1957]_
663
+ - [Ass1978]_
664
+ """
665
+
666
+ Element = CubicBraidElement
667
+
668
+ ##############################################################################
669
+ # Enum for the type of the group
670
+ ##############################################################################
671
+
672
+ class type(Enum):
673
+ r"""
674
+ Enum class to select the type of the group:
675
+
676
+ - ``Coxeter`` -- ``'C'`` the full cubic braid group
677
+ - ``AssionS`` -- ``'S'`` finite factor group of type S considered by Assion
678
+ - ``AssionU`` -- ``'U'`` finite factor group of type U considered by Assion
679
+
680
+ EXAMPLES::
681
+
682
+ sage: S2 = CubicBraidGroup(2, cbg_type=CubicBraidGroup.type.AssionS); S2
683
+ Assion group on 2 strands of type S
684
+ sage: U3 = CubicBraidGroup(2, cbg_type='U')
685
+ Traceback (most recent call last):
686
+ ...
687
+ TypeError: the cbg_type must be an instance of <enum 'CubicBraidGroup.type'>
688
+ """
689
+ Coxeter = 'C'
690
+ AssionS = 'S'
691
+ AssionU = 'U'
692
+
693
+ ###########################################################################################
694
+ # private methods
695
+ ###########################################################################################
696
+ @staticmethod
697
+ def __classcall_private__(cls, n=None, names='c', cbg_type=None):
698
+ r"""
699
+ Normalize input to ensure a unique representation.
700
+
701
+ EXAMPLES::
702
+
703
+ sage: C3 = CubicBraidGroup(3); C3.generators()
704
+ (c0, c1)
705
+ sage: CubicBraidGroup(3, 'g').generators()
706
+ (g0, g1)
707
+ sage: U3.<u1,u2>=CubicBraidGroup(3, cbg_type=CubicBraidGroup.type.AssionU); U3.generators()
708
+ (u1, u2)
709
+ """
710
+ # this code is adapted from :func:`BraidGroup`
711
+ # Support Freegroup('a,b') syntax
712
+ if n is not None:
713
+ try:
714
+ n = Integer(n)-1
715
+ except TypeError:
716
+ names = n
717
+
718
+ n = None
719
+ # derive n from counting names
720
+ if n is None:
721
+ if isinstance(names, str):
722
+ n = len(names.split(','))
723
+ else:
724
+ names = list(names)
725
+ n = len(names)
726
+
727
+ from sage.structure.category_object import normalize_names
728
+ names = tuple(normalize_names(n, names))
729
+ return super().__classcall__(cls, names, cbg_type=cbg_type)
730
+
731
+ def __init__(self, names, cbg_type=None):
732
+ r"""
733
+ Python constructor.
734
+
735
+ TESTS::
736
+
737
+ sage: # needs sage.rings.number_field
738
+ sage: C3 = CubicBraidGroup(3) # indirect doctest
739
+ sage: TestSuite(C3).run()
740
+ sage: C4 = CubicBraidGroup(4) # indirect doctest
741
+ sage: TestSuite(C4).run() # long time
742
+ sage: C6 = CubicBraidGroup(6) # indirect doctest
743
+ sage: TestSuite(C6).run() # long time
744
+ sage: S3 = AssionGroupS(3) # indirect doctest
745
+ sage: TestSuite(S3).run()
746
+ sage: S5 = AssionGroupS(5) # indirect doctest
747
+ sage: TestSuite(S5).run() # long time
748
+ sage: U3 = AssionGroupU(3) # indirect doctest
749
+ sage: TestSuite(U3).run()
750
+ sage: U4 = AssionGroupU(4) # indirect doctest
751
+ sage: TestSuite(U4).run() # long time
752
+ sage: U5 = AssionGroupU(5) # indirect doctest
753
+ sage: TestSuite(U5).run() # long time
754
+ """
755
+ n = Integer(len(names))
756
+ if n < 1:
757
+ raise ValueError("the number of strands must be an integer larger than one")
758
+ if cbg_type is None:
759
+ cbg_type = CubicBraidGroup.type.Coxeter
760
+ if not isinstance(cbg_type, CubicBraidGroup.type):
761
+ raise TypeError("the cbg_type must be an instance of %s" % CubicBraidGroup.type)
762
+
763
+ free_group = FreeGroup(names)
764
+ self._cbg_type = cbg_type
765
+ self._nstrands = n + 1
766
+ self._ident = self._cbg_type.value + self._nstrands.str()
767
+ self._braid_group = BraidGroup(names)
768
+
769
+ # internal naming of elements for convenience
770
+ b = [free_group([i]) for i in range(1, n+1)]
771
+ t = [free_group([i, i+1]) ** 3 for i in range(1, n)]
772
+ ti = [free_group([-i, -i-1]) ** 3 for i in range(1, n)]
773
+
774
+ # first the braid relations
775
+ rels = list(self._braid_group.relations())
776
+
777
+ # than the cubic relations
778
+ rels.extend(b[i]**3 for i in range(n))
779
+
780
+ # than Assion's relation Satz 2.2 for cbg_type=CubicBraidGroup.type.AssionS
781
+ # and Satz 2.4 for cbg_type=CubicBraidGroup.type.AssionU
782
+ if n > 3:
783
+ for i in range(n - 3):
784
+ if cbg_type == CubicBraidGroup.type.AssionU:
785
+ rels.append((t[i]*t[i+2])**3)
786
+ elif cbg_type == CubicBraidGroup.type.AssionS:
787
+ rels.append(b[i+2]*b[i]*t[i+1]*b[i]*ti[i+1]*t[i+2]*t[i+1]*b[i]*ti[i+1]*ti[i+2])
788
+
789
+ if cbg_type != CubicBraidGroup.type.Coxeter:
790
+ cat = Groups().Finite()
791
+ elif self._nstrands <= 5:
792
+ cat = ShephardGroups()
793
+ else:
794
+ cat = Groups().Infinite()
795
+ FinitelyPresentedGroup.__init__(self, free_group, tuple(rels), category=cat)
796
+ self._free_group = free_group
797
+
798
+ # ------------------------------------------------------------------------------------------------
799
+ # the following global pointers to classical group realizations will be set in the private method
800
+ # _create_classical_realization
801
+ # ------------------------------------------------------------------------------------------------
802
+ self._classical_group = None # This is the classical Group returned by as_classical_group
803
+ self._classical_base_group = None # this only differs for special cases for Assion groups from the former
804
+ self._classical_invariant_form = None # invariant form of the classical base group
805
+ self._classical_embedding = None # if self._classical_group different from self._classical_base_group
806
+ self._centralizing_matrix = None # for Assion groups: element in classical base group commuting with self
807
+ self._centralizing_element = None # image under nat. map of the former one in the proj. classical group
808
+
809
+ def _repr_(self):
810
+ r"""
811
+ Return a string representation.
812
+
813
+ EXAMPLES::
814
+
815
+ sage: CubicBraidGroup(2)
816
+ Cubic Braid group on 2 strands
817
+ sage: AssionGroupU(2)
818
+ Assion group on 2 strands of type U
819
+ """
820
+ if self._cbg_type == CubicBraidGroup.type.Coxeter:
821
+ return "Cubic Braid group on %s strands" % (self.strands())
822
+ else:
823
+ return "Assion group on %s strands of type %s" % (self.strands() ,self._cbg_type.value)
824
+
825
+ def index_set(self):
826
+ r"""
827
+ Return the index set of ``self``.
828
+
829
+ This is the set of integers `0,\dots,n-2` where `n` is
830
+ the number of strands.
831
+
832
+ This is only used when ``self`` is a finite reflection group.
833
+
834
+ EXAMPLES::
835
+
836
+ sage: CubicBraidGroup(3).index_set()
837
+ [0, 1]
838
+ """
839
+ return list(range(self.strands() - 1))
840
+
841
+ def simple_reflections(self):
842
+ """
843
+ Return the generators of ``self``.
844
+
845
+ This is only used when ``self`` is a finite reflection group.
846
+
847
+ EXAMPLES::
848
+
849
+ sage: CubicBraidGroup(3).simple_reflections()
850
+ (c0, c1)
851
+ """
852
+ return self.generators()
853
+
854
+ def degrees(self):
855
+ """
856
+ Return the degrees of ``self``.
857
+
858
+ This only makes sense when ``self`` is a finite reflection group.
859
+
860
+ EXAMPLES::
861
+
862
+ sage: CubicBraidGroup(4).degrees()
863
+ (6, 9, 12)
864
+ """
865
+ if self._cbg_type != CubicBraidGroup.type.Coxeter:
866
+ raise TypeError('not a finite reflection group')
867
+ if self.strands() > 5:
868
+ raise TypeError('not a finite reflection group')
869
+ d_table = {1: (), 2: (3,), 3: (4, 6),
870
+ 4: (6, 9, 12), 5: (12, 18, 24, 30)}
871
+ return tuple(Integer(deg) for deg in d_table[self.strands()])
872
+
873
+ def codegrees(self):
874
+ """
875
+ Return the codegrees of ``self``.
876
+
877
+ This only makes sense when ``self`` is a finite reflection group.
878
+
879
+ EXAMPLES::
880
+
881
+ sage: CubicBraidGroup(5).codegrees()
882
+ (0, 6, 12, 18)
883
+ """
884
+ if self._cbg_type != CubicBraidGroup.type.Coxeter:
885
+ raise TypeError('not a finite reflection group')
886
+ if self.strands() > 5:
887
+ raise TypeError('not a finite reflection group')
888
+ d_table = {1: (), 2: (0,), 3: (0, 2),
889
+ 4: (0, 3, 6), 5: (0, 6, 12, 18)}
890
+ return tuple(Integer(deg) for deg in d_table[self.strands()])
891
+
892
+ # -------------------------------------------------------------------------------
893
+ # Methods for test_suite
894
+ # -------------------------------------------------------------------------------
895
+
896
+ def _internal_test_attached_group(self, attached_group, tester):
897
+ r"""
898
+ Test conversion maps from ``self`` to the given attached Group,
899
+ which must have been defined using the :meth:`as_classical_group`,
900
+ :meth:`as_matrix_group`, :meth:`as_permutation_group` or
901
+ :meth:`as_reflection_group`.
902
+
903
+ INPUT:
904
+
905
+ - ``attached_group`` -- attached group to be tested as specified above
906
+
907
+ EXAMPLES::
908
+
909
+ sage: CBG2 = CubicBraidGroup(2)
910
+ sage: tester = CBG2._tester()
911
+ sage: CBG2M = CBG2.as_matrix_group() # needs sage.rings.number_field
912
+ sage: CBG2._internal_test_attached_group(CBG2M, tester) # needs sage.rings.number_field
913
+ """
914
+ elem = self.an_element()
915
+ att_grp_elem = attached_group(elem)
916
+ if self.is_finite() and self.strands() <= 7: # not realistic for larger number of strands
917
+ att_grp_elem_back = self(att_grp_elem)
918
+ tester.assertEqual(att_grp_elem_back, elem)
919
+
920
+ def _test_classical_group(self, **options):
921
+ r"""
922
+ Check the classical group properties.
923
+
924
+ The following is checked:
925
+
926
+ - Construction of classical group was faithful.
927
+ - Coercion maps to and from classical group exist and are
928
+ inverse to each other.
929
+
930
+ EXAMPLES::
931
+
932
+ sage: CBG2 = CubicBraidGroup(2)
933
+ sage: CBG2._test_classical_group() # needs sage.rings.number_field
934
+ """
935
+ tester = self._tester(**options)
936
+ classic_grp = self.as_classical_group()
937
+ if self.is_finite():
938
+ self._internal_test_attached_group(classic_grp, tester)
939
+
940
+ def _test_permutation_group(self, **options):
941
+ r"""
942
+ Check the permutation group properties.
943
+
944
+ The following is checked:
945
+
946
+ - Construction of permutation group was faithful.
947
+ - Coercion maps to and from permutation group exist and are
948
+ inverse to each other.
949
+
950
+ EXAMPLES::
951
+
952
+ sage: CBG2 = CubicBraidGroup(2)
953
+ sage: CBG2._test_permutation_group() # needs sage.rings.number_field
954
+ """
955
+ if self.is_finite():
956
+ tester = self._tester(**options)
957
+ permgrp = self.as_permutation_group()
958
+ self._internal_test_attached_group(permgrp, tester)
959
+
960
+ def _test_matrix_group(self, **options):
961
+ r"""
962
+ Check the matrix group properties.
963
+
964
+ The following is checked:
965
+
966
+ - Construction of matrix group was faithful.
967
+ - Coercion maps to and from matrix group exist and are
968
+ inverse to each other.
969
+
970
+ EXAMPLES::
971
+
972
+ sage: CBG2 = CubicBraidGroup(2)
973
+ sage: CBG2._test_matrix_group()
974
+ """
975
+ tester = self._tester(**options)
976
+
977
+ MatDEF = self.as_matrix_group()
978
+ self._internal_test_attached_group(MatDEF, tester)
979
+
980
+ try:
981
+ from sage.rings.finite_rings.finite_field_constructor import GF
982
+ except ImportError:
983
+ return
984
+
985
+ F3 = GF(3)
986
+ r63 = F3(2)
987
+ F4 = GF(4)
988
+ r64 = F4.gen()
989
+
990
+ if self._cbg_type != CubicBraidGroup.type.AssionU or self.strands() < 5: # not well defined else-wise
991
+ matrix_grpF3 = self.as_matrix_group(root_bur=r63)
992
+ self._internal_test_attached_group(matrix_grpF3, tester)
993
+
994
+ if self._cbg_type != CubicBraidGroup.type.AssionS or self.strands() < 5: # not well defined else-wise
995
+ matrix_grpF4 = self.as_matrix_group(root_bur=r64)
996
+ self._internal_test_attached_group(matrix_grpF4, tester)
997
+
998
+ if self.strands() < 5 or self._cbg_type == CubicBraidGroup.type.Coxeter:
999
+ matrix_grpF5 = self.as_matrix_group(characteristic=5)
1000
+ self._internal_test_attached_group(matrix_grpF5, tester)
1001
+
1002
+ matrix_grpF7 = self.as_matrix_group(domain=GF(7))
1003
+ self._internal_test_attached_group(matrix_grpF7, tester)
1004
+ return
1005
+
1006
+ def _test_reflection_group(self, **options):
1007
+ r"""
1008
+ Check the reflection group properties.
1009
+
1010
+ The following is checked:
1011
+
1012
+ - Construction of reflection group was faithful.
1013
+ - Coercion maps to and from reflection group exist and are
1014
+ inverse to each other.
1015
+
1016
+ EXAMPLES::
1017
+
1018
+ sage: CBG2 = CubicBraidGroup(2)
1019
+ sage: CBG2._test_reflection_group()
1020
+ """
1021
+ if self._cbg_type == CubicBraidGroup.type.Coxeter and self.is_finite() and self.strands() > 2:
1022
+ from sage.combinat.root_system.reflection_group_real import is_chevie_available
1023
+ if is_chevie_available():
1024
+ tester = self._tester(**options)
1025
+ reflgrp = self.as_reflection_group()
1026
+ self._internal_test_attached_group(reflgrp, tester)
1027
+
1028
+ # -------------------------------------------------------------------------------
1029
+ # -------------------------------------------------------------------------------
1030
+ # local utility-methods
1031
+ # -------------------------------------------------------------------------------
1032
+ # -------------------------------------------------------------------------------
1033
+ def _create_classical_realization(self, just_embedded=False):
1034
+ r"""
1035
+ Internal method to create the classical groups attached to ``self``.
1036
+
1037
+ This methods sets the following attributes of ``self``:
1038
+
1039
+ - self._classical_group This is the classical group returned by as_classical_group method.
1040
+ - self._classical_base_group this only differs in special cases for Assion groups from the former.
1041
+ - self._classical_invariant_form invariant form of the classical base group.
1042
+ - self._centralizing_matrix for Assion groups: element in classical base group commuting with ``self``.
1043
+ - self._centralizing_element image under natural map of the former one in the projective classical group.
1044
+ - self._classical_embedding as subgroup of classical base group (if different from classical group).
1045
+
1046
+ EXAMPLES::
1047
+
1048
+ sage: AU2 = AssionGroupU(2)
1049
+ sage: AU2._classical_group is None
1050
+ True
1051
+ sage: AU2._classical_embedding is None
1052
+ True
1053
+ sage: AU2._classical_invariant_form is None
1054
+ True
1055
+ sage: AU2._create_classical_realization()
1056
+ sage: AU2._classical_group
1057
+ General Unitary Group of degree 1 over Finite Field in a of size 2^2
1058
+ sage: AU2._classical_embedding is AU2._classical_group
1059
+ True
1060
+ sage: AU2._classical_invariant_form
1061
+ [1]
1062
+ """
1063
+
1064
+ # -------------------------------------------------------------------------------
1065
+ # Set up data of the classical Assion group (generic part)
1066
+ # -------------------------------------------------------------------------------
1067
+
1068
+ def set_classical_realization(self, base_group, proj_group, centralizing_matrix, transvec_matrices):
1069
+ r"""
1070
+ Internal method to create classical group for Assion groups.
1071
+
1072
+ This is a local function of :meth:`_create_classical_realization`.
1073
+
1074
+ It handles the common part of symplectic and unitary version and
1075
+ creates conversion maps.
1076
+
1077
+ INPUT:
1078
+
1079
+ - ``base_group`` -- the symplectic or unitary groups Sp(m,3) resp. GU(m,2)
1080
+ - ``proj_group`` -- the corresponding projective group of base_group
1081
+ - ``centralizing_matrix`` -- the centralizing matrix according to Assion
1082
+ - ``transvec_matrices`` -- list of transvection matrices according to Assion
1083
+
1084
+ OUTPUT: no output, but the function sets the attributes of ``self`` described above
1085
+ """
1086
+ centralizing_element = None
1087
+
1088
+ # ------------------------------------------------------------------------------
1089
+ # Setting the List of Braid Images
1090
+ # ------------------------------------------------------------------------------
1091
+ im_gens = [base_group(m) for m in transvec_matrices]
1092
+
1093
+ # ------------------------------------------------------------------------------
1094
+ # By the work of Assion no check on the group homomorphism is needed, at all.
1095
+ # But to take care of software bugs they are performed in cases where they are
1096
+ # not really expansive.
1097
+ # ------------------------------------------------------------------------------
1098
+ check = False
1099
+ if self.strands() < 7:
1100
+ check = True
1101
+
1102
+ # ------------------------------------------------------------------------------
1103
+ # Do the projective group realization if needed
1104
+ # ------------------------------------------------------------------------------
1105
+ embedding = self._classical_embedding
1106
+ classical_group = None
1107
+ if proj_group is None:
1108
+ classical_group = base_group
1109
+ hom_to_classic = self.hom(im_gens, check=check)
1110
+ classical_group.register_conversion(hom_to_classic)
1111
+ embedding = classical_group
1112
+ else:
1113
+ if embedding is None:
1114
+ im_gens.pop()
1115
+ embedding = base_group.subgroup(im_gens, check=check)
1116
+ embedding.register_conversion(self.hom(embedding.gens(), check=check))
1117
+ hom_to_base = self.hom(im_gens, check=check, codomain=base_group)
1118
+ base_group.register_conversion(hom_to_base)
1119
+ if not just_embedded:
1120
+ transvec_matrices.pop()
1121
+ nat_hom = base_group.hom(proj_group.gens(), check=check)
1122
+ centralizing_element = nat_hom(centralizing_matrix)
1123
+ classical_group_gens = [nat_hom(m) for m in transvec_matrices]
1124
+ classical_group = proj_group.subgroup(classical_group_gens, canonicalize=False)
1125
+ hom_to_classic = self.hom(classical_group.gens(), check=check)
1126
+ classical_group.register_conversion(hom_to_classic)
1127
+
1128
+ # ------------------------------------------------------------------------------
1129
+ # register constructed items
1130
+ # ------------------------------------------------------------------------------
1131
+ self._classical_group = classical_group
1132
+ self._classical_base_group = base_group
1133
+ self._classical_invariant_form = base_group.invariant_form()
1134
+ self._centralizing_matrix = centralizing_matrix
1135
+ self._centralizing_element = centralizing_element
1136
+ self._classical_embedding = embedding
1137
+
1138
+ # -------------------------------------------------------------------------------
1139
+ # local methods to set up the classical group (specific part)
1140
+ # -------------------------------------------------------------------------------
1141
+ # Case for symplectic groups
1142
+ # -------------------------------------------------------------------------------
1143
+
1144
+ def create_sympl_realization(self, m):
1145
+ r"""
1146
+ Internal method to create classical group for symplectic
1147
+ Assion groups (`cbg_type == CubicBraidGroup.type.AssionS`).
1148
+
1149
+ INPUT:
1150
+
1151
+ - ``m`` -- integer; the dimension of the classical groups
1152
+ vector-space of operation
1153
+
1154
+ The function calculates the centralizing matrix and the
1155
+ transvections as given by Assion and then uses
1156
+ ``set_classical_realization`` to complete the construction.
1157
+ """
1158
+ # -----------------------------------------------------------
1159
+ # getting the invariant bilinear form of the group
1160
+ # and setting constants.
1161
+ # -----------------------------------------------------------
1162
+ n = self.strands()
1163
+
1164
+ from sage.groups.matrix_gps.symplectic import Sp
1165
+ base_group = Sp(m, 3)
1166
+ proj_group = None
1167
+ if m == n:
1168
+ from sage.groups.perm_gps.permgroup_named import PSp
1169
+ proj_group = PSp(m, 3)
1170
+
1171
+ bform = base_group.invariant_form()
1172
+ bas = bform.column_space().basis()
1173
+
1174
+ mhalf = m // 2
1175
+
1176
+ # -----------------------------------------------------------
1177
+ # computing a hyperbolic decomposition basis with respect
1178
+ # to the invariant bilinear form.
1179
+ # -----------------------------------------------------------
1180
+ xbas = [bas[mhalf - i - 1] for i in range(mhalf)]
1181
+ ybas = [bas[mhalf + i] for i in range(mhalf)]
1182
+
1183
+ # -----------------------------------------------------------
1184
+ # computing the List of transvection vectors according to
1185
+ # the Assion paper, page 292.
1186
+ # -----------------------------------------------------------
1187
+ transvections = [xbas[0]] # t_1 = x_1
1188
+ for i in range(mhalf-1):
1189
+ transvections.append(ybas[i]) # t_{2i} = y_i
1190
+ transvections.append(xbas[i] + xbas[i+1]) # t_{2i+1} = x_j + x_(j+1)
1191
+ transvections.append(ybas[mhalf-1]) # t_n = y_m
1192
+
1193
+ # -----------------------------------------------------------
1194
+ # Conversion-Map from transvection vector to transvection
1195
+ # matrix.
1196
+ # -----------------------------------------------------------
1197
+ from sage.matrix.constructor import matrix
1198
+
1199
+ def transvec2mat(v, bas=bas, bform=bform, fact=1):
1200
+ t = [x + fact*(x * bform * v) * v for x in bas]
1201
+ return matrix(bform.base_ring(), t)
1202
+
1203
+ # ------------------------------------------------------------------------------
1204
+ # setting the centralizing matrix for the case of projective group realization
1205
+ # ------------------------------------------------------------------------------
1206
+ centralizing_vector = xbas[mhalf-1]
1207
+ centralizing_matrix = base_group(transvec2mat(centralizing_vector, fact=1))
1208
+ transvec_matrices = [transvec2mat(v) for v in transvections]
1209
+
1210
+ set_classical_realization(self, base_group, proj_group, centralizing_matrix, transvec_matrices)
1211
+
1212
+ # -------------------------------------------------------------------------------
1213
+ # Case for unitary groups
1214
+ # -------------------------------------------------------------------------------
1215
+
1216
+ def create_unitary_realization(self, m):
1217
+ """
1218
+ Internal method to create classical group for
1219
+ unitary Assion groups (`cbg_type == CubicBraidGroup.type.AssionU`).
1220
+
1221
+ INPUT:
1222
+
1223
+ - ``m`` -- integer; the dimension of the classical groups
1224
+ vector-space of operation
1225
+
1226
+ The function calculates the centralizing_matrix and the
1227
+ transvections as given by Assion and then uses
1228
+ ``set_classical_realization`` to complete the construction.
1229
+ """
1230
+ # ---------------------------------------------------------------------
1231
+ # getting the invariant bilinear form of the group
1232
+ # and setting constants
1233
+ # ---------------------------------------------------------------------
1234
+ n = self.strands()
1235
+
1236
+ from sage.groups.matrix_gps.unitary import GU
1237
+ base_group = GU(m, 2)
1238
+ proj_group = None
1239
+ if m == n:
1240
+ from sage.groups.perm_gps.permgroup_named import PGU
1241
+ proj_group = PGU(m, 2)
1242
+
1243
+ bform = base_group.invariant_form()
1244
+ bas = bform.column_space().basis()
1245
+ F = bform.base_ring()
1246
+ a = F.gen()
1247
+
1248
+ mthird = m // 3
1249
+
1250
+ # -----------------------------------------------------------
1251
+ # computing a orthonormal basis with respect
1252
+ # to the invariant bilinear form.
1253
+ # -----------------------------------------------------------
1254
+ xbas = []
1255
+ for i in range(m):
1256
+ if 2*i == m-1:
1257
+ xbas.append(bas[i])
1258
+ else:
1259
+ xbas.append(a*bas[i] + a.frobenius()*bas[m-1 - i])
1260
+
1261
+ # -----------------------------------------------------------
1262
+ # computing the List of transvection vectors according to
1263
+ # Assion paper, page 293.
1264
+ # -----------------------------------------------------------
1265
+ transvections = [xbas[0]] # t_1 = x_1
1266
+ if m > 1:
1267
+ transvections.append(xbas[0]+xbas[1]+xbas[2]) # t_2 = x_1 + x_2 + x_3
1268
+ for j in range(mthird):
1269
+ pos = 3*(j+1)-1
1270
+ transvections.append(xbas[pos-1]) # t_{3i} = x_{3i-1}
1271
+ if pos + 1 < m:
1272
+ transvections.append(xbas[pos-1]+xbas[pos]+xbas[pos+1]) # t_{3i+1} = x_{3i-1} + x_{3i} + x_{3i+1}
1273
+ if pos + 3 < m:
1274
+ transvections.append(xbas[pos+1]+xbas[pos+2]+xbas[pos+3]) # t_{3i+2} = x_{3i+1} + x_{3i+2} + x_{3i+3}
1275
+
1276
+ # -----------------------------------------------------------
1277
+ # Conversion-Map from transvection vector to transvection
1278
+ # matrix.
1279
+ # -----------------------------------------------------------
1280
+ from sage.matrix.constructor import matrix
1281
+
1282
+ def transvec2mat(v, bas=bas, bform=bform, fact=a):
1283
+ # note x does not change under conjugation, since it belongs to standard basis
1284
+ t = [x + fact * (x * bform * v.conjugate()) * v for x in bas]
1285
+ return matrix(F, t)
1286
+
1287
+ # ------------------------------------------------------------------------------
1288
+ # setting the centralizing matrix for the case of projective group realization.
1289
+ # ------------------------------------------------------------------------------
1290
+ centralizing_vector = xbas[m-2]+xbas[m-1]
1291
+ centralizing_matrix = base_group(transvec2mat(centralizing_vector, fact=1))
1292
+ transvec_matrices = [transvec2mat(v) for v in transvections]
1293
+
1294
+ set_classical_realization(self, base_group, proj_group, centralizing_matrix, transvec_matrices)
1295
+
1296
+ # ----------------------------------------------------------------
1297
+ # local functions declaration section finishes here
1298
+ # ----------------------------------------------------------------
1299
+
1300
+ # ----------------------------------------------------------------
1301
+ # initialization of constants
1302
+ # ----------------------------------------------------------------
1303
+
1304
+ n = self.strands()
1305
+
1306
+ # -------------------------------------------------------------------------------
1307
+ # Setting the Classical group
1308
+ # -------------------------------------------------------------------------------
1309
+ if self._cbg_type == CubicBraidGroup.type.AssionS:
1310
+ dim_sympl_group = n-1 # S(n-1) = Sp(n-1, 3)
1311
+ if n % 2 == 0:
1312
+ dim_sympl_group = n # S(n-1) = subgroup of PSp(n, 3)
1313
+ create_sympl_realization(self, dim_sympl_group)
1314
+ elif self._cbg_type == CubicBraidGroup.type.AssionU:
1315
+ dim_unitary_group = n-1 # U(n-1) = GU(n-1, 2)
1316
+ if n % 3 == 0:
1317
+ dim_unitary_group = n # U(n-1) = subgroup PGU(n, 3)
1318
+ create_unitary_realization(self, dim_unitary_group)
1319
+ else:
1320
+ # -----------------------------------------------------------------------------------------------
1321
+ # connection between Coxeter realization and unitary Burau representation according to Squier:
1322
+ # -----------------------------------------------------------------------------------------------
1323
+ # Notation of Coxeter: p = 3, \theta =\pi/3 = primitive 6th root of unity
1324
+ # i\theta = \pi/3+\pi/2 = 5\pi/6 = 5th power of primitive 12th root of unity
1325
+ # i\theta = z12^5 = - ~z12 where z12 = UCF.gen(12)
1326
+ # Let f be the unitary Form of Coxeter and J(s) the one of Squier. Then we have
1327
+ # J(z12) = 2 f
1328
+ # Let `buc` be the unitary Burau Matrix of Coxeter for the first braid generator and `bus`
1329
+ # the corresponding one according to Squier. Then we have:
1330
+ # buc_[i,i-1] = buc_[i,i+1]= - i\theta, buc_[i,i] = 1 + 2*cos(\pi/6)*i\theta
1331
+ # bus_[i,i-1] = bus_[i,i+1]= s, bus_[i,i] = -s^2
1332
+ # now 1 + 2*cos(\pi/6)*i\theta = 1 + sqrt(3)*(-sqrt(3)/2 + I/2) = 1- 3/2 + sqrt(3)I/2 = z12^4 = - ~z12^2
1333
+ # finally: Coxeter's Realization is the unitary Burau representation of Squier for s = ~z12
1334
+ # -----------------------------------------------------------------------------------------------
1335
+ from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField
1336
+
1337
+ UCF = UniversalCyclotomicField()
1338
+ z12 = UCF.gen(12)
1339
+ classical_group = self.as_matrix_group(root_bur=~z12, domain=UCF, reduced='unitary')
1340
+ self._classical_group = classical_group
1341
+ self._classical_base_group = classical_group
1342
+ self._classical_embedding = classical_group
1343
+ if self._classical_invariant_form is None:
1344
+ self._classical_invariant_form = classical_group.ambient().invariant_form()
1345
+
1346
+ def _element_constructor_(self, x, **kwds):
1347
+ r"""
1348
+ Return an element of ``self``.
1349
+
1350
+ Extensions to the element constructor of :class:`FinitelyPresentedGroup`:
1351
+ new functionalities are:
1352
+
1353
+ - constructing element from an element of the attached classical group
1354
+ (embedded and not embedded)
1355
+ - constructing element from an element of the attached permutation group
1356
+ - constructing element from an element of the attached reflection group
1357
+
1358
+ INPUT:
1359
+
1360
+ - ``x`` -- can be one of the following:
1361
+
1362
+ * an instance of the element class of ``self`` (but possible
1363
+ to a different parent)
1364
+ * an instance of the element class of the braid group
1365
+ * a tuple representing a braid in Tietze form
1366
+ * an instance of an element class of a parent ``P`` such that there
1367
+ is a map from ``self`` to ``P`` having :meth:`lift`; for example,
1368
+ an element of an alternative realization of ``self``, such as
1369
+ the classical realization
1370
+ * any other object which works for the element constructor
1371
+ of :class:`FinitelyPresentedGroup`
1372
+
1373
+ EXAMPLES::
1374
+
1375
+ sage: S3 = AssionGroupS(3)
1376
+ sage: S3Cl = S3.as_classical_group()
1377
+ sage: g = mul(S3Cl.gens())
1378
+ sage: S3(g) # indirect doctest
1379
+ s0*s1*s0^-1
1380
+ """
1381
+ if hasattr(x, 'parent'):
1382
+ parent = x.parent()
1383
+ map_to = parent.convert_map_from(self)
1384
+ if map_to is not None:
1385
+ if hasattr(map_to, 'lift'):
1386
+ return map_to.lift(x)
1387
+ return super()._element_constructor_(x)
1388
+
1389
+ #######################################################################################################################
1390
+ # ----------------------------------------------------------------------------------
1391
+ # public methods
1392
+ # ----------------------------------------------------------------------------------
1393
+ #######################################################################################################################
1394
+
1395
+ # ---------------------------------------------------------------------------------------------------------------------
1396
+ # strands
1397
+ # ---------------------------------------------------------------------------------------------------------------------
1398
+ def strands(self):
1399
+ r"""
1400
+ Return the number of strands of the braid group whose image is ``self``.
1401
+
1402
+ OUTPUT: :class:`Integer`
1403
+
1404
+ EXAMPLES::
1405
+
1406
+ sage: C4 = CubicBraidGroup(4)
1407
+ sage: C4.strands()
1408
+ 4
1409
+ """
1410
+ return self._nstrands
1411
+
1412
+ # ----------------------------------------------------------------------------------
1413
+ # braid_group
1414
+ # ----------------------------------------------------------------------------------
1415
+ def braid_group(self):
1416
+ r"""
1417
+ Return a :class:`BraidGroup` with identical generators, such that
1418
+ there exists an epimorphism to ``self``.
1419
+
1420
+ OUTPUT: a :class:`BraidGroup` having conversion maps to and from
1421
+ ``self`` (which is just a section in the latter case)
1422
+
1423
+ EXAMPLES::
1424
+
1425
+ sage: U5 = AssionGroupU(5); U5
1426
+ Assion group on 5 strands of type U
1427
+ sage: B5 = U5.braid_group(); B5
1428
+ Braid group on 5 strands
1429
+ sage: b = B5([4,3,2,-4,1])
1430
+ sage: u = U5([4,3,2,-4,1])
1431
+ sage: u == b
1432
+ False
1433
+ sage: b.burau_matrix()
1434
+ [ 1 - t t 0 0 0]
1435
+ [ 1 - t 0 t 0 0]
1436
+ [ 1 - t 0 0 0 t]
1437
+ [ 1 - t 0 0 1 -1 + t]
1438
+ [ 1 0 0 0 0]
1439
+ sage: u.burau_matrix()
1440
+ [t + 1 t 0 0 0]
1441
+ [t + 1 0 t 0 0]
1442
+ [t + 1 0 0 0 t]
1443
+ [t + 1 0 0 1 t + 1]
1444
+ [ 1 0 0 0 0]
1445
+ sage: bU = U5(b)
1446
+ sage: uB = B5(u)
1447
+ sage: bU == u
1448
+ True
1449
+ sage: uB == b
1450
+ True
1451
+ """
1452
+ return self._braid_group
1453
+
1454
+ # ----------------------------------------------------------------------------------
1455
+ # as_matrix_group
1456
+ # ----------------------------------------------------------------------------------
1457
+ @cached_method
1458
+ def as_matrix_group(self, root_bur=None, domain=None, characteristic=None, var='t', reduced=False):
1459
+ r"""
1460
+ Create an epimorphic image of ``self`` as a matrix group by use of
1461
+ the burau representation.
1462
+
1463
+ INPUT:
1464
+
1465
+ - ``root_bur`` -- (default: root of unity over `\QQ`) six (resp. twelfth)
1466
+ root of unity in some field
1467
+ - ``domain`` -- (default: cyclotomic field of order 3 and degree 2, resp.
1468
+ the domain of ``root_bur`` if given) base ring for the Burau matrix
1469
+ - ``characteristic`` -- integer (optional); the characteristic of the
1470
+ domain; if none of the keywords ``root_bur``, ``domain`` and
1471
+ ``characteristic`` are given, the default characteristic is 3
1472
+ (resp. 2) if ``self`` is of ``cbg_type``
1473
+ ``CubicBraidGroup.type.AssionS`` (resp. ``CubicBraidGroup.type.AssionU``)
1474
+ - ``var`` -- string used for the indeterminate name in case ``root_bur``
1475
+ must be constructed in a splitting field
1476
+ - ``reduced`` -- boolean (default: ``False``); for more information
1477
+ see the documentation of :meth:`Braid.burau_matrix`
1478
+
1479
+ EXAMPLES::
1480
+
1481
+ sage: # needs sage.rings.finite_rings
1482
+ sage: C5 = CubicBraidGroup(5)
1483
+ sage: C5Mch5 = C5.as_matrix_group(characteristic=5); C5Mch5
1484
+ Matrix group over Finite Field in t of size 5^2 with 4 generators (
1485
+ [2*t + 2 3*t + 4 0 0 0]
1486
+ [ 1 0 0 0 0]
1487
+ [ 0 0 1 0 0]
1488
+ [ 0 0 0 1 0]
1489
+ [ 0 0 0 0 1],
1490
+ <BLANKLINE>
1491
+ [ 1 0 0 0 0]
1492
+ [ 0 2*t + 2 3*t + 4 0 0]
1493
+ [ 0 1 0 0 0]
1494
+ [ 0 0 0 1 0]
1495
+ [ 0 0 0 0 1],
1496
+ <BLANKLINE>
1497
+ [ 1 0 0 0 0]
1498
+ [ 0 1 0 0 0]
1499
+ [ 0 0 2*t + 2 3*t + 4 0]
1500
+ [ 0 0 1 0 0]
1501
+ [ 0 0 0 0 1],
1502
+ <BLANKLINE>
1503
+ [ 1 0 0 0 0]
1504
+ [ 0 1 0 0 0]
1505
+ [ 0 0 1 0 0]
1506
+ [ 0 0 0 2*t + 2 3*t + 4]
1507
+ [ 0 0 0 1 0]
1508
+ )
1509
+ sage: c = C5([3,4,-2,-3,1]); c
1510
+ c2*c3*c1^-1*c2^-1*c0
1511
+ sage: m = C5Mch5(c); m
1512
+ [2*t + 2 3*t + 4 0 0 0]
1513
+ [ 0 0 0 1 0]
1514
+ [2*t + 1 0 2*t + 2 3*t 3*t + 3]
1515
+ [2*t + 2 0 0 3*t + 4 0]
1516
+ [ 0 0 2*t + 2 3*t + 4 0]
1517
+ sage: m_back = C5(m)
1518
+ sage: m_back == c
1519
+ True
1520
+ sage: U5 = AssionGroupU(5); U5
1521
+ Assion group on 5 strands of type U
1522
+ sage: U5Mch3 = U5.as_matrix_group(characteristic=3)
1523
+ Traceback (most recent call last):
1524
+ ...
1525
+ ValueError: Burau representation does not factor through the relations
1526
+ """
1527
+ # ------------------------------------------------------------------
1528
+ # define matrix group by generators using the Burau representation
1529
+ # ------------------------------------------------------------------
1530
+ unitary = False
1531
+ if isinstance(reduced, str):
1532
+ if reduced == 'unitary':
1533
+ unitary = True
1534
+ gen_list = []
1535
+ for braid_gen in self.gens():
1536
+ bur_mat = braid_gen.burau_matrix(root_bur=root_bur, domain=domain,
1537
+ characteristic=characteristic,
1538
+ var=var, reduced=reduced)
1539
+ if unitary:
1540
+ bur_mat, bur_mat_ad, herm_form = bur_mat
1541
+
1542
+ if domain is None:
1543
+ domain = bur_mat.base_ring()
1544
+
1545
+ gen_list.append(bur_mat)
1546
+
1547
+ if unitary and herm_form.is_singular():
1548
+ unitary = False # since a degenerated hermitian form doesn't define a unitary group
1549
+ if self._classical_invariant_form is None:
1550
+ self._classical_invariant_form = herm_form
1551
+
1552
+ if unitary:
1553
+ from sage.rings.finite_rings.finite_field_base import FiniteField
1554
+ from sage.groups.matrix_gps.unitary import GU
1555
+ _, d = herm_form.dimensions()
1556
+ if isinstance(domain, FiniteField):
1557
+ base_group = GU(d, domain, var=domain.gen(), invariant_form=herm_form)
1558
+ else:
1559
+ base_group = GU(d, domain, invariant_form=herm_form)
1560
+
1561
+ matrix_group = base_group.subgroup(gen_list)
1562
+ else:
1563
+ from sage.groups.matrix_gps.finitely_generated import MatrixGroup
1564
+ matrix_group = MatrixGroup(gen_list, category=self.category())
1565
+
1566
+ # --------------------------------------------------------------------
1567
+ # check if there is a well defined group homomorphism to matrix_group
1568
+ # Register map from ``self`` to matrix_group.
1569
+ # Since GAP' check is very expansive (on time and memory), the check is performed
1570
+ # here.
1571
+ # -------------------------------------------------------------------------------
1572
+ hom_to_mat = self.hom(matrix_group.gens(), check=False)
1573
+ if not all(hom_to_mat(rel).is_one() for rel in self.relations()):
1574
+ raise ValueError("Burau representation does not factor through the relations")
1575
+ matrix_group.register_conversion(hom_to_mat)
1576
+ return matrix_group
1577
+
1578
+ # ----------------------------------------------------------------------------------
1579
+ # Although this method is available for finitely presented group
1580
+ # we use the classical group implementation (by performance reason) to get
1581
+ # the permutation_group.
1582
+ # ----------------------------------------------------------------------------------
1583
+ @cached_method
1584
+ def as_permutation_group(self, use_classical=True):
1585
+ r"""
1586
+ Return a permutation group isomorphic to ``self`` that has a
1587
+ group isomorphism from ``self`` as a conversion.
1588
+
1589
+ INPUT:
1590
+
1591
+ - ``use_classical`` -- boolean (default: ``True``); the permutation
1592
+ group is calculated via the attached classical matrix group as this
1593
+ results in a smaller degree; if ``False``, the permutation group will
1594
+ be calculated using ``self`` (as finitely presented group)
1595
+
1596
+ EXAMPLES::
1597
+
1598
+ sage: C3 = CubicBraidGroup(3)
1599
+ sage: PC3 = C3.as_permutation_group()
1600
+ sage: assert C3.is_isomorphic(PC3) # random (with respect to the occurrence of the info message)
1601
+ sage: PC3.degree()
1602
+ 8
1603
+ sage: c = C3([2,1-2])
1604
+ sage: C3(PC3(c)) == c
1605
+ True
1606
+ """
1607
+ if not self.is_finite():
1608
+ raise ValueError('cubic braid group is infinite!')
1609
+
1610
+ if use_classical:
1611
+ CG = self.as_classical_group()
1612
+ from sage.groups.perm_gps.permgroup import PermutationGroup_generic
1613
+ if isinstance(CG, PermutationGroup_generic):
1614
+ return CG
1615
+ CGM = CG.as_matrix_group()
1616
+ PG = CGM.as_permutation_group()
1617
+ img_gens = [PG(CGM(CG(gen))) for gen in self.gens()]
1618
+ else:
1619
+ PG = super().as_permutation_group()
1620
+ img_gens = PG.gens()
1621
+
1622
+ img_gens = [PG(gen) for gen in img_gens]
1623
+ hom_to_perm = self.hom(img_gens)
1624
+ PG.register_conversion(hom_to_perm)
1625
+ return PG
1626
+
1627
+ # ----------------------------------------------------------------------------------
1628
+ # as_classical_group
1629
+ # ----------------------------------------------------------------------------------
1630
+ def as_classical_group(self, embedded=False):
1631
+ r"""
1632
+ Create an isomorphic image of ``self`` as a classical group according
1633
+ to the construction given by Coxeter resp. Assion.
1634
+
1635
+ INPUT:
1636
+
1637
+ - ``embedded`` -- boolean (default: ``False``); this boolean effects the
1638
+ cases of Assion groups when they are realized as projective groups only.
1639
+ More precisely: if ``self`` is of ``cbg_type CubicBraidGroup.type.AssionS``
1640
+ (for example) and the number of strands ``n`` is even, than its classical
1641
+ group is a subgroup of ``PSp(n,3)`` (being centralized by the element
1642
+ ``self.centralizing_element(projective=True))``. By default this group
1643
+ will be given. Setting ``embedded = True`` the classical realization
1644
+ is given as subgroup of its classical enlargement with one more strand
1645
+ (in this case as subgroup of ``Sp(n,3))``.
1646
+
1647
+ OUTPUT:
1648
+
1649
+ Depending on the type of ``self`` and the number of strands an
1650
+ instance of ``Sp(n-1,3)``, ``GU(n-1,2)``, subgroup of ``PSp(n,3)``,
1651
+ ``PGU(n,2)``, or a subgroup of ``GU(n-1, UCF)``
1652
+ (``cbg_type == CubicBraidGroup.type.Coxeter``) with respect to a
1653
+ certain Hermitian form attached to the Burau representation
1654
+ (used by Coxeter and Squier). Here ``UCF`` stands for the universal
1655
+ cyclotomic field.
1656
+
1657
+ EXAMPLES::
1658
+
1659
+ sage: # needs sage.rings.finite_rings
1660
+ sage: U3 = AssionGroupU(3)
1661
+ sage: U3Cl = U3.as_classical_group(); U3Cl
1662
+ Subgroup generated by [(1,7,6)(3,19,14)(4,15,10)(5,11,18)(12,16,20),
1663
+ (1,12,13)(2,15,19)(4,9,14)(5,18,8)(6,21,16)] of
1664
+ (The projective general unitary group of degree 3 over Finite Field of size 2)
1665
+ sage: U3Clemb = U3.as_classical_group(embedded=True); U3Clemb
1666
+ Subgroup with 2 generators (
1667
+ [0 0 a] [a + 1 a a]
1668
+ [0 1 0] [ a a + 1 a]
1669
+ [a 0 a], [ a a a + 1]
1670
+ ) of General Unitary Group of degree 3 over Finite Field in a of size 2^2
1671
+ sage: u = U3([-2,1,-2,1]); u
1672
+ (u1^-1*u0)^2
1673
+ sage: uCl = U3Cl(u); uCl
1674
+ (1,16)(2,9)(3,10)(4,19)(6,12)(7,20)(13,21)(14,15)
1675
+ sage: uCle = U3Clemb(u); uCle
1676
+ [a + 1 a + 1 1]
1677
+ [a + 1 0 a]
1678
+ [ 1 a a]
1679
+ sage: U3(uCl) == u
1680
+ True
1681
+ sage: U3(uCle) == u
1682
+ True
1683
+ sage: U4 = AssionGroupU(4)
1684
+ sage: U4Cl = U4.as_classical_group(); U4Cl
1685
+ General Unitary Group of degree 3 over Finite Field in a of size 2^2
1686
+ sage: U3Clemb.ambient() == U4Cl
1687
+ True
1688
+
1689
+ sage: # needs sage.rings.number_field
1690
+ sage: C4 = CubicBraidGroup(4)
1691
+ sage: C4Cl = C4.as_classical_group(); C4Cl
1692
+ Subgroup with 3 generators (
1693
+ [ E(3)^2 0 0] [ 1 -E(12)^7 0]
1694
+ [-E(12)^7 1 0] [ 0 E(3)^2 0]
1695
+ [ 0 0 1], [ 0 -E(12)^7 1],
1696
+ <BLANKLINE>
1697
+ [ 1 0 0]
1698
+ [ 0 1 -E(12)^7]
1699
+ [ 0 0 E(3)^2]
1700
+ ) of General Unitary Group of degree 3 over Universal Cyclotomic Field
1701
+ with respect to positive definite hermitian form
1702
+ [-E(12)^7 + E(12)^11 -1 0]
1703
+ [ -1 -E(12)^7 + E(12)^11 -1]
1704
+ [ 0 -1 -E(12)^7 + E(12)^11]
1705
+ """
1706
+ # -------------------------------------------------------------------------------
1707
+ # create the classical group if not already done
1708
+ # -------------------------------------------------------------------------------
1709
+ if self._classical_group is None:
1710
+ if embedded and self._classical_embedding is None:
1711
+ # this is separated to avoid unnecessary (for larger group exhaustive) calculations
1712
+ self._create_classical_realization(just_embedded=True)
1713
+ else:
1714
+ self._create_classical_realization()
1715
+
1716
+ if embedded and self._classical_embedding is not None:
1717
+ # ----------------------------------------------------------------------------------------
1718
+ # there is a difference between self._classical_group and self._classical_embedding
1719
+ # only in the cases where self.strands() divides by 2 (AssionGroupS) resp. 3
1720
+ # (AssionGroupU). In this case the embedding is the subgroup of the classical group
1721
+ # of one strand more (self.strands() +1) generated by the first self.strands() -1
1722
+ # generators
1723
+ # ----------------------------------------------------------------------------------------
1724
+ return self._classical_embedding
1725
+ elif self._classical_group is not None:
1726
+ return self._classical_group
1727
+
1728
+ raise ValueError("no classical embedding defined")
1729
+
1730
+ # ----------------------------------------------------------------------------------
1731
+ # as_refection_group
1732
+ # ----------------------------------------------------------------------------------
1733
+ def as_reflection_group(self):
1734
+ r"""
1735
+ Return an isomorphic image of ``self`` as irreducible complex
1736
+ reflection group.
1737
+
1738
+ This is possible only for the finite cubic braid groups of ``cbg_type``
1739
+ ``CubicBraidGroup.type.Coxeter``.
1740
+
1741
+ .. NOTE::
1742
+
1743
+ This method uses the sage implementation of reflection group via
1744
+ the ``gap3`` ``CHEVIE`` package. These must be installed in order
1745
+ to use this method.
1746
+
1747
+ EXAMPLES::
1748
+
1749
+ sage: # optional - gap3
1750
+ sage: C3.<c1,c2> = CubicBraidGroup(3)
1751
+ sage: R3 = C3.as_reflection_group(); R3
1752
+ Irreducible complex reflection group of rank 2 and type ST4
1753
+ sage: R3.cartan_matrix()
1754
+ [-2*E(3) - E(3)^2 E(3)^2]
1755
+ [ -E(3)^2 -2*E(3) - E(3)^2]
1756
+ sage: R3.simple_roots()
1757
+ Finite family {1: (0, -2*E(3) - E(3)^2), 2: (2*E(3)^2, E(3)^2)}
1758
+ sage: R3.simple_coroots()
1759
+ Finite family {1: (0, 1), 2: (1/3*E(3) - 1/3*E(3)^2, 1/3*E(3) - 1/3*E(3)^2)}
1760
+
1761
+ Conversion maps::
1762
+
1763
+ sage: # optional - gap3
1764
+ sage: r = R3.an_element()
1765
+ sage: cr = C3(r); cr
1766
+ c1*c2
1767
+ sage: mr = r.matrix(); mr
1768
+ [ 1/3*E(3) - 1/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2]
1769
+ [-2/3*E(3) + 2/3*E(3)^2 2/3*E(3) + 1/3*E(3)^2]
1770
+ sage: C3Cl = C3.as_classical_group()
1771
+ sage: C3Cl(cr)
1772
+ [ E(3)^2 -E(4)]
1773
+ [-E(12)^7 0]
1774
+
1775
+ The reflection groups can also be viewed as subgroups of unitary groups
1776
+ over the universal cyclotomic field. Note that the unitary group
1777
+ corresponding to the reflection group is isomorphic but different from
1778
+ the classical group due to different hermitian forms for the unitary
1779
+ groups they live in::
1780
+
1781
+ sage: # optional - gap3
1782
+ sage: C4 = CubicBraidGroup(4)
1783
+ sage: R4 = C4.as_reflection_group()
1784
+ sage: R4.invariant_form()
1785
+ [1 0 0]
1786
+ [0 1 0]
1787
+ [0 0 1]
1788
+ sage: _ == C4.classical_invariant_form()
1789
+ False
1790
+ """
1791
+ # -------------------------------------------------------------------------------
1792
+ # the reflection groups are called according to the Shephard-Todd classification:
1793
+ # 2 strands -> G(2,1,1)
1794
+ # 3 strands -> G4
1795
+ # 4 strands -> G25
1796
+ # 5 strands -> G32
1797
+ # -------------------------------------------------------------------------------
1798
+ from sage.combinat.root_system.reflection_group_real import is_chevie_available
1799
+ if not is_chevie_available():
1800
+ raise ImportError("the GAP3 package 'CHEVIE' is needed to obtain the corresponding reflection groups")
1801
+
1802
+ if self._cbg_type != CubicBraidGroup.type.Coxeter or self.strands() > 5 or self.strands() < 2:
1803
+ raise ValueError("no reflection group defined")
1804
+
1805
+ # -------------------------------------------------------------------------------
1806
+ # define reflection group associated to self
1807
+ # -------------------------------------------------------------------------------
1808
+ reflection_group = None
1809
+
1810
+ from sage.combinat.root_system.reflection_group_real import ReflectionGroup
1811
+
1812
+ if self.strands() == 2:
1813
+ reflection_group = ReflectionGroup([2, 1, 1])
1814
+ elif self.strands() == 3:
1815
+ reflection_group = ReflectionGroup(4)
1816
+ elif self.strands() == 4:
1817
+ reflection_group = ReflectionGroup(25)
1818
+ elif self.strands() == 5:
1819
+ reflection_group = ReflectionGroup(32)
1820
+
1821
+ hom_to_refl = self.hom(reflection_group.gens())
1822
+ reflection_group.register_conversion(hom_to_refl)
1823
+ return reflection_group
1824
+
1825
+ # ----------------------------------------------------------------------------------
1826
+ # classical invariant form returns the invariant form of the classical realization
1827
+ # ----------------------------------------------------------------------------------
1828
+ def classical_invariant_form(self):
1829
+ r"""
1830
+ Return the invariant form of the classical realization of ``self``.
1831
+
1832
+ OUTPUT:
1833
+
1834
+ A square matrix of dimension according to the space the classical
1835
+ realization is operating on. In the case of the full cubic braid groups
1836
+ and of the Assion groups of ``cbg_type CubicBraidGroup.type.AssionU``
1837
+ the matrix is Hermitian. In the case of the Assion groups of
1838
+ ``cbg_type CubicBraidGroup.type.AssionS`` it is alternating.
1839
+ Note that the invariant form of the full cubic braid group on more
1840
+ than 5 strands is degenerated (causing the group to be infinite).
1841
+
1842
+ In the case of Assion groups having projective classical groups,
1843
+ the invariant form corresponds to the ambient group of its
1844
+ classical embedding.
1845
+
1846
+ EXAMPLES::
1847
+
1848
+ sage: S3 = AssionGroupS(3)
1849
+ sage: S3.classical_invariant_form()
1850
+ [0 1]
1851
+ [2 0]
1852
+ sage: S4 = AssionGroupS(4)
1853
+ sage: S4.classical_invariant_form()
1854
+ [0 0 0 1]
1855
+ [0 0 1 0]
1856
+ [0 2 0 0]
1857
+ [2 0 0 0]
1858
+ sage: S5 = AssionGroupS(5)
1859
+ sage: S4.classical_invariant_form() == S5.classical_invariant_form()
1860
+ True
1861
+ sage: U4 = AssionGroupU(4)
1862
+ sage: U4.classical_invariant_form()
1863
+ [0 0 1]
1864
+ [0 1 0]
1865
+ [1 0 0]
1866
+ sage: C5 = CubicBraidGroup(5)
1867
+ sage: C5.classical_invariant_form()
1868
+ [-E(12)^7 + E(12)^11 -1 0 0]
1869
+ [ -1 -E(12)^7 + E(12)^11 -1 0]
1870
+ [ 0 -1 -E(12)^7 + E(12)^11 -1]
1871
+ [ 0 0 -1 -E(12)^7 + E(12)^11]
1872
+ sage: _.is_singular()
1873
+ False
1874
+ sage: C6 = CubicBraidGroup(6)
1875
+ sage: C6.classical_invariant_form().is_singular()
1876
+ True
1877
+ """
1878
+ # -------------------------------------------------------------------------------
1879
+ # create the classical_invariant_form if not already done
1880
+ # -------------------------------------------------------------------------------
1881
+ if self._classical_invariant_form is None:
1882
+ self._create_classical_realization()
1883
+
1884
+ if self._classical_invariant_form is None:
1885
+ raise ValueError("no classical invariant form defined")
1886
+
1887
+ return self._classical_invariant_form
1888
+
1889
+ # ----------------------------------------------------------------------------------
1890
+ # centralizing element in the classical symplectic resp. unitary group
1891
+ # ----------------------------------------------------------------------------------
1892
+ def centralizing_element(self, embedded=False):
1893
+ r"""
1894
+ Return the centralizing element defined by the work of Assion
1895
+ (Hilfssatz 1.1.3 and 1.2.3).
1896
+
1897
+ INPUT:
1898
+
1899
+ - ``embedded`` -- boolean (default; ``False``); this boolean only effects
1900
+ the cases of Assion groups when they are realized as projective groups.
1901
+ More precisely: if ``self`` is of ``cbg_type CubicBraidGroup.type.AssionS``
1902
+ (for example) and the number of strands ``n`` is even, than its
1903
+ classical group is a subgroup of ``PSp(n,3)`` being centralized
1904
+ by the element return for option ``embedded=False``. Otherwise the
1905
+ image of this element inside the embedded classical group will be
1906
+ returned (see option embedded of :meth:`classical_group`).
1907
+
1908
+ OUTPUT:
1909
+
1910
+ Depending on the optional keyword a permutation as an element
1911
+ of ``PSp(n,3)`` (type S) or ``PGU(n,2)`` (type U) for ``n = 0 mod 2``
1912
+ (type S) resp. ``n = 0 mod 3`` (type U) is returned. Otherwise, the
1913
+ centralizing element is a matrix belonging to ``Sp(n,3)``
1914
+ resp. ``GU(n,2)``.
1915
+
1916
+ EXAMPLES::
1917
+
1918
+ sage: U3 = AssionGroupU(3); U3
1919
+ Assion group on 3 strands of type U
1920
+ sage: U3Cl = U3.as_classical_group(); U3Cl
1921
+ Subgroup generated by [(1,7,6)(3,19,14)(4,15,10)(5,11,18)(12,16,20),
1922
+ (1,12,13)(2,15,19)(4,9,14)(5,18,8)(6,21,16)]
1923
+ of (The projective general unitary group of degree 3 over Finite Field of size 2)
1924
+ sage: c = U3.centralizing_element(); c
1925
+ (1,16)(2,9)(3,10)(4,19)(6,12)(7,20)(13,21)(14,15)
1926
+ sage: c in U3Cl
1927
+ True
1928
+ sage: P = U3Cl.ambient_group()
1929
+ sage: P.centralizer(c) == U3Cl
1930
+ True
1931
+
1932
+ Embedded version::
1933
+
1934
+ sage: cm = U3.centralizing_element(embedded=True); cm
1935
+ [a + 1 a + 1 1]
1936
+ [a + 1 0 a]
1937
+ [ 1 a a]
1938
+ sage: U4 = AssionGroupU(4)
1939
+ sage: U4Cl = U4.as_classical_group()
1940
+ sage: cm in U4Cl
1941
+ True
1942
+ sage: [cm * U4Cl(g) == U4Cl(g) * cm for g in U4.gens()]
1943
+ [True, True, False]
1944
+ """
1945
+ # -------------------------------------------------------------------------------
1946
+ # create the centralizing elements if not already done
1947
+ # -------------------------------------------------------------------------------
1948
+ if self._centralizing_matrix is None:
1949
+ self._create_classical_realization()
1950
+
1951
+ if self._centralizing_matrix is None:
1952
+ raise ValueError("no centralizing element defined")
1953
+ else:
1954
+ if embedded or self._centralizing_element is None:
1955
+ return self._centralizing_matrix
1956
+ else:
1957
+ return self._centralizing_element
1958
+
1959
+ # ----------------------------------------------------------------------------------
1960
+ # calculating the order by formula
1961
+ # ----------------------------------------------------------------------------------
1962
+ def order(self):
1963
+ r"""
1964
+ To avoid long wait-time on calculations the order will be obtained
1965
+ using the classical realization.
1966
+
1967
+ OUTPUT: cardinality of the group as integer or infinity
1968
+
1969
+ EXAMPLES::
1970
+
1971
+ sage: S15 = AssionGroupS(15)
1972
+ sage: S15.order()
1973
+ 109777561863482259035023554842176139436811616256000
1974
+ sage: C6 = CubicBraidGroup(6)
1975
+ sage: C6.order()
1976
+ +Infinity
1977
+ """
1978
+ from sage.rings.infinity import infinity
1979
+ n = self.strands()
1980
+
1981
+ if self._cbg_type == CubicBraidGroup.type.Coxeter and n > 5:
1982
+ order = infinity
1983
+ else:
1984
+ order = self.as_classical_group(embedded=True).order()
1985
+
1986
+ return order
1987
+
1988
+ cardinality = order
1989
+
1990
+ def is_finite(self):
1991
+ r"""
1992
+ Return if ``self`` is a finite group or not.
1993
+
1994
+ EXAMPLES::
1995
+
1996
+ sage: CubicBraidGroup(6).is_finite()
1997
+ False
1998
+ sage: AssionGroupS(6).is_finite()
1999
+ True
2000
+ """
2001
+ return not (self._cbg_type == CubicBraidGroup.type.Coxeter and self.strands() > 5)
2002
+
2003
+ # ------------------------------------------------------------------
2004
+ # creating a CubicBraidGroup as subgroup of self on less strands
2005
+ # ------------------------------------------------------------------
2006
+ def cubic_braid_subgroup(self, nstrands=None):
2007
+ r"""
2008
+ Return a cubic braid group as subgroup of ``self`` on the first
2009
+ ``nstrands`` strands.
2010
+
2011
+ INPUT:
2012
+
2013
+ - ``nstrands`` -- (default: ``self.strands() - 1``) integer at least 1
2014
+ and at most ``self.strands()`` giving the number of strands of
2015
+ the subgroup
2016
+
2017
+ .. WARNING::
2018
+
2019
+ Since ``self`` is inherited from :class:`UniqueRepresentation`, the
2020
+ obtained instance is identical to other instances created with the
2021
+ same arguments (see example below). The ambient group corresponds
2022
+ to the last call of this method.
2023
+
2024
+ EXAMPLES::
2025
+
2026
+ sage: U5 = AssionGroupU(5)
2027
+ sage: U3s = U5.cubic_braid_subgroup(3)
2028
+ sage: u1, u2 = U3s.gens()
2029
+ sage: u1 in U5
2030
+ False
2031
+ sage: U5(u1) in U5.gens()
2032
+ True
2033
+ sage: U3s is AssionGroupU(3)
2034
+ True
2035
+ sage: U3s.ambient() == U5
2036
+ True
2037
+ """
2038
+ if nstrands is None:
2039
+ nstrands = self.strands() - 1
2040
+
2041
+ n = self.strands()
2042
+
2043
+ nstrands = Integer(nstrands)
2044
+
2045
+ if nstrands >= n or nstrands <= 0:
2046
+ raise ValueError("nstrands must be positive and less than %s" % (self.strands()))
2047
+
2048
+ names = self.variable_names()
2049
+ names_red = names[:nstrands - 1]
2050
+ subgrp = CubicBraidGroup(names=names_red, cbg_type=self._cbg_type)
2051
+ subgrp._ambient = self
2052
+ return subgrp