Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/groups/matrix_gps/coxeter_group.py
8815 views
1
"""
2
Coxeter Groups As Matrix Groups
3
4
This implements a general Coxeter group as a matrix group by using the
5
reflection representation.
6
7
AUTHORS:
8
9
- Travis Scrimshaw (2013-08-28): Initial version
10
"""
11
12
##############################################################################
13
# Copyright (C) 2013 Travis Scrimshaw <tscrim at ucdavis.edu>
14
#
15
# Distributed under the terms of the GNU General Public License (GPL)
16
#
17
# The full text of the GPL is available at:
18
#
19
# http://www.gnu.org/licenses/
20
##############################################################################
21
22
from sage.structure.unique_representation import UniqueRepresentation
23
from sage.categories.coxeter_groups import CoxeterGroups
24
25
from sage.combinat.root_system.cartan_type import CartanType, CartanType_abstract
26
from sage.groups.matrix_gps.finitely_generated import FinitelyGeneratedMatrixGroup_generic
27
from sage.groups.matrix_gps.group_element import MatrixGroupElement_generic
28
from sage.graphs.graph import Graph
29
from sage.matrix.constructor import matrix
30
from sage.matrix.matrix_space import MatrixSpace
31
from sage.rings.all import ZZ
32
from sage.rings.infinity import infinity
33
from sage.rings.universal_cyclotomic_field.universal_cyclotomic_field import UniversalCyclotomicField
34
35
36
class CoxeterMatrixGroup(FinitelyGeneratedMatrixGroup_generic, UniqueRepresentation):
37
r"""
38
A Coxeter group represented as a matrix group.
39
40
Let `(W, S)` be a Coxeter system and we construct a vector space `V`
41
over `\RR` with a basis of `\{ \alpha_s \}_{s \in S}` and inner product
42
43
.. MATH::
44
45
B(\alpha_s, \alpha_t) = -\cos\left( \frac{\pi}{m_{st}} \right)
46
47
where we have `B(\alpha_s, \alpha_t) = -1` if `m_{st} = \infty`. Next we
48
define a representation `\sigma_s : V \to V` by
49
50
.. MATH::
51
52
\sigma_s \lambda = \lambda - 2 B(\alpha_s, \lambda) \alpha_s.
53
54
This representation is faithful so we can represent the Coxeter group `W`
55
by the set of matrices `\sigma_s` acting on `V`.
56
57
INPUT:
58
59
- ``data`` -- a Coxeter matrix or graph or a Cartan type
60
- ``base_ring`` -- (default: the universal cyclotomic field) the base
61
ring which contains all values `\cos(\pi/m_{ij})` where `(m_{ij})_{ij}`
62
is the Coxeter matrix
63
- ``index_set`` -- (optional) an indexing set for the generators
64
65
For more on creating Coxeter groups, see
66
:meth:`~sage.combinat.root_system.coxeter_group.CoxeterGroup`.
67
68
.. TODO::
69
70
Currently the label `\infty` is implemented as `-1` in the Coxeter
71
matrix.
72
73
EXAMPLES:
74
75
We can create Coxeter groups from Coxeter matrices::
76
77
sage: W = CoxeterGroup([[1, 6, 3], [6, 1, 10], [3, 10, 1]])
78
sage: W
79
Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
80
[ 1 6 3]
81
[ 6 1 10]
82
[ 3 10 1]
83
sage: W.gens()
84
(
85
[ -1 -E(12)^7 + E(12)^11 1]
86
[ 0 1 0]
87
[ 0 0 1],
88
<BLANKLINE>
89
[ 1 0 0]
90
[-E(12)^7 + E(12)^11 -1 E(20) - E(20)^9]
91
[ 0 0 1],
92
<BLANKLINE>
93
[ 1 0 0]
94
[ 0 1 0]
95
[ 1 E(20) - E(20)^9 -1]
96
)
97
sage: m = matrix([[1,3,3,3], [3,1,3,2], [3,3,1,2], [3,2,2,1]])
98
sage: W = CoxeterGroup(m)
99
sage: W.gens()
100
(
101
[-1 1 1 1] [ 1 0 0 0] [ 1 0 0 0] [ 1 0 0 0]
102
[ 0 1 0 0] [ 1 -1 1 0] [ 0 1 0 0] [ 0 1 0 0]
103
[ 0 0 1 0] [ 0 0 1 0] [ 1 1 -1 0] [ 0 0 1 0]
104
[ 0 0 0 1], [ 0 0 0 1], [ 0 0 0 1], [ 1 0 0 -1]
105
)
106
sage: a,b,c,d = W.gens()
107
sage: (a*b*c)^3
108
[ 5 1 -5 7]
109
[ 5 0 -4 5]
110
[ 4 1 -4 4]
111
[ 0 0 0 1]
112
sage: (a*b)^3
113
[1 0 0 0]
114
[0 1 0 0]
115
[0 0 1 0]
116
[0 0 0 1]
117
sage: b*d == d*b
118
True
119
sage: a*c*a == c*a*c
120
True
121
122
We can create the matrix representation over different base rings and with
123
different index sets. Note that the base ring must contain all
124
`2*\cos(\pi/m_{ij})` where `(m_{ij})_{ij}` is the Coxeter matrix::
125
126
sage: W = CoxeterGroup(m, base_ring=RR, index_set=['a','b','c','d'])
127
sage: W.base_ring()
128
Real Field with 53 bits of precision
129
sage: W.index_set()
130
('a', 'b', 'c', 'd')
131
132
sage: CoxeterGroup(m, base_ring=ZZ)
133
Coxeter group over Integer Ring with Coxeter matrix:
134
[1 3 3 3]
135
[3 1 3 2]
136
[3 3 1 2]
137
[3 2 2 1]
138
sage: CoxeterGroup([[1,4],[4,1]], base_ring=QQ)
139
Traceback (most recent call last):
140
...
141
TypeError: unable to convert sqrt(2) to a rational
142
143
Using the well-known conversion between Coxeter matrices and Coxeter
144
graphs, we can input a Coxeter graph. Following the standard convention,
145
edges with no label (i.e. labelled by ``None``) are treated as 3::
146
147
sage: G = Graph([(0,3,None), (1,3,15), (2,3,7), (0,1,3)])
148
sage: W = CoxeterGroup(G); W
149
Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
150
[ 1 3 2 3]
151
[ 3 1 2 15]
152
[ 2 2 1 7]
153
[ 3 15 7 1]
154
sage: G2 = W.coxeter_graph()
155
sage: CoxeterGroup(G2) is W
156
True
157
158
Because there currently is no class for `\ZZ \cup \{ \infty \}`, labels
159
of `\infty` are given by `-1` in the Coxeter matrix::
160
161
sage: G = Graph([(0,1,None), (1,2,4), (0,2,oo)])
162
sage: W = CoxeterGroup(G)
163
sage: W.coxeter_matrix()
164
[ 1 3 -1]
165
[ 3 1 4]
166
[-1 4 1]
167
168
We can also create Coxeter groups from Cartan types using the
169
``implementation`` keyword::
170
171
sage: W = CoxeterGroup(['D',5], implementation="reflection")
172
sage: W
173
Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
174
[1 3 2 2 2]
175
[3 1 3 2 2]
176
[2 3 1 3 3]
177
[2 2 3 1 2]
178
[2 2 3 2 1]
179
sage: W = CoxeterGroup(['H',3], implementation="reflection")
180
sage: W
181
Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
182
[1 3 2]
183
[3 1 5]
184
[2 5 1]
185
"""
186
@staticmethod
187
def __classcall_private__(cls, data, base_ring=None, index_set=None):
188
"""
189
Normalize arguments to ensure a unique representation.
190
191
EXAMPLES::
192
193
sage: W1 = CoxeterGroup(['A',2], implementation="reflection", base_ring=UniversalCyclotomicField())
194
sage: W2 = CoxeterGroup([[1,3],[3,1]], index_set=(1,2))
195
sage: W1 is W2
196
True
197
sage: G1 = Graph([(1,2)])
198
sage: W3 = CoxeterGroup(G1)
199
sage: W1 is W3
200
True
201
sage: G2 = Graph([(1,2,3)])
202
sage: W4 = CoxeterGroup(G2)
203
sage: W1 is W4
204
True
205
206
Check with `\infty` because of the hack of using `-1` to represent
207
`\infty` in the Coxeter matrix::
208
209
sage: G = Graph([(0, 1, 3), (1, 2, oo)])
210
sage: W1 = CoxeterGroup(matrix([[1, 3, 2], [3,1,-1], [2,-1,1]]))
211
sage: W2 = CoxeterGroup(G)
212
sage: W1 is W2
213
True
214
sage: CoxeterGroup(W1.coxeter_graph()) is W1
215
True
216
"""
217
if isinstance(data, CartanType_abstract):
218
if index_set is None:
219
index_set = data.index_set()
220
data = data.coxeter_matrix()
221
elif isinstance(data, Graph):
222
G = data
223
n = G.num_verts()
224
225
# Setup the basis matrix as all 2 except 1 on the diagonal
226
data = matrix(ZZ, [[2]*n]*n)
227
for i in range(n):
228
data[i, i] = ZZ.one()
229
230
verts = G.vertices()
231
for e in G.edges():
232
m = e[2]
233
if m is None:
234
m = 3
235
elif m == infinity or m == -1: # FIXME: Hack because there is no ZZ\cup\{\infty\}
236
m = -1
237
elif m <= 1:
238
raise ValueError("invalid Coxeter graph label")
239
i = verts.index(e[0])
240
j = verts.index(e[1])
241
data[j, i] = data[i, j] = m
242
243
if index_set is None:
244
index_set = G.vertices()
245
else:
246
try:
247
data = matrix(data)
248
except (ValueError, TypeError):
249
data = CartanType(data).coxeter_matrix()
250
if not data.is_symmetric():
251
raise ValueError("the Coxeter matrix is not symmetric")
252
if any(d != 1 for d in data.diagonal()):
253
raise ValueError("the Coxeter matrix diagonal is not all 1")
254
if any(val <= 1 and val != -1 for i, row in enumerate(data.rows())
255
for val in row[i+1:]):
256
raise ValueError("invalid Coxeter label")
257
258
if index_set is None:
259
index_set = range(data.nrows())
260
261
if base_ring is None:
262
base_ring = UniversalCyclotomicField()
263
data.set_immutable()
264
return super(CoxeterMatrixGroup, cls).__classcall__(cls,
265
data, base_ring, tuple(index_set))
266
267
def __init__(self, coxeter_matrix, base_ring, index_set):
268
"""
269
Initialize ``self``.
270
271
EXAMPLES::
272
273
sage: W = CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]])
274
sage: TestSuite(W).run() # long time
275
sage: W = CoxeterGroup([[1,3,2],[3,1,4],[2,4,1]], base_ring=QQbar)
276
sage: TestSuite(W).run() # long time
277
sage: W = CoxeterGroup([[1,3,2],[3,1,6],[2,6,1]])
278
sage: TestSuite(W).run(max_runs=30) # long time
279
sage: W = CoxeterGroup([[1,3,2],[3,1,-1],[2,-1,1]])
280
sage: TestSuite(W).run(max_runs=30) # long time
281
"""
282
self._matrix = coxeter_matrix
283
self._index_set = index_set
284
n = ZZ(coxeter_matrix.nrows())
285
MS = MatrixSpace(base_ring, n, sparse=True)
286
# FIXME: Hack because there is no ZZ \cup \{ \infty \}: -1 represents \infty
287
if base_ring is UniversalCyclotomicField():
288
val = lambda x: base_ring.gen(2*x) + ~base_ring.gen(2*x) if x != -1 else base_ring(2)
289
else:
290
from sage.functions.trig import cos
291
from sage.symbolic.constants import pi
292
val = lambda x: base_ring(2*cos(pi / x)) if x != -1 else base_ring(2)
293
gens = [MS.one() + MS({(i, j): val(coxeter_matrix[i, j])
294
for j in range(n)})
295
for i in range(n)]
296
FinitelyGeneratedMatrixGroup_generic.__init__(self, n, base_ring,
297
gens,
298
category=CoxeterGroups())
299
300
def _repr_(self):
301
"""
302
Return a string representation of ``self``.
303
304
EXAMPLES::
305
306
sage: CoxeterGroup([[1,3,2],[3,1,3],[2,3,1]])
307
Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
308
[1 3 2]
309
[3 1 3]
310
[2 3 1]
311
"""
312
return "Coxeter group over {} with Coxeter matrix:\n{}".format(self.base_ring(), self._matrix)
313
314
def index_set(self):
315
"""
316
Return the index set of ``self``.
317
318
EXAMPLES::
319
320
sage: W = CoxeterGroup([[1,3],[3,1]])
321
sage: W.index_set()
322
(0, 1)
323
sage: W = CoxeterGroup([[1,3],[3,1]], index_set=['x', 'y'])
324
sage: W.index_set()
325
('x', 'y')
326
sage: W = CoxeterGroup(['H',3])
327
sage: W.index_set()
328
(1, 2, 3)
329
"""
330
return self._index_set
331
332
def coxeter_matrix(self):
333
"""
334
Return the Coxeter matrix of ``self``.
335
336
EXAMPLES::
337
338
sage: W = CoxeterGroup([[1,3],[3,1]])
339
sage: W.coxeter_matrix()
340
[1 3]
341
[3 1]
342
sage: W = CoxeterGroup(['H',3])
343
sage: W.coxeter_matrix()
344
[1 3 2]
345
[3 1 5]
346
[2 5 1]
347
"""
348
return self._matrix
349
350
def coxeter_graph(self):
351
"""
352
Return the Coxeter graph of ``self``.
353
354
EXAMPLES::
355
356
sage: W = CoxeterGroup(['H',3], implementation="reflection")
357
sage: G = W.coxeter_graph(); G
358
Graph on 3 vertices
359
sage: G.edges()
360
[(1, 2, None), (2, 3, 5)]
361
sage: CoxeterGroup(G) is W
362
True
363
sage: G = Graph([(0, 1, 3), (1, 2, oo)])
364
sage: W = CoxeterGroup(G)
365
sage: W.coxeter_graph() == G
366
True
367
sage: CoxeterGroup(W.coxeter_graph()) is W
368
True
369
"""
370
G = Graph()
371
G.add_vertices(self.index_set())
372
for i, row in enumerate(self._matrix.rows()):
373
for j, val in enumerate(row[i+1:]):
374
if val == 3:
375
G.add_edge(self._index_set[i], self._index_set[i+1+j])
376
elif val > 3:
377
G.add_edge(self._index_set[i], self._index_set[i+1+j], val)
378
elif val == -1: # FIXME: Hack because there is no ZZ\cup\{\infty\}
379
G.add_edge(self._index_set[i], self._index_set[i+1+j], infinity)
380
return G
381
382
def simple_reflection(self, i):
383
"""
384
Return the simple reflection `s_i`.
385
386
INPUT:
387
388
- ``i`` -- an element from the index set
389
390
EXAMPLES::
391
392
sage: W = CoxeterGroup(['A',3], implementation="reflection")
393
sage: W.simple_reflection(1)
394
[-1 1 0]
395
[ 0 1 0]
396
[ 0 0 1]
397
sage: W.simple_reflection(2)
398
[ 1 0 0]
399
[ 1 -1 1]
400
[ 0 0 1]
401
sage: W.simple_reflection(3)
402
[ 1 0 0]
403
[ 0 1 0]
404
[ 0 1 -1]
405
"""
406
if not i in self._index_set:
407
raise ValueError("%s is not in the index set %s" % (i, self.index_set()))
408
return self.gen(self._index_set.index(i))
409
410
class Element(MatrixGroupElement_generic):
411
"""
412
A Coxeter group element.
413
"""
414
def has_right_descent(self, i):
415
r"""
416
Return whether ``i`` is a right descent of ``self``.
417
418
A Coxeter system `(W, S)` has a root system defined as
419
`\{ w(\alpha_s) \}_{w \in W}` and we define the positive
420
(resp. negative) roots `\alpha = \sum_{s \in S} c_s \alpha_s`
421
by all `c_s \geq 0` (resp.`c_s \leq 0`). In particular, we note
422
that if `\ell(w s) > \ell(w)` then `w(\alpha_s) > 0` and if
423
`\ell(ws) < \ell(w)` then `w(\alpha_s) < 0`.
424
Thus `i \in I` is a right descent if `w(\alpha_{s_i}) < 0`
425
or equivalently if the matrix representing `w` has all entries
426
of the `i`-th column being non-positive.
427
428
INPUT:
429
430
- ``i`` -- an element in the index set
431
432
EXAMPLES::
433
434
sage: W = CoxeterGroup(['A',3], implementation="reflection")
435
sage: a,b,c = W.gens()
436
sage: elt = b*a*c
437
sage: map(lambda i: elt.has_right_descent(i), [1, 2, 3])
438
[True, False, True]
439
"""
440
i = self.parent()._index_set.index(i)
441
col = self.matrix().column(i)
442
return all(x <= 0 for x in col)
443
444
445