passagemath-coxeter3 10.6.38__cp311-cp311-musllinux_1_2_x86_64.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.
@@ -0,0 +1,717 @@
1
+ # sage_setup: distribution = sagemath-coxeter3
2
+ # sage.doctest: optional - coxeter3
3
+ """
4
+ Coxeter Groups implemented with Coxeter3
5
+ """
6
+ # ****************************************************************************
7
+ # Copyright (C) 2009-2013 Mike Hansen <mhansen@gmail.com>
8
+ #
9
+ # Distributed under the terms of the GNU General Public License (GPL)
10
+ # https://www.gnu.org/licenses/
11
+ # ****************************************************************************
12
+
13
+ from sage.libs.coxeter3.coxeter import get_CoxGroup, CoxGroupElement
14
+ from sage.misc.cachefunc import cached_method
15
+
16
+ from sage.structure.unique_representation import UniqueRepresentation
17
+ from sage.structure.element_wrapper import ElementWrapper
18
+ from sage.structure.richcmp import richcmp
19
+ from sage.categories.coxeter_groups import CoxeterGroups
20
+ from sage.structure.parent import Parent
21
+
22
+ from sage.combinat.root_system.coxeter_matrix import CoxeterMatrix
23
+
24
+ from sage.rings.integer_ring import ZZ
25
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
26
+
27
+
28
+ class CoxeterGroup(UniqueRepresentation, Parent):
29
+ @staticmethod
30
+ def __classcall__(cls, cartan_type, *args, **options):
31
+ """
32
+ TESTS::
33
+
34
+ sage: from sage.libs.coxeter3.coxeter_group import CoxeterGroup
35
+ sage: CoxeterGroup(['B',2])
36
+ Coxeter group of type ['B', 2] implemented by Coxeter3
37
+ sage: CoxeterGroup(CartanType(['B', 3]).relabel({1: 3, 2: 2, 3: 1}))
38
+ Coxeter group of type ['B', 3] relabelled by {1: 3, 2: 2, 3: 1} implemented by Coxeter3
39
+ """
40
+ from sage.combinat.root_system.cartan_type import CartanType
41
+ ct = CartanType(cartan_type)
42
+ return super().__classcall__(cls, ct, *args, **options)
43
+
44
+ def __init__(self, cartan_type):
45
+ """
46
+ TESTS::
47
+
48
+ sage: from sage.libs.coxeter3.coxeter_group import CoxeterGroup
49
+ sage: CoxeterGroup(['A',2])
50
+ Coxeter group of type ['A', 2] implemented by Coxeter3
51
+
52
+ As degrees and codegrees are not implemented, they are skipped in the
53
+ testsuite::
54
+
55
+ sage: to_skip = ['_test_degrees', '_test_codegrees']
56
+ sage: TestSuite(CoxeterGroup(['A',2])).run(skip=to_skip)
57
+ """
58
+ category = CoxeterGroups()
59
+ if cartan_type.is_finite():
60
+ category = category.Finite()
61
+ Parent.__init__(self, category=category)
62
+ self._coxgroup = get_CoxGroup(cartan_type)
63
+ self._cartan_type = cartan_type
64
+
65
+ def _repr_(self):
66
+ """
67
+ EXAMPLES::
68
+
69
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3'); W # indirect doctest
70
+ Coxeter group of type ['A', 3] implemented by Coxeter3
71
+ sage: W = CoxeterGroup(['A', 3, 1], implementation='coxeter3'); W
72
+ Coxeter group of type ['A', 3, 1] implemented by Coxeter3
73
+ """
74
+ return "Coxeter group of type %s implemented by Coxeter3" % (self.cartan_type())
75
+
76
+ def __iter__(self):
77
+ """
78
+ EXAMPLES::
79
+
80
+ sage: W = CoxeterGroup(['A', 2], implementation='coxeter3')
81
+ sage: list(W)
82
+ [[], [1], [2], [1, 2], [2, 1], [1, 2, 1]]
83
+ """
84
+ for x in self._coxgroup:
85
+ yield CoxeterGroup.Element(self, x)
86
+
87
+ def cartan_type(self):
88
+ """
89
+ Return the Cartan type for this Coxeter group.
90
+
91
+ EXAMPLES::
92
+
93
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
94
+ sage: W.cartan_type()
95
+ ['A', 3]
96
+ """
97
+ return self._cartan_type
98
+
99
+ def index_set(self):
100
+ """
101
+ Return the index set for the generators of this Coxeter group.
102
+
103
+ EXAMPLES::
104
+
105
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
106
+ sage: W.index_set()
107
+ (1, 2, 3)
108
+ sage: C = CoxeterGroup(['A', 3,1], implementation='coxeter3')
109
+ sage: C.index_set()
110
+ (0, 1, 2, 3)
111
+ """
112
+ return self.cartan_type().index_set()
113
+
114
+ def bruhat_interval(self, u, v):
115
+ """
116
+ Return the Bruhat interval between ``u`` and ``v``.
117
+
118
+ EXAMPLES::
119
+
120
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
121
+ sage: W.bruhat_interval([1],[3,1,2,3])
122
+ [[1], [1, 2], [1, 3], [1, 2, 3], [1, 3, 2], [1, 2, 3, 2]]
123
+ """
124
+ u, v = self(u), self(v)
125
+ return self._coxgroup.bruhat_interval(u.value, v.value)
126
+
127
+ def cardinality(self):
128
+ """
129
+ Return the cardinality of this Coxeter group.
130
+
131
+ EXAMPLES::
132
+
133
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
134
+ sage: W.cardinality()
135
+ 24
136
+ """
137
+ return self._coxgroup.order()
138
+
139
+ def one(self):
140
+ """
141
+ Return the identity element of this Coxeter group.
142
+
143
+ EXAMPLES::
144
+
145
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
146
+ sage: W.one()
147
+ []
148
+ """
149
+ return self.element_class(self, [])
150
+
151
+ def simple_reflections(self):
152
+ """
153
+ Return the family of generators for this Coxeter group.
154
+
155
+ EXAMPLES::
156
+
157
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
158
+ sage: s = W.simple_reflections()
159
+ sage: s[2]*s[1]*s[2]
160
+ [1, 2, 1]
161
+ """
162
+ from sage.sets.family import Family
163
+ return Family(self.index_set(), lambda i: self.element_class(self, [i]))
164
+
165
+ gens = simple_reflections
166
+
167
+ def from_reduced_word(self, w):
168
+ """
169
+ Return an element of ``self`` from its (reduced) word.
170
+
171
+ EXAMPLES::
172
+
173
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
174
+ sage: W.from_reduced_word([1, 3])
175
+ [1, 3]
176
+ sage: W.from_reduced_word([3, 1])
177
+ [1, 3]
178
+ """
179
+ return self.element_class(self, w)
180
+
181
+ def rank(self):
182
+ """
183
+ Return the rank of this Coxeter group, that is, the number of generators.
184
+
185
+ EXAMPLES::
186
+
187
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
188
+ sage: W.rank()
189
+ 3
190
+ """
191
+ return self._coxgroup.rank()
192
+
193
+ def is_finite(self):
194
+ """
195
+ Return ``True`` if this is a finite Coxeter group.
196
+
197
+ EXAMPLES::
198
+
199
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
200
+ sage: W.is_finite()
201
+ True
202
+ """
203
+ return self._coxgroup.is_finite()
204
+
205
+ def length(self, x):
206
+ """
207
+ Return the length of an element ``x`` in this Coxeter group.
208
+ This is just the length of a reduced word for ``x``.
209
+
210
+ EXAMPLES::
211
+
212
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
213
+ sage: W.length(W([1,2]))
214
+ 2
215
+ sage: W.length(W([1,1]))
216
+ 0
217
+ """
218
+ return x.length()
219
+
220
+ @cached_method
221
+ def coxeter_matrix(self):
222
+ """
223
+ Return the Coxeter matrix for this Coxeter group.
224
+
225
+ The columns and rows are ordered according to the result of
226
+ :meth:`index_set`.
227
+
228
+ EXAMPLES::
229
+
230
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
231
+ sage: m = W.coxeter_matrix(); m
232
+ [1 3 2]
233
+ [3 1 3]
234
+ [2 3 1]
235
+ sage: m.index_set() == W.index_set()
236
+ True
237
+ """
238
+ return CoxeterMatrix(self._coxgroup.coxeter_matrix(), self.index_set())
239
+
240
+ def root_system(self):
241
+ """
242
+ Return the root system associated with this Coxeter group.
243
+
244
+ EXAMPLES::
245
+
246
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
247
+ sage: R = W.root_system(); R
248
+ Root system of type ['A', 3]
249
+ sage: alpha = R.root_space().basis()
250
+ sage: alpha[2] + alpha[3]
251
+ alpha[2] + alpha[3]
252
+ """
253
+ return self.cartan_type().root_system()
254
+
255
+ def _an_element_(self):
256
+ """
257
+ Return an element of this Coxeter group.
258
+
259
+ EXAMPLES::
260
+
261
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
262
+ sage: W._an_element_()
263
+ []
264
+ """
265
+ return self.element_class(self, [])
266
+
267
+ def m(self, i, j):
268
+ r"""
269
+ This is deprecated, use ``self.coxeter_matrix()[i,j]`` instead.
270
+
271
+ TESTS::
272
+
273
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
274
+ sage: W.m(1, 1)
275
+ doctest:warning...:
276
+ DeprecationWarning: the .m(i, j) method has been deprecated; use .coxeter_matrix()[i,j] instead.
277
+ See https://github.com/sagemath/sage/issues/30237 for details.
278
+ 1
279
+ """
280
+ from sage.misc.superseded import deprecation
281
+ deprecation(30237, "the .m(i, j) method has been deprecated; use .coxeter_matrix()[i,j] instead.")
282
+ return self.coxeter_matrix()[i, j]
283
+
284
+ def kazhdan_lusztig_polynomial(self, u, v, constant_term_one=True):
285
+ r"""
286
+ Return the Kazhdan-Lusztig polynomial `P_{u,v}`.
287
+
288
+ INPUT:
289
+
290
+ - ``u``, ``v`` -- elements of the underlying Coxeter group
291
+ - ``constant_term_one`` -- boolean (default: ``True``); ``True`` uses
292
+ the constant equals one convention, ``False`` uses the Leclerc-Thibon
293
+ convention
294
+
295
+ .. SEEALSO::
296
+
297
+ - :class:`~sage.combinat.kazhdan_lusztig.KazhdanLusztigPolynomial`
298
+ - :meth:`parabolic_kazhdan_lusztig_polynomial`
299
+
300
+ EXAMPLES::
301
+
302
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
303
+ sage: W.kazhdan_lusztig_polynomial([], [1,2, 1])
304
+ 1
305
+ sage: W.kazhdan_lusztig_polynomial([1],[3,2])
306
+ 0
307
+ sage: W = CoxeterGroup(['A',3],implementation='coxeter3')
308
+ sage: W.kazhdan_lusztig_polynomial([2],[2,1,3,2])
309
+ q + 1
310
+
311
+ .. NOTE::
312
+
313
+ Coxeter3, as well as Sage's native implementation in
314
+ :class:`~sage.combinat.kazhdan_lusztig.KazhdanLusztigPolynomial`
315
+ use the convention under which Kazhdan-Lusztig
316
+ polynomials give the change of basis from the `(C_w)_{w\in W}`
317
+ basis to the `(T_w)_{w\in W}` of the Hecke algebra of `W` with
318
+ parameters `q` and `q^{-1}`:
319
+
320
+ .. MATH:: C_w = \sum_u P_{u,w} T_u.
321
+
322
+ In particular, `P_{u,u}=1`::
323
+
324
+ sage: all(W.kazhdan_lusztig_polynomial(u,u) == 1 for u in W)
325
+ True
326
+
327
+ This convention differs from Theorem 2.7 in [LT1998]_ by:
328
+
329
+ .. MATH::
330
+
331
+ {}^{LT} P_{y,w}(q) = q^{\ell(w)-\ell(y)} P_{y,w}(q^{-2})
332
+
333
+ To access the Leclerc-Thibon convention use::
334
+
335
+ sage: W = CoxeterGroup(['A',3],implementation='coxeter3')
336
+ sage: W.kazhdan_lusztig_polynomial([2],[2,1,3,2],constant_term_one=False)
337
+ q^3 + q
338
+
339
+ TESTS:
340
+
341
+ We check that Coxeter3 and Sage's implementation give the same results::
342
+
343
+ sage: C = CoxeterGroup(['B', 3], implementation='coxeter3')
344
+ sage: W = WeylGroup("B3",prefix='s')
345
+ sage: [s1,s2,s3] = W.simple_reflections()
346
+ sage: R.<q> = LaurentPolynomialRing(QQ)
347
+ sage: KL = KazhdanLusztigPolynomial(W,q)
348
+ sage: all(KL.P(1,w) == C.kazhdan_lusztig_polynomial([],w.reduced_word()) for w in W) # long (15s)
349
+ True
350
+ """
351
+ u, v = self(u), self(v)
352
+ p = u.value.kazhdan_lusztig_polynomial(v.value)
353
+ if constant_term_one:
354
+ return p
355
+ ZZq = PolynomialRing(ZZ, 'q', sparse=True)
356
+ # This is the same as q**len_diff * p(q**(-2))
357
+ len_diff = v.length() - u.length()
358
+ d = {-2 * deg + len_diff: coeff for deg, coeff in enumerate(p)
359
+ if coeff != 0}
360
+ return ZZq(d)
361
+
362
+ def parabolic_kazhdan_lusztig_polynomial(self, u, v, J, constant_term_one=True):
363
+ r"""
364
+ Return the parabolic Kazhdan-Lusztig polynomial `P_{u,v}^{-,J}`.
365
+
366
+ INPUT:
367
+
368
+ - ``u``, ``v`` -- minimal length coset representatives of `W/W_J` for this Coxeter group `W`
369
+ - ``J`` -- a subset of the index set of ``self`` specifying the parabolic subgroup
370
+
371
+ This method implements the parabolic Kazhdan-Lusztig polynomials
372
+ `P^{-,J}_{u,v}` of [Deo1987b]_, which are defined as
373
+ `P^{-,J}_{u,v} = \sum_{z\in W_J} (-1)^{\ell(z)} P_{yz,w}(q)`
374
+ with the conventions in Sage.
375
+ As for :meth:`kazhdan_lusztig_polynomial` the convention
376
+ differs from Theorem 2.7 in [LT1998]_ by:
377
+
378
+ .. MATH::
379
+
380
+ {}^{LT} P_{y,w}^{-,J}(q) = q^{\ell(w)-\ell(y)} P_{y,w}^{-,J}(q^{-2})
381
+
382
+ EXAMPLES::
383
+
384
+ sage: W = CoxeterGroup(['A',3], implementation='coxeter3')
385
+ sage: W.parabolic_kazhdan_lusztig_polynomial([],[3,2],[1,3])
386
+ 0
387
+ sage: W.parabolic_kazhdan_lusztig_polynomial([2],[2,1,3,2],[1,3])
388
+ q
389
+
390
+ sage: C = CoxeterGroup(['A',3,1], implementation='coxeter3')
391
+ sage: C.parabolic_kazhdan_lusztig_polynomial([],[1],[0])
392
+ 1
393
+ sage: C.parabolic_kazhdan_lusztig_polynomial([],[1,2,1],[0])
394
+ 1
395
+ sage: C.parabolic_kazhdan_lusztig_polynomial([],[0,1,0,1,2,1],[0])
396
+ q
397
+ sage: w=[1, 2, 1, 3, 0, 2, 1, 0, 3, 0, 2]
398
+ sage: v=[1, 2, 1, 3, 0, 1, 2, 1, 0, 3, 0, 2, 1, 0, 3, 0, 2]
399
+ sage: C.parabolic_kazhdan_lusztig_polynomial(w,v,[1,3])
400
+ q^2 + q
401
+ sage: C.parabolic_kazhdan_lusztig_polynomial(w,v,[1,3],constant_term_one=False)
402
+ q^4 + q^2
403
+
404
+ TESTS::
405
+
406
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
407
+ sage: type(W.parabolic_kazhdan_lusztig_polynomial([2],[],[1]))
408
+ <class 'sage.rings.polynomial.polynomial_integer_dense_flint.Polynomial_integer_dense_flint'>
409
+ """
410
+ u = self(u)
411
+ v = self(v)
412
+ if any(d in J for d in u.descents()) or any(d in J for d in v.descents()):
413
+ raise ValueError("u and v have to be minimal coset representatives")
414
+ J_set = set(J)
415
+ WOI = self.weak_order_ideal(lambda x: J_set.issuperset(x.descents()))
416
+ if constant_term_one:
417
+ P = PolynomialRing(ZZ, 'q')
418
+ return P.sum((-1)**(z.length()) * self.kazhdan_lusztig_polynomial(u * z, v)
419
+ for z in WOI if (u * z).bruhat_le(v))
420
+ P = PolynomialRing(ZZ, 'q', sparse=True)
421
+ return P.sum((-1)**(z.length()) * self.kazhdan_lusztig_polynomial(u * z, v, constant_term_one=False).shift(z.length())
422
+ for z in WOI if (u * z).bruhat_le(v))
423
+
424
+ class Element(ElementWrapper):
425
+ wrapped_class = CoxGroupElement
426
+
427
+ def __init__(self, parent, x):
428
+ """
429
+ TESTS::
430
+
431
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
432
+ sage: W([2,1,2])
433
+ [1, 2, 1]
434
+
435
+ Check that :issue:`32266` is fixed::
436
+
437
+ sage: A3 = CoxeterGroup('A3', implementation='coxeter3')
438
+ sage: s1,s2,s3 = A3.simple_reflections()
439
+ sage: s1*s3
440
+ [1, 3]
441
+ sage: s3*s1
442
+ [1, 3]
443
+ sage: s3*s1 == s1*s3
444
+ True
445
+ """
446
+ if not isinstance(x, CoxGroupElement):
447
+ x = CoxGroupElement(parent._coxgroup, x).reduced()
448
+ x = x.normal_form()
449
+ ElementWrapper.__init__(self, parent, x)
450
+
451
+ def __iter__(self):
452
+ """
453
+ Return an iterator for the elements in the reduced word.
454
+
455
+ EXAMPLES::
456
+
457
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
458
+ sage: w = W([1,2,1])
459
+ sage: list(iter(w))
460
+ [1, 2, 1]
461
+ """
462
+ return iter(self.value)
463
+
464
+ def coatoms(self):
465
+ """
466
+ Return the coatoms (or co-covers) of this element in the Bruhat order.
467
+
468
+ EXAMPLES::
469
+
470
+ sage: W = CoxeterGroup(['B', 3], implementation='coxeter3')
471
+ sage: w = W([1,2,3])
472
+ sage: w.coatoms()
473
+ [[2, 3], [3, 1], [1, 2]]
474
+ """
475
+ W = self.parent()
476
+ return [W(w) for w in self.value.coatoms()]
477
+
478
+ def _richcmp_(self, other, op):
479
+ """
480
+ Return lexicographic comparison of ``self`` and ``other``.
481
+
482
+ EXAMPLES::
483
+
484
+ sage: W = CoxeterGroup(['B', 3], implementation='coxeter3')
485
+ sage: w = W([1,2,3])
486
+ sage: v = W([3,1,2])
487
+ sage: v < w
488
+ False
489
+ sage: w < v
490
+ True
491
+
492
+ Some tests for equality::
493
+
494
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
495
+ sage: W([1,2,1]) == W([2,1,2])
496
+ True
497
+ sage: W([1,2,1]) == W([2,1])
498
+ False
499
+ """
500
+ return richcmp(list(self), list(other), op)
501
+
502
+ def reduced_word(self):
503
+ """
504
+ Return the reduced word of ``self``.
505
+
506
+ EXAMPLES::
507
+
508
+ sage: W = CoxeterGroup(['B', 3], implementation='coxeter3')
509
+ sage: w = W([1,2,3])
510
+ sage: w.reduced_word()
511
+ [1, 2, 3]
512
+ """
513
+ return list(self)
514
+
515
+ def __invert__(self):
516
+ """
517
+ Return the inverse of this Coxeter group element.
518
+
519
+ EXAMPLES::
520
+
521
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
522
+ sage: w = W([1,2,3])
523
+ sage: ~w
524
+ [3, 2, 1]
525
+ """
526
+ return self.__class__(self.parent(), ~self.value)
527
+
528
+ inverse = __invert__
529
+
530
+ def __getitem__(self, i):
531
+ """
532
+ EXAMPLES::
533
+
534
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
535
+ sage: w0 = W([1,2,1])
536
+ sage: w0[0]
537
+ 1
538
+ sage: w0[1]
539
+ 2
540
+ """
541
+ # Allow the error message to be raised by the underlying element
542
+ return self.value[i]
543
+
544
+ def _mul_(self, y):
545
+ """
546
+ EXAMPLES::
547
+
548
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
549
+ sage: s = W.gens()
550
+ sage: s[1]._mul_(s[1])
551
+ []
552
+ sage: s[1]*s[2]*s[1]
553
+ [1, 2, 1]
554
+ sage: s[2]*s[1]*s[2]
555
+ [1, 2, 1]
556
+ """
557
+ return self.__class__(self.parent(), self.value * y.value)
558
+
559
+ def __len__(self):
560
+ """
561
+ EXAMPLES::
562
+
563
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
564
+ sage: w = W([1,2,1])
565
+ sage: w.length()
566
+ 3
567
+ sage: len(w)
568
+ 3
569
+ """
570
+ return len(self.value)
571
+
572
+ length = __len__
573
+
574
+ def bruhat_le(self, v):
575
+ r"""
576
+ Return whether ``self`` `\le` ``v`` in Bruhat order.
577
+
578
+ EXAMPLES::
579
+
580
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
581
+ sage: W([]).bruhat_le([1,2,1])
582
+ True
583
+ """
584
+ v = self.parent()(v)
585
+ return self.value.bruhat_le(v.value)
586
+
587
+ def poincare_polynomial(self):
588
+ """
589
+ Return the Poincaré polynomial associated with this element.
590
+
591
+ EXAMPLES::
592
+
593
+ sage: W = CoxeterGroup(['A', 2], implementation='coxeter3')
594
+ sage: W.long_element().poincare_polynomial()
595
+ t^3 + 2*t^2 + 2*t + 1
596
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
597
+ sage: W([2,1,3,2]).poincare_polynomial()
598
+ t^4 + 4*t^3 + 5*t^2 + 3*t + 1
599
+ sage: W([1,2,3,2,1]).poincare_polynomial()
600
+ t^5 + 4*t^4 + 6*t^3 + 5*t^2 + 3*t + 1
601
+ sage: rw = sage.combinat.permutation.from_reduced_word
602
+ sage: p = [w.poincare_polynomial() for w in W]
603
+ sage: [rw(w.reduced_word()) for i,w in enumerate(W) if p[i] != p[i].reverse()]
604
+ [[3, 4, 1, 2], [4, 2, 3, 1]]
605
+ """
606
+ return self.value.poincare_polynomial()
607
+
608
+ def has_right_descent(self, i) -> bool:
609
+ """
610
+ Return whether ``i`` is a right descent of this element.
611
+
612
+ EXAMPLES::
613
+
614
+ sage: W = CoxeterGroup(['A', 4], implementation='coxeter3')
615
+ sage: W([1,2]).has_right_descent(1)
616
+ False
617
+ sage: W([1,2]).has_right_descent(2)
618
+ True
619
+ """
620
+ return i in self.value.right_descents()
621
+
622
+ def has_left_descent(self, i) -> bool:
623
+ """
624
+ Return ``True`` if ``i`` is a left descent of this element.
625
+
626
+ EXAMPLES::
627
+
628
+ sage: W = CoxeterGroup(['A', 4], implementation='coxeter3')
629
+ sage: W([1,2]).has_left_descent(1)
630
+ True
631
+ sage: W([1,2]).has_left_descent(2)
632
+ False
633
+ """
634
+ return i in self.value.left_descents()
635
+
636
+ def action(self, v):
637
+ """
638
+ Return the action of this Coxeter group element on the root space.
639
+
640
+ INPUT:
641
+
642
+ - ``v`` -- an element of the root space associated with the Coxeter group for ``self``
643
+
644
+ EXAMPLES::
645
+
646
+ sage: W = CoxeterGroup(['B', 3], implementation='coxeter3')
647
+ sage: R = W.root_system().root_space()
648
+ sage: v = R.an_element(); v
649
+ 2*alpha[1] + 2*alpha[2] + 3*alpha[3]
650
+ sage: w = W([1,2,3])
651
+ sage: w.action(v)
652
+ -alpha[1] + alpha[2] + alpha[3]
653
+ """
654
+ # TODO: Find a better way to do this
655
+ W = self.parent().root_system().root_space().weyl_group()
656
+ w = W.from_reduced_word(list(self))
657
+ return w.action(v)
658
+
659
+ def action_on_rational_function(self, f):
660
+ r"""
661
+ Return the natural action of this Coxeter group element on a
662
+ polynomial considered as an element of `S(\mathfrak{h}^*)`.
663
+
664
+ .. NOTE::
665
+
666
+ Note that the number of variables in the polynomial
667
+ ring must correspond to the rank of this Coxeter
668
+ group. The ordering of the variables is assumed to
669
+ coincide with the result of :meth:`index_set`.
670
+
671
+ EXAMPLES::
672
+
673
+ sage: W = CoxeterGroup(['A', 3], implementation='coxeter3')
674
+ sage: S = PolynomialRing(QQ, 'x,y,z').fraction_field()
675
+ sage: x,y,z = S.gens()
676
+ sage: W([1]).action_on_rational_function(x+y+z)
677
+ (x^2*y + x*z + 1)/x
678
+ sage: W([2]).action_on_rational_function(x+y+z)
679
+ (x*y^2 + y^2*z + 1)/y
680
+ sage: W([3]).action_on_rational_function(x+y+z)
681
+ (y*z^2 + x*z + 1)/z
682
+ """
683
+ Q = f.parent()
684
+ Q_gens = Q.gens()
685
+ W = self.parent()
686
+ R = W.root_system().root_space()
687
+ alpha = R.basis()
688
+ n = W.rank()
689
+
690
+ if Q.ngens() != n:
691
+ raise ValueError("the number of generators for the polynomial "
692
+ "ring must be the same as the rank of the "
693
+ "root system")
694
+
695
+ basis_elements = [alpha[i] for i in W.index_set()]
696
+ basis_to_order = {s: i for i, s in enumerate(W.index_set())}
697
+
698
+ results = []
699
+ for poly in [f.numerator(), f.denominator()]:
700
+ result = 0
701
+ exponents = poly.exponents()
702
+
703
+ for exponent in exponents:
704
+ # Construct something in the root lattice from the exponent vector
705
+ exponent = sum(e * b for e, b in zip(exponent, basis_elements))
706
+ exponent = self.action(exponent)
707
+
708
+ monomial = 1
709
+ for s, c in exponent.monomial_coefficients().items():
710
+ monomial *= Q_gens[basis_to_order[s]]**int(c)
711
+
712
+ result += monomial
713
+
714
+ results.append(result)
715
+
716
+ numerator, denominator = results
717
+ return numerator / denominator