Skip to content

Commit 1c1a0e1

Browse files
author
Release Manager
committed
gh-39415: Implementation of the general Burau representation for Artin groups <!-- ^ Please provide a concise and informative title. --> <!-- ^ Don't put issue numbers in the title, do this in the PR description below. --> <!-- ^ For example, instead of "Fixes #12345" use "Introduce new method to calculate 1 + 2". --> <!-- v Describe your changes below in detail. --> <!-- v Why is this change required? What problem does it solve? --> <!-- v If this PR resolves an open issue, please link to it here. For example, "Fixes #12345". --> We provide an implementation of the generalized Burau representation (following [this paper by Bapat and Queffelec](https://arxiv.org/abs/2409.00144)). We also allow Artin groups for arbitrary type to be constructed. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [x] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [x] I have updated the documentation and checked the documentation preview. ### ⌛ Dependencies <!-- List all open PRs that this PR logically depends on. For example, --> <!-- - #12345: short description why this is a dependency --> <!-- - #34567: ... --> URL: #39415 Reported by: Travis Scrimshaw Reviewer(s): Enrique Manuel Artal Bartolo, Sebastian Oehms, Travis Scrimshaw
2 parents cac481c + 02a7986 commit 1c1a0e1

File tree

3 files changed

+352
-8
lines changed

3 files changed

+352
-8
lines changed

src/doc/en/reference/references/index.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -568,6 +568,10 @@ REFERENCES:
568568
Springer Verlag 2006; pre-print available at
569569
http://eprint.iacr.org/2005/200
570570
571+
.. [BQ2024] Asilata Bapat and Hoel Queffelec.
572+
*Some remarks about the faithfulness of the Burau representation
573+
of Artin-Tits groups*. Preprint, 2024. :arxiv:`2409.00144`.
574+
571575
.. [BBS1982] \L. Blum, M. Blum, and M. Shub. Comparison of Two
572576
Pseudo-Random Number Generators. *Advances in Cryptology:
573577
Proceedings of Crypto '82*, pp.61--78, 1982.

src/sage/groups/artin.py

Lines changed: 313 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,17 @@
99
AUTHORS:
1010
1111
- Travis Scrimshaw (2018-02-05): Initial version
12+
- Travis Scrimshaw (2025-01-30): Allowed general construction; implemented
13+
general Burau representation
14+
15+
.. TODO::
16+
17+
Implement affine type Artin groups with their well-known embeddings into
18+
the classical braid group.
1219
"""
1320

1421
# ****************************************************************************
15-
# Copyright (C) 2018 Travis Scrimshaw <tcscrims at gmail.com>
22+
# Copyright (C) 2018-2025 Travis Scrimshaw <tcscrims at gmail.com>
1623
#
1724
# This program is free software: you can redistribute it and/or modify
1825
# it under the terms of the GNU General Public License as published by
@@ -26,6 +33,7 @@
2633
from sage.groups.free_group import FreeGroup
2734
from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement
2835
from sage.misc.cachefunc import cached_method
36+
from sage.misc.lazy_attribute import lazy_attribute
2937
from sage.rings.infinity import Infinity
3038
from sage.structure.richcmp import richcmp, rich_to_bool
3139
from sage.structure.unique_representation import UniqueRepresentation
@@ -156,6 +164,155 @@ def coxeter_group_element(self, W=None):
156164
In = W.index_set()
157165
return W.prod(s[In[abs(i)-1]] for i in self.Tietze())
158166

167+
def burau_matrix(self, var='t'):
168+
r"""
169+
Return the Burau matrix of the Artin group element.
170+
171+
Following [BQ2024]_, the (generalized) Burau representation
172+
of an Artin group is defined by deforming the reflection
173+
representation of the corresponding Coxeter group. However,
174+
we substitute `q \mapsto -t` from [BQ2024]_ to match one of
175+
the unitary (reduced) Burau representations of the braid group
176+
(see :meth:`sage.groups.braid.Braid.burau_matrix()` for details.)
177+
178+
More precisely, let `(m_{ij})_{i,j \in I}` be the
179+
:meth:`Coxeter matrix<coxeter_matrix>`. Then the action is
180+
given on the basis `(\alpha_1, \ldots \alpha_n)` (corresponding
181+
to the reflection representation of the corresponding
182+
:meth:`Coxeter group<coxeter_group>`) by
183+
184+
.. MATH::
185+
186+
\sigma_i(\alpha_j) = \alpha_j
187+
- \langle \alpha_i, \alpha_j \rangle_q \alpha_i,
188+
\qquad \text{ where }
189+
\langle \alpha_i, \alpha_j \rangle_q := \begin{cases}
190+
1 + t^2 & \text{if } i = j, \\
191+
-2 t \cos(\pi/m_{ij}) & \text{if } i \neq j.
192+
\end{cases}.
193+
194+
By convention `\cos(\pi/\infty) = 1`. Note that the inverse of the
195+
generators act by `\sigma_i^{-1}(\alpha_j) = \alpha_j - q^{-2}
196+
\langle \alpha_j, \alpha_i \rangle_q \alpha_i`.
197+
198+
INPUT:
199+
200+
- ``var`` -- string (default: ``'t'``); the name of the
201+
variable in the entries of the matrix
202+
203+
OUTPUT:
204+
205+
The Burau matrix of the Artin group element over the Laurent
206+
polynomial ring in the variable ``var``.
207+
208+
EXAMPLES::
209+
210+
sage: A.<s1,s2,s3> = ArtinGroup(['B',3])
211+
sage: B1 = s1.burau_matrix()
212+
sage: B2 = s2.burau_matrix()
213+
sage: B3 = s3.burau_matrix()
214+
sage: [B1, B2, B3]
215+
[
216+
[-t^2 t 0] [ 1 0 0] [ 1 0 0]
217+
[ 0 1 0] [ t -t^2 a*t] [ 0 1 0]
218+
[ 0 0 1], [ 0 0 1], [ 0 a*t -t^2]
219+
]
220+
sage: B1 * B2 * B1 == B2 * B1 * B2
221+
True
222+
sage: B2 * B3 * B2 * B3 == B3 * B2 * B3 * B2
223+
True
224+
sage: B1 * B3 == B3 * B1
225+
True
226+
sage: (~s1).burau_matrix() * B1 == 1
227+
True
228+
229+
We verify the example in Theorem 4.1 of [BQ2024]_::
230+
231+
sage: A.<s1,s2,s3,s4> = ArtinGroup(['A', 3, 1])
232+
sage: [g.burau_matrix() for g in A.gens()]
233+
[
234+
[-t^2 t 0 t] [ 1 0 0 0] [ 1 0 0 0]
235+
[ 0 1 0 0] [ t -t^2 t 0] [ 0 1 0 0]
236+
[ 0 0 1 0] [ 0 0 1 0] [ 0 t -t^2 t]
237+
[ 0 0 0 1], [ 0 0 0 1], [ 0 0 0 1],
238+
<BLANKLINE>
239+
[ 1 0 0 0]
240+
[ 0 1 0 0]
241+
[ 0 0 1 0]
242+
[ t 0 t -t^2]
243+
]
244+
sage: a = s3^2 * s4 * s3 * s2 *s1 * ~s3 * s4 * s3 * s2 * s1^-2 * s4
245+
sage: b = s1^2 * ~s2 * s4 * s1 * ~s3 * s2 * ~s4 * s3 * s1 * s4 * s1 * ~s2 * s4^-2 * s3
246+
sage: alpha = a * s3 * ~a
247+
sage: beta = b * s2 * ~b
248+
sage: elm = alpha * beta * ~alpha * ~beta
249+
sage: print(elm.Tietze())
250+
(3, 3, 4, 3, 2, 1, -3, 4, 3, 2, -1, -1, 4, 3, -4, 1, 1, -2, -3,
251+
-4, 3, -1, -2, -3, -4, -3, -3, 1, 1, -2, 4, 1, -3, 2, -4, 3, 1,
252+
4, 1, -2, -4, -4, 3, 2, -3, 4, 4, 2, -1, -4, -1, -3, 4, -2, 3,
253+
-1, -4, 2, -1, -1, 3, 3, 4, 3, 2, 1, -3, 4, 3, 2, -1, -1, 4,
254+
-3, -4, 1, 1, -2, -3, -4, 3, -1, -2, -3, -4, -3, -3, 1, 1, -2,
255+
4, 1, -3, 2, -4, 3, 1, 4, 1, -2, -4, -4, 3, -2, -3, 4, 4, 2,
256+
-1, -4, -1, -3, 4, -2, 3, -1, -4, 2, -1, -1)
257+
sage: elm.burau_matrix()
258+
[1 0 0 0]
259+
[0 1 0 0]
260+
[0 0 1 0]
261+
[0 0 0 1]
262+
263+
Next, we show ``elm`` is not the identity by using the embedding of
264+
the affine braid group `\widetilde{B}_n \to B_{n+1}`::
265+
266+
sage: B.<t1,t2,t3,t4> = BraidGroup(5)
267+
sage: D = t1 * t2 * t3 * t4^2
268+
sage: t0 = D * t3 * ~D
269+
sage: t0*t1*t0 == t1*t0*t1
270+
True
271+
sage: t0*t2 == t2*t0
272+
True
273+
sage: t0*t3*t0 == t3*t0*t3
274+
True
275+
sage: T = [t0, t1, t2, t3]
276+
sage: emb = B.prod(T[i-1] if i > 0 else ~T[-i-1] for i in elm.Tietze())
277+
sage: emb.is_one()
278+
False
279+
280+
Since the Burau representation does not respect the group embedding,
281+
the corresponding `B_5` element's Burau matrix is not the identity
282+
(Bigelow gave an example of the representation not being faithful for
283+
`B_5`, but it is still open for `B_4`)::
284+
285+
sage: emb.burau_matrix() != 1
286+
True
287+
288+
We also verify the result using the elements in [BQ2024]_ Remark 4.2::
289+
290+
sage: ap = s3 * s1 * s2 * s1 * ~s3 * s4 * s2 * s3 * s2 * ~s3 * s1^-2 * s4
291+
sage: bp = s1 * ~s4 * s1^2 * s3^-2 * ~s2 * s4 * s1 * ~s3 * s2 * ~s4 * s3 * s1 * s4 * s1 * ~s2 * s4^-2 * s3
292+
sage: alpha = ap * s3 * ~ap
293+
sage: beta = bp * s2 * ~bp
294+
sage: elm = alpha * beta * ~alpha * ~beta
295+
sage: elm.burau_matrix()
296+
[1 0 0 0]
297+
[0 1 0 0]
298+
[0 0 1 0]
299+
[0 0 0 1]
300+
301+
REFERENCES:
302+
303+
- [BQ2024]_
304+
"""
305+
gens, invs = self.parent()._burau_generators
306+
MS = gens[0].parent()
307+
ret = MS.prod(gens[i-1] if i > 0 else invs[-i-1] for i in self.Tietze())
308+
309+
if var == 't':
310+
return ret
311+
312+
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
313+
poly_ring = LaurentPolynomialRing(ret.base_ring().base_ring(), var)
314+
return ret.change_ring(poly_ring)
315+
159316

160317
class FiniteTypeArtinGroupElement(ArtinGroupElement):
161318
"""
@@ -453,7 +610,7 @@ def __classcall_private__(cls, coxeter_data, names=None):
453610
from sage.groups.raag import RightAngledArtinGroup
454611
return RightAngledArtinGroup(coxeter_data.coxeter_graph(), names)
455612
if not coxeter_data.is_finite():
456-
raise NotImplementedError
613+
return super().__classcall__(cls, coxeter_data, names)
457614
if coxeter_data.coxeter_type().cartan_type().type() == 'A':
458615
from sage.groups.braid import BraidGroup
459616
return BraidGroup(coxeter_data.rank()+1, names)
@@ -476,15 +633,17 @@ def __init__(self, coxeter_matrix, names):
476633
rels = []
477634
# Generate the relations based on the Coxeter graph
478635
I = coxeter_matrix.index_set()
636+
gens = free_group.gens()
479637
for ii, i in enumerate(I):
480-
for j in I[ii + 1:]:
638+
for jj, j in enumerate(I[ii + 1:], start=ii+1):
481639
m = coxeter_matrix[i, j]
482640
if m == Infinity: # no relation
483641
continue
484-
elt = [i, j] * m
485-
for ind in range(m, 2*m):
486-
elt[ind] = -elt[ind]
487-
rels.append(free_group(elt))
642+
elt = (gens[ii] * gens[jj]) ** (m // 2)
643+
if m % 2 == 1:
644+
elt = elt * gens[ii] * ~gens[jj]
645+
elt = elt * (~gens[ii] * ~gens[jj]) ** (m // 2)
646+
rels.append(elt)
488647
FinitelyPresentedGroup.__init__(self, free_group, tuple(rels))
489648

490649
def _repr_(self):
@@ -688,6 +847,153 @@ def _standard_lift(self, w):
688847
"""
689848
return self(self._standard_lift_Tietze(w))
690849

850+
@lazy_attribute
851+
def _burau_generators(self):
852+
"""
853+
The Burau matrices for the generators of ``self`` and their inverses.
854+
855+
EXAMPLES::
856+
857+
sage: A = ArtinGroup(['G',2])
858+
sage: A._burau_generators
859+
[[
860+
[-t^2 a*t] [ 1 0]
861+
[ 0 1], [ a*t -t^2]
862+
],
863+
[
864+
[ -t^-2 a*t^-1] [ 1 0]
865+
[ 0 1], [a*t^-1 -t^-2]
866+
]]
867+
868+
sage: A = ArtinGroup(['H',3])
869+
sage: A._burau_generators
870+
[[
871+
[-t^2 t 0] [ 1 0 0]
872+
[ 0 1 0] [ t -t^2 (1/2*a + 1/2)*t]
873+
[ 0 0 1], [ 0 0 1],
874+
<BLANKLINE>
875+
[ 1 0 0]
876+
[ 0 1 0]
877+
[ 0 (1/2*a + 1/2)*t -t^2]
878+
],
879+
[
880+
[-t^-2 t^-1 0]
881+
[ 0 1 0]
882+
[ 0 0 1],
883+
<BLANKLINE>
884+
[ 1 0 0]
885+
[ t^-1 -t^-2 (1/2*a + 1/2)*t^-1]
886+
[ 0 0 1],
887+
<BLANKLINE>
888+
[ 1 0 0]
889+
[ 0 1 0]
890+
[ 0 (1/2*a + 1/2)*t^-1 -t^-2]
891+
]]
892+
893+
sage: CM = matrix([[1,4,7], [4,1,10], [7,10,1]])
894+
sage: A = ArtinGroup(CM)
895+
sage: gens = A._burau_generators[0]; gens
896+
[
897+
[ -t^2 (E(8) - E(8)^3)*t (-E(7)^3 - E(7)^4)*t]
898+
[ 0 1 0]
899+
[ 0 0 1],
900+
<BLANKLINE>
901+
[ 1 0 0]
902+
[ (E(8) - E(8)^3)*t -t^2 (E(20) - E(20)^9)*t]
903+
[ 0 0 1],
904+
<BLANKLINE>
905+
[ 1 0 0]
906+
[ 0 1 0]
907+
[(-E(7)^3 - E(7)^4)*t (E(20) - E(20)^9)*t -t^2]
908+
]
909+
sage: all(gens[i] * A._burau_generators[1][i] == 1 for i in range(3))
910+
True
911+
sage: B1, B2, B3 = gens
912+
sage: (B1 * B2)^2 == (B2 * B1)^2
913+
True
914+
sage: (B2 * B3)^5 == (B3 * B2)^5
915+
True
916+
sage: B1 * B3 * B1 * B3 * B1 * B3 * B1 == B3 * B1 * B3 * B1 * B3 * B1 * B3
917+
True
918+
"""
919+
data = self.coxeter_type()
920+
coxeter_matrix = self.coxeter_matrix()
921+
n = coxeter_matrix.rank()
922+
923+
# Determine the base field
924+
if data.is_simply_laced():
925+
from sage.rings.integer_ring import ZZ
926+
base_ring = ZZ
927+
elif data.is_finite():
928+
from sage.rings.number_field.number_field import QuadraticField
929+
letter = data.cartan_type().type()
930+
if letter in ['B', 'C', 'F']:
931+
base_ring = QuadraticField(2)
932+
elif letter == 'G':
933+
base_ring = QuadraticField(3)
934+
elif letter == 'H':
935+
base_ring = QuadraticField(5)
936+
else:
937+
from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField
938+
base_ring = UniversalCyclotomicField()
939+
else:
940+
from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField
941+
base_ring = UniversalCyclotomicField()
942+
943+
# Construct the matrices
944+
from sage.matrix.args import SparseEntry
945+
from sage.matrix.matrix_space import MatrixSpace
946+
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
947+
import sage.rings.abc
948+
poly_ring = LaurentPolynomialRing(base_ring, 't')
949+
q = -poly_ring.gen()
950+
MS = MatrixSpace(poly_ring, n, sparse=True)
951+
one = MS.one()
952+
# FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty
953+
if isinstance(base_ring, sage.rings.abc.UniversalCyclotomicField):
954+
E = base_ring.gen
955+
956+
def val(x):
957+
if x == -1:
958+
return 2 * q
959+
elif x == 1:
960+
return 1 + q**2
961+
else:
962+
return q * (E(2*x) + ~E(2*x))
963+
elif isinstance(base_ring, sage.rings.abc.NumberField_quadratic):
964+
from sage.rings.universal_cyclotomic_field import UniversalCyclotomicField
965+
E = UniversalCyclotomicField().gen
966+
967+
def val(x):
968+
if x == -1:
969+
return 2 * q
970+
elif x == 1:
971+
return 1 + q**2
972+
else:
973+
return q * base_ring((E(2 * x) + ~E(2 * x)).to_cyclotomic_field())
974+
else:
975+
def val(x):
976+
if x == -1:
977+
return 2 * q
978+
elif x == 1:
979+
return 1 + q**2
980+
elif x == 2:
981+
return 0
982+
elif x == 3:
983+
return q
984+
else:
985+
from sage.functions.trig import cos
986+
from sage.symbolic.constants import pi
987+
return q * base_ring(2 * cos(pi / x))
988+
index_set = data.index_set()
989+
gens = [one - MS([SparseEntry(i, j, val(coxeter_matrix[index_set[i], index_set[j]]))
990+
for j in range(n)])
991+
for i in range(n)]
992+
invs = [one - q**-2 * MS([SparseEntry(i, j, val(coxeter_matrix[index_set[i], index_set[j]]))
993+
for j in range(n)])
994+
for i in range(n)]
995+
return [gens, invs]
996+
691997
Element = ArtinGroupElement
692998

693999

0 commit comments

Comments
 (0)