Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/categories/coxeter_groups.py
4054 views
1
r"""
2
Coxeter Groups
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2009 Nicolas M. Thiery <nthiery at users.sf.net>
6
#
7
# Distributed under the terms of the GNU General Public License (GPL)
8
# http://www.gnu.org/licenses/
9
#******************************************************************************
10
# With contributions from Dan Bump, Steve Pon, Qiang Wang, Anne Schilling
11
12
from sage.misc.cachefunc import cached_method, cached_in_parent_method
13
from sage.misc.abstract_method import abstract_method
14
from sage.misc.constant_function import ConstantFunction
15
from sage.misc.misc import attrcall
16
from sage.categories.category_singleton import Category_singleton
17
from sage.categories.groups import Groups
18
from sage.categories.enumerated_sets import EnumeratedSets
19
from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
20
from sage.structure.sage_object import have_same_parent
21
from sage.combinat.finite_class import FiniteCombinatorialClass
22
from sage.misc.flatten import flatten
23
24
class CoxeterGroups(Category_singleton):
25
r"""
26
The category of Coxeter groups.
27
28
A *Coxeter group* is a group `W` with a distinguished (finite)
29
family of involutions `(s_i)_{i\in I}`, called the *simple
30
reflections*, subject to relations of the form `(s_is_j)^{m_{i,j}} = 1`.
31
32
`I` is the *index set* of `W` and `|I|` is the *rank* of `W`.
33
34
See http://en.wikipedia.org/wiki/Coxeter_group for details.
35
36
EXAMPLES::
37
38
sage: C = CoxeterGroups()
39
sage: C # todo: uppercase for Coxeter
40
Category of coxeter groups
41
sage: C.super_categories()
42
[Category of groups, Category of enumerated sets]
43
44
sage: W = C.example(); W
45
The symmetric group on {0, ..., 3}
46
47
sage: W.simple_reflections()
48
Finite family {0: (1, 0, 2, 3), 1: (0, 2, 1, 3), 2: (0, 1, 3, 2)}
49
50
Here are some further examples::
51
52
sage: FiniteCoxeterGroups().example()
53
The 5-th dihedral group of order 10
54
sage: FiniteWeylGroups().example()
55
The symmetric group on {0, ..., 3}
56
sage: WeylGroup(["B", 3])
57
Weyl Group of type ['B', 3] (as a matrix group acting on the ambient space)
58
59
Those will eventually be also in this category::
60
61
sage: SymmetricGroup(4)
62
Symmetric group of order 4! as a permutation group
63
sage: DihedralGroup(5)
64
Dihedral group of order 10 as a permutation group
65
66
TODO: add a demo of usual computations on Coxeter groups.
67
68
SEE ALSO: :class:`WeylGroups`, :mod:`sage.combinat.root_system`
69
70
TESTS::
71
72
sage: W = CoxeterGroups().example(); TestSuite(W).run(verbose = "True")
73
running ._test_an_element() . . . pass
74
running ._test_associativity() . . . pass
75
running ._test_category() . . . pass
76
running ._test_elements() . . .
77
Running the test suite of self.an_element()
78
running ._test_category() . . . pass
79
running ._test_eq() . . . pass
80
running ._test_not_implemented_methods() . . . pass
81
running ._test_pickling() . . . pass
82
pass
83
running ._test_elements_eq() . . . pass
84
running ._test_enumerated_set_contains() . . . pass
85
running ._test_enumerated_set_iter_cardinality() . . . pass
86
running ._test_enumerated_set_iter_list() . . . pass
87
running ._test_eq() . . . pass
88
running ._test_has_descent() . . . pass
89
running ._test_inverse() . . . pass
90
running ._test_not_implemented_methods() . . . pass
91
running ._test_one() . . . pass
92
running ._test_pickling() . . . pass
93
running ._test_prod() . . . pass
94
running ._test_reduced_word() . . . pass
95
running ._test_simple_projections() . . . pass
96
running ._test_some_elements() . . . pass
97
"""
98
99
def super_categories(self):
100
"""
101
EXAMPLES::
102
103
sage: CoxeterGroups().super_categories()
104
[Category of groups, Category of enumerated sets]
105
"""
106
return [Groups(), EnumeratedSets()]
107
108
class ParentMethods:
109
110
@abstract_method
111
def index_set(self):
112
"""
113
Returns the index set of (the simple reflections of)
114
``self``, as a list (or iterable).
115
116
EXAMPLES::
117
118
sage: W = FiniteCoxeterGroups().example(); W
119
The 5-th dihedral group of order 10
120
sage: W.index_set()
121
[1, 2]
122
"""
123
# return self.simple_reflections().keys()
124
125
def _an_element_(self):
126
"""
127
Implements: :meth:`Sets.ParentMethods.an_element` by
128
returning the product of the simple reflections (a Coxeter
129
element).
130
131
EXAMPLES::
132
133
sage: W=CoxeterGroups().example()
134
sage: W
135
The symmetric group on {0, ..., 3}
136
sage: W.an_element() # indirect doctest
137
(1, 2, 3, 0)
138
139
"""
140
return self.prod(self.simple_reflections())
141
142
def some_elements(self):
143
"""
144
Implements :meth:`Sets.ParentMethods.some_elements` by
145
returning some typical element of `self`.
146
147
EXAMPLES::
148
149
sage: W=WeylGroup(['A',3])
150
sage: W.some_elements()
151
[[0 1 0 0]
152
[1 0 0 0]
153
[0 0 1 0]
154
[0 0 0 1],
155
[1 0 0 0]
156
[0 0 1 0]
157
[0 1 0 0]
158
[0 0 0 1],
159
[1 0 0 0]
160
[0 1 0 0]
161
[0 0 0 1]
162
[0 0 1 0],
163
[1 0 0 0]
164
[0 1 0 0]
165
[0 0 1 0]
166
[0 0 0 1],
167
[0 0 0 1]
168
[1 0 0 0]
169
[0 1 0 0]
170
[0 0 1 0]]
171
sage: W.order()
172
24
173
"""
174
return list(self.simple_reflections()) + [ self.one(), self.an_element() ]
175
176
def __iter__(self):
177
r"""
178
Returns an iterator over the elements of this Coxeter group.
179
180
EXAMPLES::
181
182
sage: D5 = FiniteCoxeterGroups().example(5)
183
sage: sorted(list(D5)) # indirect doctest (but see :meth:`._test_enumerated_set_iter_list`)
184
[(),
185
(1,),
186
(1, 2),
187
(1, 2, 1),
188
(1, 2, 1, 2),
189
(1, 2, 1, 2, 1),
190
(2,),
191
(2, 1),
192
(2, 1, 2),
193
(2, 1, 2, 1)]
194
195
sage: W = WeylGroup(["A",2,1])
196
sage: g = iter(W)
197
sage: g.next()
198
[1 0 0]
199
[0 1 0]
200
[0 0 1]
201
sage: g.next()
202
[-1 1 1]
203
[ 0 1 0]
204
[ 0 0 1]
205
sage: g.next()
206
[ 0 -1 2]
207
[ 1 -1 1]
208
[ 0 0 1]
209
"""
210
return iter(self.weak_order_ideal(predicate = ConstantFunction(True)))
211
212
def weak_order_ideal(self, predicate, side ="right", category = None):
213
"""
214
Returns a weak order ideal defined by a predicate
215
216
INPUT:
217
218
- ``predicate``: a predicate on the elements of ``self`` defining an
219
weak order ideal in ``self``
220
- ``side``: "left" or "right" (default: "right")
221
222
OUTPUT: an enumerated set
223
224
EXAMPLES::
225
226
sage: D6 = FiniteCoxeterGroups().example(5)
227
sage: I = D6.weak_order_ideal(predicate = lambda w: w.length() <= 3)
228
sage: I.cardinality()
229
7
230
sage: list(I)
231
[(), (1,), (1, 2), (1, 2, 1), (2,), (2, 1), (2, 1, 2)]
232
233
We now consider an infinite Coxeter group::
234
235
sage: W = WeylGroup(["A",1,1])
236
sage: I = W.weak_order_ideal(predicate = lambda w: w.length() <= 2)
237
sage: list(iter(I))
238
[[1 0]
239
[0 1],
240
[-1 2]
241
[ 0 1],
242
[ 3 -2]
243
[ 2 -1],
244
[ 1 0]
245
[ 2 -1],
246
[-1 2]
247
[-2 3]]
248
249
Even when the result is finite, some features of
250
:class:`FiniteEnumeratedSets` are not available::
251
252
sage: I.cardinality() # todo: not implemented
253
5
254
sage: list(I) # todo: not implemented
255
256
unless this finiteness is explicitly specified::
257
258
sage: I = W.weak_order_ideal(predicate = lambda w: w.length() <= 2,
259
... category = FiniteEnumeratedSets())
260
sage: I.cardinality()
261
5
262
sage: list(I)
263
[[1 0]
264
[0 1],
265
[-1 2]
266
[ 0 1],
267
[ 3 -2]
268
[ 2 -1],
269
[ 1 0]
270
[ 2 -1],
271
[-1 2]
272
[-2 3]]
273
274
.. rubric:: Background
275
276
The weak order is returned as a :class:`SearchForest`.
277
This is achieved by assigning to each element `u1` of the
278
ideal a single ancestor `u=u1 s_i`, where `i` is the
279
smallest descent of `u`.
280
281
This allows for iterating through the elements in
282
roughly Constant Amortized Time and constant memory
283
(taking the operations and size of the generated objects
284
as constants).
285
"""
286
from sage.combinat.backtrack import SearchForest
287
def succ(u):
288
for i in u.descents(positive = True, side = side):
289
u1 = u.apply_simple_reflection(i, side)
290
if i == u1.first_descent(side = side) and predicate(u1):
291
yield u1
292
return
293
from sage.categories.finite_coxeter_groups import FiniteCoxeterGroups
294
default_category = FiniteEnumeratedSets() if self in FiniteCoxeterGroups() else EnumeratedSets()
295
return SearchForest((self.one(),), succ, category = default_category.or_subcategory(category))
296
297
def grassmannian_elements(self, side = "right"):
298
"""
299
INPUT:
300
301
- ``side``: "left" or "right" (default: "right")
302
303
Returns the left or right grassmanian elements of self, as an enumerated set
304
305
EXAMPLES::
306
307
sage: S = CoxeterGroups().example()
308
sage: G = S.grassmannian_elements()
309
sage: G.cardinality()
310
12
311
sage: G.list()
312
[(0, 1, 2, 3), (1, 0, 2, 3), (2, 0, 1, 3), (3, 0, 1, 2), (0, 2, 1, 3), (1, 2, 0, 3), (0, 3, 1, 2), (1, 3, 0, 2), (2, 3, 0, 1), (0, 1, 3, 2), (0, 2, 3, 1), (1, 2, 3, 0)]
313
sage: sorted(tuple(w.descents()) for w in G)
314
[(), (0,), (0,), (0,), (1,), (1,), (1,), (1,), (1,), (2,), (2,), (2,)]
315
sage: G = S.grassmannian_elements(side = "left")
316
sage: G.cardinality()
317
12
318
sage: sorted(tuple(w.descents(side = "left")) for w in G)
319
[(), (0,), (0,), (0,), (1,), (1,), (1,), (1,), (1,), (2,), (2,), (2,)]
320
"""
321
order_side = "left" if side == "right" else "right"
322
return self.weak_order_ideal(attrcall("is_grassmannian", side = side), side = order_side)
323
324
def from_reduced_word(self, word):
325
r"""
326
INPUT:
327
328
- ``word`` - a list (or iterable) of elements of ``self.index_set()``
329
330
Returns the group element corresponding to the given
331
word. Namely, if ``word`` is `[i_1,i_2,\ldots,i_k]`, then
332
this returns the corresponding product of simple
333
reflections `s_{i_1} s_{i_2} \cdots s_{i_k}`.
334
335
Note: the main use case is for constructing elements from
336
reduced words, hence the name of this method. But actually
337
the input word need *not* be reduced.
338
339
EXAMPLES::
340
341
sage: W = CoxeterGroups().example()
342
sage: W
343
The symmetric group on {0, ..., 3}
344
sage: s = W.simple_reflections()
345
sage: W.from_reduced_word([0,2,0,1])
346
(0, 3, 1, 2)
347
sage: W.from_reduced_word((0,2,0,1))
348
(0, 3, 1, 2)
349
sage: s[0]*s[2]*s[0]*s[1]
350
(0, 3, 1, 2)
351
352
See also :meth:'._test_reduced_word'::
353
354
sage: W._test_reduced_word()
355
356
TESTS::
357
358
sage: W=WeylGroup(['E',6])
359
sage: W.from_reduced_word([2,3,4,2])
360
[ 0 1 0 0 0 0 0 0]
361
[ 0 0 -1 0 0 0 0 0]
362
[-1 0 0 0 0 0 0 0]
363
[ 0 0 0 1 0 0 0 0]
364
[ 0 0 0 0 1 0 0 0]
365
[ 0 0 0 0 0 1 0 0]
366
[ 0 0 0 0 0 0 1 0]
367
[ 0 0 0 0 0 0 0 1]
368
369
"""
370
return self.one().apply_simple_reflections(word, side = 'right')
371
372
def _test_reduced_word(self, **options):
373
"""
374
Runs sanity checks on :meth:'CoxeterGroups.ElementMethods.reduced_word' and
375
:meth:'.from_reduced_word`.
376
377
EXAMPLES::
378
379
sage: W = CoxeterGroups().example()
380
sage: W._test_reduced_word()
381
382
"""
383
tester = self._tester(**options)
384
s = self.simple_reflections()
385
for x in tester.some_elements():
386
red = x.reduced_word()
387
tester.assertEquals(self.from_reduced_word(red), x)
388
tester.assertEquals(self.prod((s[i] for i in red)), x)
389
390
def simple_reflection(self, i):
391
"""
392
INPUT:
393
394
- ``i`` - an element from the index set.
395
396
Returns the simple reflection `s_i`
397
398
EXAMPLES::
399
400
sage: W = CoxeterGroups().example()
401
sage: W
402
The symmetric group on {0, ..., 3}
403
sage: W.simple_reflection(1)
404
(0, 2, 1, 3)
405
sage: s = W.simple_reflections()
406
sage: s[1]
407
(0, 2, 1, 3)
408
409
"""
410
assert(i in self.index_set())
411
return self.one().apply_simple_reflection(i) # don't care about left/right
412
413
@cached_method
414
def simple_reflections(self):
415
r"""
416
Returns the simple reflections `(s_i)_{i\in I}`, as a family.
417
418
EXAMPLES::
419
420
sage: W = CoxeterGroups().example()
421
sage: W
422
The symmetric group on {0, ..., 3}
423
sage: s = W.simple_reflections()
424
sage: s
425
Finite family {0: (1, 0, 2, 3), 1: (0, 2, 1, 3), 2: (0, 1, 3, 2)}
426
sage: s[0]
427
(1, 0, 2, 3)
428
sage: s[1]
429
(0, 2, 1, 3)
430
sage: s[2]
431
(0, 1, 3, 2)
432
433
434
This default implementation uses :meth:`.index_set` and
435
:meth:`.simple_reflection`.
436
"""
437
from sage.sets.family import Family
438
return Family(self.index_set(), self.simple_reflection)
439
440
def group_generators(self):
441
r"""
442
Implements :meth:`Groups.ParentMethods.group_generators`
443
by returning the simple reflections of ``self``.
444
445
EXAMPLES::
446
447
sage: D10 = FiniteCoxeterGroups().example(10)
448
sage: D10.group_generators()
449
Finite family {1: (1,), 2: (2,)}
450
sage: SymmetricGroup(5).group_generators()
451
Finite family {1: (1,2), 2: (2,3), 3: (3,4), 4: (4,5)}
452
453
Those give semigroup generators, even for an infinite group::
454
455
sage: W = WeylGroup(["A",2,1])
456
sage: W.semigroup_generators()
457
Finite family {0: [-1 1 1]
458
[ 0 1 0]
459
[ 0 0 1],
460
1: [ 1 0 0]
461
[ 1 -1 1]
462
[ 0 0 1],
463
2: [ 1 0 0]
464
[ 0 1 0]
465
[ 1 1 -1]}
466
"""
467
return self.simple_reflections()
468
469
semigroup_generators = group_generators
470
471
def simple_projection(self, i, side = 'right', toward_max = True):
472
r"""
473
INPUT:
474
475
- ``i`` - an element of the index set of self
476
477
Returns the simple projection `\pi_i` (or `\overline\pi_i` if toward_max is False).
478
479
See :meth:`.simple_projections` for the options. and for
480
the definition of the simple projections.
481
482
EXAMPLES::
483
484
sage: W = CoxeterGroups().example()
485
sage: W
486
The symmetric group on {0, ..., 3}
487
sage: s = W.simple_reflections()
488
sage: sigma=W.an_element()
489
sage: sigma
490
(1, 2, 3, 0)
491
sage: u0=W.simple_projection(0)
492
sage: d0=W.simple_projection(0,toward_max=False)
493
sage: sigma.length()
494
3
495
sage: pi=sigma*s[0]
496
sage: pi.length()
497
4
498
sage: u0(sigma)
499
(2, 1, 3, 0)
500
sage: pi
501
(2, 1, 3, 0)
502
sage: u0(pi)
503
(2, 1, 3, 0)
504
sage: d0(sigma)
505
(1, 2, 3, 0)
506
sage: d0(pi)
507
(1, 2, 3, 0)
508
509
"""
510
assert(i in self.index_set() or i==0) # i==0 allows for affine descents
511
return lambda x: x.apply_simple_projection(i, side = side, toward_max = toward_max) # should use default_keyword
512
513
@cached_method
514
def simple_projections(self, side = 'right', toward_max = True):
515
r"""
516
INPUT:
517
518
- ``self`` - a Coxeter group `W`
519
- ``side`` - 'left' or 'right' (default: 'right')
520
- ``toward_max`` - a boolean (default: True) specifying
521
the direction of the projection
522
523
Returns the simple projections of `W`, as a family.
524
525
To each simple reflection `s_i` of `W`, corresponds a
526
*simple projection* `\pi_i` from `W` to `W` defined by:
527
528
`\pi_i(w) = w s_i` if `i` is not a descent of `w`
529
`\pi_i(w) = w` otherwise.
530
531
The simple projections `(\pi_i)_{i\in I}` move elements
532
down the right permutohedron, toward the maximal element.
533
They satisfy the same relations as the simple reflections,
534
except that `\pi_i^2=\pi`, whereas `s_i^2 = s_i`. As such,
535
the simple projections generate the `0`-Hecke monoid.
536
537
By symmetry, one can also define the projections
538
`(\overline\pi_i)_{i\in I}` (option ``toward_max``):
539
540
`\overline\pi_i(w) = w s_i` if `i` is a descent of `w`
541
`\overline\pi_i(w) = w` otherwise.
542
543
as well as the analogues acting on the left (option ``side``).
544
545
TODO: the name `toward_max` is not explicit and
546
should/will be replaced; suggestions welcome.
547
548
EXAMPLES::
549
550
sage: W = CoxeterGroups().example()
551
sage: W
552
The symmetric group on {0, ..., 3}
553
sage: s = W.simple_reflections()
554
sage: sigma=W.an_element()
555
sage: sigma
556
(1, 2, 3, 0)
557
sage: pi=W.simple_projections()
558
sage: pi
559
Finite family {0: <function <lambda> at ...>, 1: <function <lambda> at ...>, 2: <function <lambda> ...>}
560
sage: pi[1](sigma)
561
(1, 3, 2, 0)
562
sage: W.simple_projection(1)(sigma)
563
(1, 3, 2, 0)
564
"""
565
from sage.sets.family import Family
566
return Family(self.index_set(), lambda i: self.simple_projection(i, side = side, toward_max = toward_max))
567
568
def bruhat_interval(self, x, y):
569
"""
570
Returns the list of t such that x <= t <= y.
571
572
EXAMPLES::
573
574
sage: W = WeylGroup("A3", prefix="s")
575
sage: [s1,s2,s3]=W.simple_reflections()
576
sage: W.bruhat_interval(s2,s1*s3*s2*s1*s3)
577
[s1*s2*s3*s2*s1, s2*s3*s2*s1, s3*s1*s2*s1, s1*s2*s3*s1, s1*s2*s3*s2, s3*s2*s1, s2*s3*s1, s2*s3*s2, s1*s2*s1, s3*s1*s2, s1*s2*s3, s2*s1, s3*s2, s2*s3, s1*s2, s2]
578
sage: W = WeylGroup(['A',2,1], prefix="s")
579
sage: [s0,s1,s2]=W.simple_reflections()
580
sage: W.bruhat_interval(1,s0*s1*s2)
581
[s0*s1*s2, s1*s2, s0*s2, s0*s1, s2, s1, s0, 1]
582
"""
583
if x == 1:
584
x = self.one()
585
if y == 1:
586
y = self.one()
587
if x == y:
588
return [x]
589
ret = []
590
if not x.bruhat_le(y):
591
return ret
592
ret.append([y])
593
while ret[-1] != []:
594
nextlayer = []
595
for z in ret[-1]:
596
for t in z.bruhat_lower_covers():
597
if t not in nextlayer:
598
if x.bruhat_le(t):
599
nextlayer.append(t)
600
ret.append(nextlayer)
601
return flatten(ret)
602
603
# TODO: Groups() should have inverse() call __invert__
604
# With strong doc stating that this is just a convenience for the user
605
# and links to ~ / __invert__
606
607
# parabolic_subgroup
608
609
def _test_simple_projections(self, **options):
610
"""
611
Runs sanity checks on :meth:`.simple_projections`
612
and :meth:`CoxeterGroups.ElementMethods.apply_simple_projection`
613
614
EXAMPLES::
615
616
sage: W = CoxeterGroups().example()
617
sage: W._test_simple_projections()
618
"""
619
tester = self._tester(**options)
620
for side in ['left', 'right']:
621
pi = self.simple_projections(side = side)
622
opi = self.simple_projections(side = side, toward_max = False)
623
for i in self.index_set():
624
for w in tester.some_elements():
625
tester.assert_( pi[i](w) == w.apply_simple_projection(i, side = side))
626
tester.assert_( pi[i](w) == w.apply_simple_projection(i, side = side, toward_max = True ))
627
tester.assert_(opi[i](w) == w.apply_simple_projection(i, side = side, toward_max = False))
628
tester.assert_( pi[i](w).has_descent(i, side = side))
629
tester.assert_(not opi[i](w).has_descent(i, side = side))
630
tester.assertEquals(set([pi[i](w), opi[i](w)]),
631
set([w, w.apply_simple_reflection(i, side = side)]))
632
633
634
def _test_has_descent(self, **options):
635
"""
636
Runs sanity checks on the method
637
:meth:`CoxeterGroups.ElementMethods.has_descent` of the
638
elements of self.
639
640
EXAMPLES::
641
642
sage: W = CoxeterGroups().example()
643
sage: W._test_has_descent()
644
"""
645
tester = self._tester(**options)
646
s = self.simple_reflections()
647
for i in self.index_set():
648
tester.assert_(not self.one().has_descent(i))
649
tester.assert_(not self.one().has_descent(i, side = 'left'))
650
tester.assert_(not self.one().has_descent(i, side = 'right'))
651
tester.assert_(self.one().has_descent(i, positive = True))
652
tester.assert_(self.one().has_descent(i, positive = True, side = 'left'))
653
tester.assert_(self.one().has_descent(i, positive = True, side = 'right'))
654
for j in self.index_set():
655
tester.assertEquals(s[i].has_descent(j, side = 'left' ), i==j)
656
tester.assertEquals(s[i].has_descent(j, side = 'right'), i==j)
657
tester.assertEquals(s[i].has_descent(j ), i==j)
658
tester.assertEquals(s[i].has_descent(j, positive = True, side = 'left' ), i!=j)
659
tester.assertEquals(s[i].has_descent(j, positive = True, side = 'right'), i!=j)
660
tester.assertEquals(s[i].has_descent(j, positive = True, ), i!=j)
661
if i == j:
662
continue
663
u = s[i] * s[j]
664
v = s[j] * s[i]
665
tester.assert_((s[i]*s[j]).has_descent(i, side = 'left' ))
666
tester.assert_((s[i]*s[j]).has_descent(j, side = 'right'))
667
tester.assertEquals((s[i]*s[j]).has_descent(j, side = 'left' ), u == v)
668
tester.assertEquals((s[i]*s[j]).has_descent(i, side = 'right'), u == v)
669
670
class ElementMethods:
671
def has_descent(self, i, side = 'right', positive=False):
672
"""
673
Returns whether i is a (left/right) descent of self.
674
675
See :meth:`.descents` for a description of the options.
676
677
EXAMPLES::
678
679
sage: W = CoxeterGroups().example()
680
sage: s = W.simple_reflections()
681
sage: w = s[0] * s[1] * s[2]
682
sage: w.has_descent(2)
683
True
684
sage: [ w.has_descent(i) for i in [0,1,2] ]
685
[False, False, True]
686
sage: [ w.has_descent(i, side = 'left') for i in [0,1,2] ]
687
[True, False, False]
688
sage: [ w.has_descent(i, positive = True) for i in [0,1,2] ]
689
[True, True, False]
690
691
This default implementation delegates the work to
692
:meth:`.has_left_descent` and :meth:`.has_right_descent`.
693
"""
694
assert isinstance(positive, bool)
695
if side == 'right':
696
return self.has_right_descent(i) != positive
697
else:
698
assert side == 'left'
699
return self.has_left_descent(i) != positive
700
701
@abstract_method(optional = True)
702
def has_right_descent(self, i):
703
"""
704
Returns whether ``i`` is a right descent of self.
705
706
EXAMPLES::
707
708
sage: W = CoxeterGroups().example(); W
709
The symmetric group on {0, ..., 3}
710
sage: w = W.an_element(); w
711
(1, 2, 3, 0)
712
sage: w.has_right_descent(0)
713
False
714
sage: w.has_right_descent(1)
715
False
716
sage: w.has_right_descent(2)
717
True
718
"""
719
720
def has_left_descent(self, i):
721
"""
722
Returns whether `i` is a left descent of self.
723
724
This default implementation uses that a left descent of
725
`w` is a right descent of `w^{-1}`.
726
727
EXAMPLES::
728
729
sage: W = CoxeterGroups().example(); W
730
The symmetric group on {0, ..., 3}
731
sage: w = W.an_element(); w
732
(1, 2, 3, 0)
733
sage: w.has_left_descent(0)
734
True
735
sage: w.has_left_descent(1)
736
False
737
sage: w.has_left_descent(2)
738
False
739
740
TESTS::
741
742
sage: w.has_left_descent.__module__
743
'sage.categories.coxeter_groups'
744
"""
745
return (~self).has_right_descent(i)
746
747
def first_descent(self, side = 'right', index_set=None, positive=False):
748
"""
749
Returns the first left (resp. right) descent of self, as
750
ane element of ``index_set``, or ``None`` if there is none.
751
752
See :meth:`.descents` for a description of the options.
753
754
EXAMPLES::
755
756
sage: W = CoxeterGroups().example()
757
sage: s = W.simple_reflections()
758
sage: w = s[2]*s[0]
759
sage: w.first_descent()
760
0
761
sage: w = s[0]*s[2]
762
sage: w.first_descent()
763
0
764
sage: w = s[0]*s[1]
765
sage: w.first_descent()
766
1
767
"""
768
if index_set is None:
769
index_set = self.parent().index_set()
770
for i in index_set:
771
if self.has_descent(i, side = side, positive = positive):
772
return i
773
return None
774
775
776
def descents(self, side = 'right', index_set=None, positive=False):
777
"""
778
INPUT:
779
780
- ``index_set`` - a subset (as a list or iterable) of the nodes of the dynkin diagram;
781
(default: all of them)
782
- ``side`` - 'left' or 'right' (default: 'right')
783
- ``positive`` - a boolean (default: ``False``)
784
785
Returns the descents of self, as a list of elements of the
786
index_set.
787
788
The ``index_set`` option can be used to restrict to the
789
parabolic subgroup indexed by ``index_set``.
790
791
If positive is ``True``, then returns the non-descents
792
instead
793
794
TODO: find a better name for ``positive``: complement? non_descent?
795
796
Caveat: the return type may change to some other iterable
797
(tuple, ...) in the future. Please use keyword arguments
798
also, as the order of the arguments may change as well.
799
800
EXAMPLES::
801
802
sage: W=CoxeterGroups().example()
803
sage: s=W.simple_reflections()
804
sage: w=s[0]*s[1]
805
sage: w.descents()
806
[1]
807
sage: w=s[0]*s[2]
808
sage: w.descents()
809
[0, 2]
810
811
TODO: side, index_set, positive
812
"""
813
if index_set==None:
814
index_set=self.parent().index_set()
815
return [ i for i in index_set if self.has_descent(i, side = side, positive = positive) ]
816
817
def is_grassmannian(self, side = "right"):
818
"""
819
INPUT:
820
821
- ``side`` - "left" or "right" (default: "right")
822
823
Tests whether ``self`` is Grassmannian, i.e. it has at
824
most one descent on the right (resp. on the left).
825
826
EXAMPLES::
827
828
sage: W = CoxeterGroups().example(); W
829
The symmetric group on {0, ..., 3}
830
sage: s = W.simple_reflections()
831
sage: W.one().is_grassmannian()
832
True
833
sage: s[1].is_grassmannian()
834
True
835
sage: (s[1]*s[2]).is_grassmannian()
836
True
837
sage: (s[0]*s[1]).is_grassmannian()
838
True
839
sage: (s[1]*s[2]*s[1]).is_grassmannian()
840
False
841
842
sage: (s[0]*s[2]*s[1]).is_grassmannian(side = "left")
843
False
844
sage: (s[0]*s[2]*s[1]).is_grassmannian(side = "right")
845
True
846
sage: (s[0]*s[2]*s[1]).is_grassmannian()
847
True
848
"""
849
return len(self.descents(side = side)) <= 1
850
851
def reduced_word_reverse_iterator(self):
852
"""
853
Returns a reverse iterator on a reduced word for self.
854
855
EXAMPLES::
856
857
sage: W=CoxeterGroups().example()
858
sage: s = W.simple_reflections()
859
sage: sigma = s[0]*s[1]*s[2]
860
sage: rI=sigma.reduced_word_reverse_iterator()
861
sage: [i for i in rI]
862
[2, 1, 0]
863
sage: s[0]*s[1]*s[2]==sigma
864
True
865
sage: sigma.length()
866
3
867
868
SEE ALSO :meth:`.reduced_word`
869
870
Default implementation: recursively remove the first right
871
descent until the identity is reached (see :meth:`.first_descent` and
872
:meth:`apply_simple_reflection`).
873
874
"""
875
while True:
876
i = self.first_descent()
877
if i is None:
878
return
879
self = self.apply_simple_reflection(i, 'right')
880
yield i
881
882
def reduced_word(self):
883
r"""
884
Returns a reduced word for self.
885
886
This is a word `[i_1,i_2,\ldots,i_k]` of minimal length
887
such that `s_{i_1} s_{i_2} \cdots s_{i_k}=self`, where `s`
888
are the simple reflections.
889
890
EXAMPLES::
891
892
sage: W=CoxeterGroups().example()
893
sage: s=W.simple_reflections()
894
sage: w=s[0]*s[1]*s[2]
895
sage: w.reduced_word()
896
[0, 1, 2]
897
sage: w=s[0]*s[2]
898
sage: w.reduced_word()
899
[2, 0]
900
901
SEE ALSO: :meth:`.reduced_words`, :meth:`.reduced_word_reverse_iterator`, :meth:`length`
902
903
"""
904
result = list(self.reduced_word_reverse_iterator())
905
return list(reversed(result))
906
907
#def lex_min_reduced_word(w):
908
# return list(reversed((w.inverse()).reduced_word()))
909
910
def reduced_words(self):
911
r"""
912
Returns all reduced words for self.
913
914
EXAMPLES::
915
916
sage: W=CoxeterGroups().example()
917
sage: s=W.simple_reflections()
918
sage: w=s[0]*s[2]
919
sage: w.reduced_words()
920
[[2, 0], [0, 2]]
921
sage: W=WeylGroup(['E',6])
922
sage: w=W.from_reduced_word([2,3,4,2])
923
sage: w.reduced_words()
924
[[3, 2, 4, 2], [2, 3, 4, 2], [3, 4, 2, 4]]
925
926
TODO: the result should be full featured finite enumerated
927
set (e.g. counting can be done much faster than iterating).
928
"""
929
descents = self.descents()
930
if descents == []:
931
return [[]]
932
else:
933
return [ r + [i]
934
for i in self.descents()
935
for r in (self.apply_simple_reflection(i)).reduced_words()
936
]
937
938
def length(self):
939
r"""
940
Returns the length of self, that is the minimal length of
941
a product of simple reflections giving self.
942
943
EXAMPLES::
944
945
sage: W = CoxeterGroups().example()
946
sage: s1 = W.simple_reflection(1)
947
sage: s2 = W.simple_reflection(2)
948
sage: s1.length()
949
1
950
sage: (s1*s2).length()
951
2
952
sage: W = CoxeterGroups().example()
953
sage: s = W.simple_reflections()
954
sage: w = s[0]*s[1]*s[0]
955
sage: w.length()
956
3
957
sage: W = CoxeterGroups().example()
958
sage: sum((x^w.length()) for w in W) - expand(prod(sum(x^i for i in range(j+1)) for j in range(4))) # This is scandalously slow!!!
959
0
960
961
SEE ALSO: :meth:`.reduced_word`
962
963
TODO: Should use reduced_word_iterator (or reverse_iterator)
964
965
"""
966
return len(self.reduced_word())
967
968
def coset_representative(self, index_set, side = 'right'):
969
r"""
970
INPUT:
971
972
- ``index_set`` - a subset (or iterable) of the nodes of the dynkin diagram
973
- ``side`` - 'left' or 'right'
974
975
Returns the unique shortest element of the coxeter group
976
$W$ which is in the same left (resp. right) coset as
977
``self``, with respect to the parabolic subgroup $W_I$.
978
979
EXAMPLES::
980
sage: W = CoxeterGroups().example(5)
981
sage: s = W.simple_reflections()
982
sage: w = s[2]*s[1]*s[3]
983
sage: w.coset_representative([]).reduced_word()
984
[2, 3, 1]
985
sage: w.coset_representative([1]).reduced_word()
986
[2, 3]
987
sage: w.coset_representative([1,2]).reduced_word()
988
[2, 3]
989
sage: w.coset_representative([1,3] ).reduced_word()
990
[2]
991
sage: w.coset_representative([2,3] ).reduced_word()
992
[2, 1]
993
sage: w.coset_representative([1,2,3] ).reduced_word()
994
[]
995
sage: w.coset_representative([], side = 'left').reduced_word()
996
[2, 3, 1]
997
sage: w.coset_representative([1], side = 'left').reduced_word()
998
[2, 3, 1]
999
sage: w.coset_representative([1,2], side = 'left').reduced_word()
1000
[3]
1001
sage: w.coset_representative([1,3], side = 'left').reduced_word()
1002
[2, 3, 1]
1003
sage: w.coset_representative([2,3], side = 'left').reduced_word()
1004
[1]
1005
sage: w.coset_representative([1,2,3], side = 'left').reduced_word()
1006
[]
1007
1008
"""
1009
while True:
1010
i = self.first_descent(side = side, index_set = index_set)
1011
if i is None:
1012
return self
1013
self = self.apply_simple_reflection(i, side = side)
1014
1015
def apply_simple_projection(self, i, side = 'right', toward_max = True):
1016
r"""
1017
INPUT:
1018
1019
- ``i`` - an element of the index set of the Coxeter group
1020
- ``side`` - 'left' or 'right' (default: 'right')
1021
- ``toward_max`` - a boolean (default: True) specifying
1022
the direction of the projection
1023
1024
Returns the result of the application of the simple
1025
projection `\pi_i` (resp. `\overline\pi_i`) on self.
1026
1027
See :meth:`CoxeterGroups.ParentMethods.simple_projections`
1028
for the definition of the simple projections.
1029
1030
EXAMPLE::
1031
1032
sage: W=CoxeterGroups().example()
1033
sage: w=W.an_element()
1034
sage: w
1035
(1, 2, 3, 0)
1036
sage: w.apply_simple_projection(2)
1037
(1, 2, 3, 0)
1038
sage: w.apply_simple_projection(2, toward_max=False)
1039
(1, 2, 0, 3)
1040
1041
"""
1042
if self.has_descent(i, side = side) is not toward_max:
1043
return self.apply_simple_reflection(i, side = side)
1044
else:
1045
return self
1046
1047
def binary_factorizations(self, predicate = ConstantFunction(True)):
1048
"""
1049
Returns the set of all the factorizations `self = u v` such
1050
that `l(self) = l(u) + l(v)`.
1051
1052
Iterating through this set is Constant Amortized Time
1053
(counting arithmetic operations in the coxeter group as
1054
constant time) complexity, and memory linear in the length
1055
of `self`.
1056
1057
One can pass as optional argument a predicate p such that
1058
`p(u)` implies `p(u')` for any `u` left factor of `self`
1059
and `u'` left factor of `u`. Then this returns only the
1060
factorizations `self = uv` such `p(u)` holds.
1061
1062
EXAMPLES:
1063
1064
We construct the set of all factorizations of the maximal
1065
element of the group::
1066
1067
sage: W = WeylGroup(['A',3])
1068
sage: s = W.simple_reflections()
1069
sage: w0 = W.from_reduced_word([1,2,3,1,2,1])
1070
sage: w0.binary_factorizations().cardinality()
1071
24
1072
1073
The same number of factorizations, by bounded length::
1074
1075
sage: [w0.binary_factorizations(lambda u: u.length() <= l).cardinality() for l in [-1,0,1,2,3,4,5,6]]
1076
[0, 1, 4, 9, 15, 20, 23, 24]
1077
1078
The number of factorizations of the elements just below
1079
the maximal element::
1080
1081
sage: [(s[i]*w0).binary_factorizations().cardinality() for i in [1,2,3]]
1082
[12, 12, 12]
1083
sage: w0.binary_factorizations(lambda u: False).cardinality()
1084
0
1085
1086
TESTS::
1087
1088
sage: w0.binary_factorizations().category()
1089
Category of finite enumerated sets
1090
"""
1091
from sage.combinat.backtrack import SearchForest
1092
W = self.parent()
1093
if not predicate(W.one()):
1094
return FiniteCombinatorialClass([])
1095
s = W.simple_reflections()
1096
def succ((u,v)):
1097
for i in v.descents(side = 'left'):
1098
u1 = u * s[i]
1099
if i == u1.first_descent() and predicate(u1):
1100
yield (u1, s[i]*v)
1101
return SearchForest(((W.one(), self),), succ, category = FiniteEnumeratedSets())
1102
1103
# TODO: standardize / cleanup
1104
def apply_simple_reflections(self, word, side = 'right'):
1105
"""
1106
INPUT:
1107
1108
- ``word`` -- A sequence of indices of Coxeter generators
1109
- ``side`` -- Indicates multiplying from left or right
1110
1111
Returns the result of the (left/right) multiplication of
1112
word to self. ``self`` is not changed.
1113
1114
EXAMPLES::
1115
1116
sage: W=CoxeterGroups().example()
1117
sage: w=W.an_element(); w
1118
(1, 2, 3, 0)
1119
sage: w.apply_simple_reflections([0,1])
1120
(2, 3, 1, 0)
1121
sage: w
1122
(1, 2, 3, 0)
1123
sage: w.apply_simple_reflections([0,1],side='left')
1124
(0, 1, 3, 2)
1125
"""
1126
for i in word:
1127
self = self.apply_simple_reflection(i, side)
1128
return self
1129
1130
1131
def apply_simple_reflection_left(self, i):
1132
"""
1133
Returns ``self`` multiplied by the simple reflection ``s[i]`` on the left
1134
1135
This low level method is used intensively. Coxeter groups
1136
are encouraged to override this straightforward
1137
implementation whenever a faster approach exists.
1138
1139
EXAMPLES::
1140
1141
sage: W=CoxeterGroups().example()
1142
sage: w = W.an_element(); w
1143
(1, 2, 3, 0)
1144
sage: w.apply_simple_reflection_left(0)
1145
(0, 2, 3, 1)
1146
sage: w.apply_simple_reflection_left(1)
1147
(2, 1, 3, 0)
1148
sage: w.apply_simple_reflection_left(2)
1149
(1, 3, 2, 0)
1150
1151
TESTS::
1152
1153
sage: w.apply_simple_reflection_left.__module__
1154
'sage.categories.coxeter_groups'
1155
"""
1156
s = self.parent().simple_reflections()
1157
return s[i] * self
1158
1159
def apply_simple_reflection_right(self, i):
1160
"""
1161
Returns ``self`` multiplied by the simple reflection ``s[i]`` on the right
1162
1163
This low level method is used intensively. Coxeter groups
1164
are encouraged to override this straightforward
1165
implementation whenever a faster approach exists.
1166
1167
EXAMPLES::
1168
1169
sage: W=CoxeterGroups().example()
1170
sage: w = W.an_element(); w
1171
(1, 2, 3, 0)
1172
sage: w.apply_simple_reflection_right(0)
1173
(2, 1, 3, 0)
1174
sage: w.apply_simple_reflection_right(1)
1175
(1, 3, 2, 0)
1176
sage: w.apply_simple_reflection_right(2)
1177
(1, 2, 0, 3)
1178
1179
TESTS::
1180
1181
sage: w.apply_simple_reflection_right.__module__
1182
'sage.categories.coxeter_groups'
1183
"""
1184
s = self.parent().simple_reflections()
1185
return self * s[i]
1186
1187
def apply_simple_reflection(self, i, side = 'right'):
1188
"""
1189
Returns ``self`` multiplied by the simple reflection ``s[i]``
1190
1191
INPUT:
1192
1193
- ``i`` -- an element of the index set
1194
- ``side`` -- "left" or "right" (default: "right")
1195
1196
This default implementation simply calls
1197
:meth:`apply_simple_reflection_left` or
1198
:meth:`apply_simple_reflection_right`.
1199
1200
EXAMPLES::
1201
1202
sage: W=CoxeterGroups().example()
1203
sage: w = W.an_element(); w
1204
(1, 2, 3, 0)
1205
sage: w.apply_simple_reflection(0, side = "left")
1206
(0, 2, 3, 1)
1207
sage: w.apply_simple_reflection(1, side = "left")
1208
(2, 1, 3, 0)
1209
sage: w.apply_simple_reflection(2, side = "left")
1210
(1, 3, 2, 0)
1211
1212
sage: w.apply_simple_reflection(0, side = "right")
1213
(2, 1, 3, 0)
1214
sage: w.apply_simple_reflection(1, side = "right")
1215
(1, 3, 2, 0)
1216
sage: w.apply_simple_reflection(2, side = "right")
1217
(1, 2, 0, 3)
1218
1219
By default, ``side`` is "right"::
1220
1221
sage: w.apply_simple_reflection(0)
1222
(2, 1, 3, 0)
1223
1224
TESTS::
1225
1226
sage: w.apply_simple_reflection_right.__module__
1227
'sage.categories.coxeter_groups'
1228
"""
1229
if side == 'right':
1230
return self.apply_simple_reflection_right(i)
1231
else:
1232
return self.apply_simple_reflection_left(i)
1233
1234
def _mul_(self, other):
1235
r"""
1236
Returns the product of ``self`` and ``other``
1237
1238
This default implementation computes a reduced word of
1239
``other`` using :meth:`reduced_word`, and applies the
1240
corresponding simple reflections on ``self`` using
1241
:meth:`apply_simple_reflections`.
1242
1243
EXAMPLES::
1244
1245
sage: W = FiniteCoxeterGroups().example(); W
1246
The 5-th dihedral group of order 10
1247
sage: w = W.an_element()
1248
sage: w
1249
(1, 2)
1250
sage: w._mul_(w)
1251
(1, 2, 1, 2)
1252
sage: w._mul_(w)._mul_(w)
1253
(2, 1, 2, 1)
1254
1255
This method is called when computing ``self*other``::
1256
1257
sage: w * w
1258
(1, 2, 1, 2)
1259
1260
TESTS::
1261
1262
sage: w._mul_.__module__
1263
'sage.categories.coxeter_groups'
1264
"""
1265
return self.apply_simple_reflections(other.reduced_word())
1266
1267
def inverse(self):
1268
"""
1269
Returns the inverse of self
1270
1271
EXAMPLES::
1272
1273
sage: W=WeylGroup(['B',7])
1274
sage: w=W.an_element()
1275
sage: u=w.inverse()
1276
sage: u==~w
1277
True
1278
sage: u*w==w*u
1279
True
1280
sage: u*w
1281
[1 0 0 0 0 0 0]
1282
[0 1 0 0 0 0 0]
1283
[0 0 1 0 0 0 0]
1284
[0 0 0 1 0 0 0]
1285
[0 0 0 0 1 0 0]
1286
[0 0 0 0 0 1 0]
1287
[0 0 0 0 0 0 1]
1288
1289
"""
1290
1291
return self.parent().one().apply_simple_reflections(self.reduced_word_reverse_iterator())
1292
1293
__invert__ = inverse
1294
1295
@cached_in_parent_method
1296
def bruhat_lower_covers(self):
1297
"""
1298
Returns all elements that ``self`` covers in (strong) Bruhat order.
1299
1300
If ``w = self`` has a descent at `i`, then the elements that
1301
`w` covers are exactly `\{ws_i, u_1s_i, u_2s_i,..., u_js_i\}`,
1302
where the `u_k` are elements that `ws_i` covers that also
1303
do not have a descent at `i`.
1304
1305
EXAMPLES::
1306
1307
sage: W = WeylGroup(["A",3])
1308
sage: w = W.from_reduced_word([3,2,3])
1309
sage: print([v.reduced_word() for v in w.bruhat_lower_covers()])
1310
[[3, 2], [2, 3]]
1311
1312
sage: W = WeylGroup(["A",3])
1313
sage: print([v.reduced_word() for v in W.simple_reflection(1).bruhat_lower_covers()])
1314
[[]]
1315
sage: print([v.reduced_word() for v in W.one().bruhat_lower_covers()])
1316
[]
1317
sage: W = WeylGroup(["B",4,1])
1318
sage: w = W.from_reduced_word([0,2])
1319
sage: print([v.reduced_word() for v in w.bruhat_lower_covers()])
1320
[[2], [0]]
1321
1322
We now show how to construct the Bruhat poset::
1323
1324
sage: W = WeylGroup(["A",3])
1325
sage: covers = tuple([u, v] for v in W for u in v.bruhat_lower_covers() )
1326
sage: P = Poset((W, covers), cover_relations = True)
1327
sage: P.show()
1328
1329
Alternatively, one can just use::
1330
1331
sage: P = W.bruhat_poset()
1332
1333
The algorithm is taken from Stembridge's coxeter/weyl package for Maple.
1334
"""
1335
desc = self.first_descent()
1336
if desc is not None:
1337
ww = self.apply_simple_reflection(desc)
1338
return [u.apply_simple_reflection(desc) for u in ww.bruhat_lower_covers() if not u.has_descent(desc)] + [ww]
1339
else:
1340
return []
1341
1342
@cached_in_parent_method
1343
def bruhat_upper_covers(self):
1344
r"""
1345
Returns all elements that cover ``self`` in (strong) Bruhat order.
1346
1347
The algorithm works recursively, using the 'inverse' of the method described for
1348
lower covers :meth:`bruhat_lower_covers`. Namely, it runs through all `i` in the
1349
index set: if `w`=``self`` has no right descent `i`, then `w s_i` is a cover;
1350
if `w` has a decent at `i`, then `u_j s_i` is a cover of `w` where `u_j` is a cover
1351
of `w s_i`.
1352
1353
EXAMPLES::
1354
1355
sage: W = WeylGroup(['A',3,1])
1356
sage: w = W.from_reduced_word([1,2,1])
1357
sage: sorted([x.reduced_word() for x in w.bruhat_upper_covers()])
1358
[[0, 1, 2, 1], [1, 2, 0, 1], [1, 2, 1, 0], [1, 2, 3, 1], [2, 3, 1, 2], [3, 1, 2, 1]]
1359
1360
sage: W = WeylGroup(['A',3])
1361
sage: w = W.long_element()
1362
sage: w.bruhat_upper_covers()
1363
[]
1364
1365
sage: W = WeylGroup(['A',3])
1366
sage: w = W.from_reduced_word([1,2,1])
1367
sage: S = [v for v in W if w in v.bruhat_lower_covers()]
1368
sage: C = w.bruhat_upper_covers()
1369
sage: set(S) == set(C)
1370
True
1371
"""
1372
Covers = []
1373
for i in self.parent().index_set():
1374
if i in self.descents():
1375
Covers += [ x.apply_simple_reflection(i) for x in self.apply_simple_reflection(i).bruhat_upper_covers()
1376
if i not in x.descents() ]
1377
else:
1378
Covers += [ self.apply_simple_reflection(i) ]
1379
return [x for x in set(Covers)]
1380
1381
@cached_in_parent_method
1382
def bruhat_le(self, other):
1383
"""
1384
Bruhat comparison
1385
1386
INPUT:
1387
1388
- other - an element of the same Coxeter group
1389
1390
OUTPUT: a boolean
1391
1392
Returns whether ``self`` <= ``other`` in the Bruhat order.
1393
1394
EXAMPLES::
1395
1396
sage: W = WeylGroup(["A",3])
1397
sage: u = W.from_reduced_word([1,2,1])
1398
sage: v = W.from_reduced_word([1,2,3,2,1])
1399
sage: u.bruhat_le(u)
1400
True
1401
sage: u.bruhat_le(v)
1402
True
1403
sage: v.bruhat_le(u)
1404
False
1405
sage: v.bruhat_le(v)
1406
True
1407
sage: s = W.simple_reflections()
1408
sage: s[1].bruhat_le(W.one())
1409
False
1410
1411
The implementation uses the equivalent condition that any
1412
reduced word for ``other`` contains a reduced word for
1413
``self`` as subword. See Stembridge, A short derivation of
1414
the Mobius function for the Bruhat order. J. Algebraic
1415
Combin. 25 (2007), no. 2, 141--148, Proposition 1.1.
1416
1417
Complexity: `O(l * c)`, where `l` is the minimum of the
1418
lengths of `u` and of `v`, and `c` is the cost of the low
1419
level methods :meth:`first_descent`, :meth:`has_descent`,
1420
:meth:`apply_simple_reflection`, etc. Those are typically
1421
`O(n)`, where `n` is the rank of the Coxeter group.
1422
1423
TESTS:
1424
1425
We now run consistency tests with permutations and
1426
:meth:`bruhat_lower_covers`::
1427
1428
sage: W = WeylGroup(["A",3])
1429
sage: P4 = Permutations(4)
1430
sage: def P4toW(w): return W.from_reduced_word(w.reduced_word())
1431
sage: for u in P4:
1432
... for v in P4:
1433
... assert u.bruhat_lequal(v) == P4toW(u).bruhat_le(P4toW(v))
1434
1435
sage: W = WeylGroup(["B",3])
1436
sage: P = W.bruhat_poset() # This is built from bruhat_lower_covers
1437
sage: Q = Poset((W, attrcall("bruhat_le"))) # long time (10s)
1438
sage: all( u.bruhat_le(v) == (P(u) <= P(v)) for u in W for v in W ) # long time (7s)
1439
True
1440
sage: all((P(u) <= P(v)) == (Q(u) <= Q(v)) for u in W for v in W) # long time (9s)
1441
True
1442
1443
"""
1444
assert have_same_parent(self, other)
1445
# could first compare the length, when that information is cheap
1446
desc = other.first_descent()
1447
if desc is not None:
1448
return self.apply_simple_projection(desc, toward_max = False).bruhat_le(other.apply_simple_reflection(desc))
1449
else:
1450
return self == other
1451
1452
def weak_le(self, other, side = 'right'):
1453
"""
1454
comparison in weak order
1455
1456
INPUT:
1457
1458
- other - an element of the same Coxeter group
1459
- side - 'left' or 'right' (default: 'right')
1460
1461
OUTPUT: a boolean
1462
1463
Returns whether ``self`` <= ``other`` in left
1464
(resp. right) weak order, that is if 'v' can be obtained
1465
from 'v' by length increasing multiplication by simple
1466
reflections on the left (resp. right).
1467
1468
EXAMPLES::
1469
1470
sage: W = WeylGroup(["A",3])
1471
sage: u = W.from_reduced_word([1,2])
1472
sage: v = W.from_reduced_word([1,2,3,2])
1473
sage: u.weak_le(u)
1474
True
1475
sage: u.weak_le(v)
1476
True
1477
sage: v.weak_le(u)
1478
False
1479
sage: v.weak_le(v)
1480
True
1481
1482
Comparison for left weak order is achieved with the option ``side``::
1483
1484
sage: u.weak_le(v, side = 'left')
1485
False
1486
1487
The implementation uses the equivalent condition that any
1488
reduced word for `u` is a right (resp. left) prefix of
1489
some reduced word for `v`.
1490
1491
Complexity: `O(l * c)`, where `l` is the minimum of the
1492
lengths of `u` and of `v`, and `c` is the cost of the low
1493
level methods :meth:`first_descent`, :meth:`has_descent`,
1494
:meth:`apply_simple_reflection`. Those are typically
1495
`O(n)`, where `n` is the rank of the Coxeter group.
1496
1497
We now run consistency tests with permutations::
1498
1499
sage: W = WeylGroup(["A",3])
1500
sage: P4 = Permutations(4)
1501
sage: def P4toW(w): return W.from_reduced_word(w.reduced_word())
1502
sage: for u in P4: # long time (5s on sage.math, 2011)
1503
... for v in P4:
1504
... assert u.permutohedron_lequal(v) == P4toW(u).weak_le(P4toW(v))
1505
... assert u.permutohedron_lequal(v, side='left') == P4toW(u).weak_le(P4toW(v), side='left')
1506
"""
1507
assert have_same_parent(self, other)
1508
# could first compare the length, when that information is cheap
1509
prefix_side = 'left' if side == 'right' else 'right'
1510
1511
while True:
1512
desc = self.first_descent(side = prefix_side)
1513
if desc is None:
1514
return True
1515
if not other.has_descent(desc, side = prefix_side):
1516
return False
1517
self = self.apply_simple_reflection(desc, side = prefix_side)
1518
other = other.apply_simple_reflection(desc, side = prefix_side)
1519
1520
def weak_covers(self, side = 'right', index_set = None, positive = False):
1521
"""
1522
Returns all elements that ``self`` covers in weak order.
1523
1524
INPUT:
1525
1526
- side - 'left' or 'right' (default: 'right')
1527
- positive - a boolean (default: False)
1528
- index_set - a list of indices or None
1529
1530
OUTPUT: a list
1531
1532
EXAMPLES::
1533
1534
sage: W = WeylGroup(['A',3])
1535
sage: w = W.from_reduced_word([3,2,1])
1536
sage: [x.reduced_word() for x in w.weak_covers()]
1537
[[3, 2]]
1538
1539
To obtain instead elements that cover self, set ``positive = True``::
1540
1541
sage: [x.reduced_word() for x in w.weak_covers(positive = True)]
1542
[[3, 1, 2, 1], [2, 3, 2, 1]]
1543
1544
To obtain covers for left weak order, set the option side to 'left'::
1545
1546
sage: [x.reduced_word() for x in w.weak_covers(side='left')]
1547
[[2, 1]]
1548
sage: w = W.from_reduced_word([3,2,3,1])
1549
sage: [x.reduced_word() for x in w.weak_covers()]
1550
[[2, 3, 2], [3, 2, 1]]
1551
sage: [x.reduced_word() for x in w.weak_covers(side='left')]
1552
[[3, 2, 1], [2, 3, 1]]
1553
1554
Covers w.r.t. a parabolic subgroup are obtained with the option ``index_set``::
1555
1556
sage: [x.reduced_word() for x in w.weak_covers(index_set = [1,2])]
1557
[[2, 3, 2]]
1558
"""
1559
return [ self.apply_simple_reflection(i, side=side)
1560
for i in self.descents(side=side, index_set = index_set, positive = positive) ]
1561
1562
1563
def lower_covers(self, side = 'right', index_set = None):
1564
"""
1565
Returns all elements that ``self`` covers in weak order.
1566
1567
INPUT:
1568
1569
- side - 'left' or 'right' (default: 'right')
1570
- index_set - a list of indices or None
1571
1572
OUTPUT: a list
1573
1574
EXAMPLES::
1575
1576
sage: W = WeylGroup(['A',3])
1577
sage: w = W.from_reduced_word([3,2,1])
1578
sage: [x.reduced_word() for x in w.lower_covers()]
1579
[[3, 2]]
1580
1581
To obtain covers for left weak order, set the option side to 'left'::
1582
1583
sage: [x.reduced_word() for x in w.lower_covers(side='left')]
1584
[[2, 1]]
1585
sage: w = W.from_reduced_word([3,2,3,1])
1586
sage: [x.reduced_word() for x in w.lower_covers()]
1587
[[2, 3, 2], [3, 2, 1]]
1588
1589
Covers w.r.t. a parabolic subgroup are obtained with the option ``index_set``::
1590
1591
sage: [x.reduced_word() for x in w.lower_covers(index_set = [1,2])]
1592
[[2, 3, 2]]
1593
sage: [x.reduced_word() for x in w.lower_covers(side='left')]
1594
[[3, 2, 1], [2, 3, 1]]
1595
"""
1596
return self.weak_covers(side = side, index_set = index_set, positive = False)
1597
1598
def upper_covers(self, side = 'right', index_set = None):
1599
"""
1600
Returns all elements that cover ``self`` in weak order.
1601
1602
INPUT:
1603
1604
- side - 'left' or 'right' (default: 'right')
1605
- index_set - a list of indices or None
1606
1607
OUTPUT: a list
1608
1609
EXAMPLES::
1610
1611
sage: W = WeylGroup(['A',3])
1612
sage: w = W.from_reduced_word([2,3])
1613
sage: [x.reduced_word() for x in w.upper_covers()]
1614
[[2, 3, 1], [2, 3, 2]]
1615
1616
To obtain covers for left weak order, set the option ``side`` to 'left'::
1617
1618
sage: [x.reduced_word() for x in w.upper_covers(side = 'left')]
1619
[[1, 2, 3], [2, 3, 2]]
1620
1621
Covers w.r.t. a parabolic subgroup are obtained with the option ``index_set``::
1622
1623
sage: [x.reduced_word() for x in w.upper_covers(index_set = [1])]
1624
[[2, 3, 1]]
1625
sage: [x.reduced_word() for x in w.upper_covers(side = 'left', index_set = [1])]
1626
[[1, 2, 3]]
1627
"""
1628
return self.weak_covers(side = side, index_set = index_set, positive = True)
1629
1630
1631