Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/algebras/shuffle_algebra.py
8818 views
1
# -*- coding: utf-8 -*-
2
r"""
3
Shuffle algebras
4
5
AUTHORS:
6
7
- Frédéric Chapoton (2013-03): Initial version
8
- Matthieu Deneufchatel (2013-07): Implemented dual PBW basis
9
"""
10
11
#*****************************************************************************
12
# Copyright (C) 2013 Frédéric Chapoton <chapoton-math-univ-lyon1-fr>
13
#
14
# Distributed under the terms of the GNU General Public License (GPL)
15
# http://www.gnu.org/licenses/
16
#*****************************************************************************
17
18
from sage.categories.rings import Rings
19
from sage.categories.algebras_with_basis import AlgebrasWithBasis
20
from sage.categories.commutative_algebras import CommutativeAlgebras
21
from sage.categories.coalgebras_with_basis import CoalgebrasWithBasis
22
from sage.categories.tensor import TensorProductsCategory, tensor
23
from sage.combinat.free_module import CombinatorialFreeModule
24
from sage.combinat.words.alphabet import Alphabet
25
from sage.combinat.words.words import Words
26
from sage.combinat.words.word import Word
27
from sage.combinat.words.finite_word import FiniteWord_class
28
from sage.misc.cachefunc import cached_method
29
from sage.misc.lazy_attribute import lazy_attribute
30
from sage.misc.misc_c import prod
31
from sage.sets.family import Family
32
33
class ShuffleAlgebra(CombinatorialFreeModule):
34
r"""
35
The shuffle algebra on some generators over a base ring.
36
37
Shuffle algebras are commutative and associative algebras, with a
38
basis indexed by words. The product of two words `w_1 \cdot w_2` is given
39
by the sum over the shuffle product of `w_1` and `w_2`.
40
41
.. SEEALSO::
42
43
For more on shuffle products, see
44
:mod:`~sage.combinat.words.shuffle_product` and
45
:meth:`~sage.combinat.words.finite_word.FiniteWord_class.shuffle()`.
46
47
REFERENCES:
48
49
- :wikipedia:`Shuffle algebra`
50
51
INPUT:
52
53
- ``R`` -- ring
54
55
- ``names`` -- generator names (string or an alphabet)
56
57
EXAMPLES::
58
59
sage: F = ShuffleAlgebra(QQ, 'xyz'); F
60
Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field
61
62
sage: mul(F.gens())
63
B[word: xyz] + B[word: xzy] + B[word: yxz] + B[word: yzx] + B[word: zxy] + B[word: zyx]
64
65
sage: mul([ F.gen(i) for i in range(2) ]) + mul([ F.gen(i+1) for i in range(2) ])
66
B[word: xy] + B[word: yx] + B[word: yz] + B[word: zy]
67
68
sage: S = ShuffleAlgebra(ZZ, 'abcabc'); S
69
Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring
70
sage: S.base_ring()
71
Integer Ring
72
73
sage: G = ShuffleAlgebra(S, 'mn'); G
74
Shuffle Algebra on 2 generators ['m', 'n'] over Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring
75
sage: G.base_ring()
76
Shuffle Algebra on 3 generators ['a', 'b', 'c'] over Integer Ring
77
78
Shuffle algebras commute with their base ring::
79
80
sage: K = ShuffleAlgebra(QQ,'ab')
81
sage: a,b = K.gens()
82
sage: K.is_commutative()
83
True
84
sage: L = ShuffleAlgebra(K,'cd')
85
sage: c,d = L.gens()
86
sage: L.is_commutative()
87
True
88
sage: s = a*b^2 * c^3; s
89
(12*B[word:abb]+12*B[word:bab]+12*B[word:bba])*B[word: ccc]
90
sage: parent(s)
91
Shuffle Algebra on 2 generators ['c', 'd'] over Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field
92
sage: c^3 * a * b^2
93
(12*B[word:abb]+12*B[word:bab]+12*B[word:bba])*B[word: ccc]
94
95
Shuffle algebras are commutative::
96
97
sage: c^3 * b * a * b == c * a * c * b^2 * c
98
True
99
100
We can also manipulate elements in the basis and coerce elements from our
101
base field::
102
103
sage: F = ShuffleAlgebra(QQ, 'abc')
104
sage: B = F.basis()
105
sage: B[Word('bb')] * B[Word('ca')]
106
B[word: bbca] + B[word: bcab] + B[word: bcba] + B[word: cabb] + B[word: cbab] + B[word: cbba]
107
sage: 1 - B[Word('bb')] * B[Word('ca')] / 2
108
B[word: ] - 1/2*B[word: bbca] - 1/2*B[word: bcab] - 1/2*B[word: bcba] - 1/2*B[word: cabb] - 1/2*B[word: cbab] - 1/2*B[word: cbba]
109
"""
110
@staticmethod
111
def __classcall_private__(cls, R, names):
112
"""
113
Normalize input to ensure a unique representation.
114
115
EXAMPLES::
116
117
sage: F1 = ShuffleAlgebra(QQ, 'xyz')
118
sage: F2 = ShuffleAlgebra(QQ, ['x','y','z'])
119
sage: F3 = ShuffleAlgebra(QQ, Alphabet('xyz'))
120
sage: F1 is F2 and F1 is F3
121
True
122
"""
123
return super(ShuffleAlgebra, cls).__classcall__(cls, R, Alphabet(names))
124
125
def __init__(self, R, names):
126
r"""
127
Initialize ``self``.
128
129
EXAMPLES::
130
131
sage: F = ShuffleAlgebra(QQ, 'xyz'); F
132
Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field
133
sage: TestSuite(F).run()
134
135
TESTS::
136
137
sage: ShuffleAlgebra(24, 'toto')
138
Traceback (most recent call last):
139
...
140
TypeError: argument R must be a ring
141
"""
142
if R not in Rings():
143
raise TypeError("argument R must be a ring")
144
self._alphabet = names
145
self.__ngens = self._alphabet.cardinality()
146
CombinatorialFreeModule.__init__(self, R, Words(names),
147
latex_prefix="",
148
category=(AlgebrasWithBasis(R), CommutativeAlgebras(R), CoalgebrasWithBasis(R)))
149
150
def variable_names(self):
151
r"""
152
Return the names of the variables.
153
154
EXAMPLES::
155
156
sage: R = ShuffleAlgebra(QQ,'xy')
157
sage: R.variable_names()
158
{'x', 'y'}
159
"""
160
return self._alphabet
161
162
def is_commutative(self):
163
r"""
164
Return ``True`` as the shuffle algebra is commutative.
165
166
EXAMPLES::
167
168
sage: R = ShuffleAlgebra(QQ,'x')
169
sage: R.is_commutative()
170
True
171
sage: R = ShuffleAlgebra(QQ,'xy')
172
sage: R.is_commutative()
173
True
174
"""
175
return True
176
177
def _repr_(self):
178
r"""
179
Text representation of this shuffle algebra.
180
181
EXAMPLES::
182
183
sage: F = ShuffleAlgebra(QQ,'xyz')
184
sage: F # indirect doctest
185
Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Rational Field
186
187
sage: ShuffleAlgebra(ZZ,'a')
188
Shuffle Algebra on one generator ['a'] over Integer Ring
189
"""
190
if self.__ngens == 1:
191
gen = "one generator"
192
else:
193
gen = "%s generators" %self.__ngens
194
return "Shuffle Algebra on "+ gen +" %s over %s"%(
195
self._alphabet.list(), self.base_ring())
196
197
@cached_method
198
def one_basis(self):
199
r"""
200
Return the empty word, which index of `1` of this algebra,
201
as per :meth:`AlgebrasWithBasis.ParentMethods.one_basis`.
202
203
EXAMPLES::
204
205
sage: A = ShuffleAlgebra(QQ,'a')
206
sage: A.one_basis()
207
word:
208
sage: A.one()
209
B[word: ]
210
"""
211
return self.basis().keys()([])
212
213
def product_on_basis(self, w1, w2):
214
r"""
215
Return the product of basis elements ``w1`` and ``w2``, as per
216
:meth:`AlgebrasWithBasis.ParentMethods.product_on_basis()`.
217
218
INPUT:
219
220
- ``w1``, ``w2`` -- Basis elements
221
222
EXAMPLES::
223
224
sage: A = ShuffleAlgebra(QQ,'abc')
225
sage: W = A.basis().keys()
226
sage: A.product_on_basis(W("acb"), W("cba"))
227
B[word: acbacb] + B[word: acbcab] + 2*B[word: acbcba] + 2*B[word: accbab] + 4*B[word: accbba] + B[word: cabacb] + B[word: cabcab] + B[word: cabcba] + B[word: cacbab] + 2*B[word: cacbba] + 2*B[word: cbaacb] + B[word: cbacab] + B[word: cbacba]
228
229
sage: (a,b,c) = A.algebra_generators()
230
sage: a * (1-b)^2 * c
231
2*B[word: abbc] - 2*B[word: abc] + 2*B[word: abcb] + B[word: ac] - 2*B[word: acb] + 2*B[word: acbb] + 2*B[word: babc] - 2*B[word: bac] + 2*B[word: bacb] + 2*B[word: bbac] + 2*B[word: bbca] - 2*B[word: bca] + 2*B[word: bcab] + 2*B[word: bcba] + B[word: ca] - 2*B[word: cab] + 2*B[word: cabb] - 2*B[word: cba] + 2*B[word: cbab] + 2*B[word: cbba]
232
"""
233
return sum(self.basis()[u] for u in w1.shuffle(w2))
234
235
def gen(self,i):
236
r"""
237
The ``i``-th generator of the algebra.
238
239
INPUT:
240
241
- ``i`` -- an integer
242
243
EXAMPLES::
244
245
sage: F = ShuffleAlgebra(ZZ,'xyz')
246
sage: F.gen(0)
247
B[word: x]
248
249
sage: F.gen(4)
250
Traceback (most recent call last):
251
...
252
IndexError: argument i (= 4) must be between 0 and 2
253
"""
254
n = self.__ngens
255
if i < 0 or not i < n:
256
raise IndexError("argument i (= %s) must be between 0 and %s"%(i, n-1))
257
return self.algebra_generators()[i]
258
259
def coproduct_on_basis(self, w):
260
"""
261
Return the coproduct of the element of the basis indexed by
262
the word ``w``.
263
264
INPUT:
265
266
- ``w`` -- a word
267
268
EXAMPLES::
269
270
sage: F = ShuffleAlgebra(QQ,'ab')
271
sage: F.coproduct_on_basis(Word('a'))
272
B[word: ] # B[word: a] + B[word: a] # B[word: ]
273
sage: F.coproduct_on_basis(Word('aba'))
274
B[word: ] # B[word: aba] + B[word: a] # B[word: ab] + B[word: a] # B[word: ba]
275
+ B[word: aa] # B[word: b] + B[word: ab] # B[word: a] + B[word: aba] # B[word: ]
276
+ B[word: b] # B[word: aa] + B[word: ba] # B[word: a]
277
sage: F.coproduct_on_basis(Word())
278
B[word: ] # B[word: ]
279
"""
280
if len(w) == 0:
281
return self.tensor_square().monomial((self.one_basis(), self.one_basis()))
282
if len(w) == 1:
283
return self.tensor_square().sum_of_terms([
284
((w, self.one_basis()), 1),
285
((self.one_basis(), w), 1) ], distinct=True)
286
287
B = self.basis()
288
result = self.coproduct_on_basis(Word([w[0]]))
289
for i in w[1:]:
290
result = self.tensor_square().sum_of_terms([
291
((Word(v1)*Word(u1), Word(v2)*Word(u2)), coeff1 * coeff2)
292
for ((u1,u2),coeff1) in self.coproduct_on_basis(Word([i]))
293
for ((v1,v2),coeff2) in result ])
294
return result
295
296
def coproduct(self, S):
297
"""
298
Return the coproduct of the series ``S``.
299
300
EXAMPLES::
301
302
sage: F = ShuffleAlgebra(QQ,'ab')
303
sage: S = F.an_element(); S
304
B[word: ] + 2*B[word: a] + 3*B[word: b]
305
sage: F.coproduct(S)
306
B[word: ] # B[word: ] + 2*B[word: ] # B[word: a] + 3*B[word: ] # B[word: b]
307
+ 2*B[word: a] # B[word: ] + 3*B[word: b] # B[word: ]
308
sage: F.coproduct(F.one())
309
B[word: ] # B[word: ]
310
"""
311
return sum([c * self.coproduct_on_basis(i)
312
for i,c in S.monomial_coefficients().items()])
313
314
def counit(self,S):
315
"""
316
Return the counit of ``S``.
317
318
EXAMPLES::
319
320
sage: F = ShuffleAlgebra(QQ,'ab')
321
sage: S = F.an_element(); S
322
B[word: ] + 2*B[word: a] + 3*B[word: b]
323
sage: F.counit(S)
324
1
325
"""
326
return S.monomial_coefficients().get(Word(), 0)
327
328
@cached_method
329
def algebra_generators(self):
330
r"""
331
Return the generators of this algebra.
332
333
EXAMPLES::
334
335
sage: A = ShuffleAlgebra(ZZ,'fgh'); A
336
Shuffle Algebra on 3 generators ['f', 'g', 'h'] over Integer Ring
337
sage: A.algebra_generators()
338
Family (B[word: f], B[word: g], B[word: h])
339
"""
340
Words = self.basis().keys()
341
return Family( [self.monomial(Words(a)) for a in self._alphabet] )
342
# FIXME: use this once the keys argument of FiniteFamily will be honoured
343
# for the specifying the order of the elements in the family
344
#return Family(self._alphabet, lambda a: self.term(self.basis().keys()(a)))
345
346
gens = algebra_generators
347
348
def _element_constructor_(self, x):
349
r"""
350
Convert ``x`` into ``self``.
351
352
EXAMPLES::
353
354
sage: R = ShuffleAlgebra(QQ,'xy')
355
sage: x, y = R.gens()
356
sage: R(3)
357
3*B[word: ]
358
sage: R(x)
359
B[word: x]
360
sage: R('xyy')
361
B[word: xyy]
362
sage: R(Word('xyx'))
363
B[word: xyx]
364
"""
365
if isinstance(x, (str, FiniteWord_class)):
366
W = self.basis().keys()
367
return self.monomial(W(x))
368
369
P = x.parent()
370
if isinstance(P, ShuffleAlgebra):
371
if P is self:
372
return x
373
if not (P is self.base_ring()):
374
return self.element_class(self, x.monomial_coefficients())
375
if isinstance(P, DualPBWBasis):
376
return self(P.expansion(x))
377
# ok, not a shuffle algebra element (or should not be viewed as one).
378
if isinstance(x, basestring):
379
from sage.misc.sage_eval import sage_eval
380
return sage_eval(x,locals=self.gens_dict())
381
R = self.base_ring()
382
# coercion via base ring
383
x = R(x)
384
if x == 0:
385
return self.element_class(self,{})
386
else:
387
return self.from_base_ring_from_one_basis(x)
388
389
def _coerce_impl(self, x):
390
r"""
391
Canonical coercion of ``x`` into ``self``.
392
393
Here is what canonically coerces to ``self``:
394
395
- this shuffle algebra,
396
397
- anything that coerces to the base ring of this shuffle algebra,
398
399
- any shuffle algebra on the same variables, whose base ring
400
coerces to the base ring of this shuffle algebra.
401
402
EXAMPLES::
403
404
sage: F = ShuffleAlgebra(GF(7), 'xyz'); F
405
Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7
406
407
Elements of the shuffle algebra canonically coerce in::
408
409
sage: x, y, z = F.gens()
410
sage: F.coerce(x*y) # indirect doctest
411
B[word: xy] + B[word: yx]
412
413
Elements of the integers coerce in, since there is a coerce map
414
from `\ZZ` to GF(7)::
415
416
sage: F.coerce(1) # indirect doctest
417
B[word: ]
418
419
There is no coerce map from `\QQ` to `\GF{7}`::
420
421
sage: F.coerce(2/3) # indirect doctest
422
Traceback (most recent call last):
423
...
424
TypeError: no canonical coercion from Rational Field to Shuffle Algebra on 3 generators ['x', 'y', 'z'] over Finite Field of size 7
425
426
Elements of the base ring coerce in::
427
428
sage: F.coerce(GF(7)(5))
429
5*B[word: ]
430
431
The shuffle algebra over `\ZZ` on `x, y, z` coerces in, since
432
`\ZZ` coerces to `\GF{7}`::
433
434
sage: G = ShuffleAlgebra(ZZ,'xyz')
435
sage: Gx,Gy,Gz = G.gens()
436
sage: z = F.coerce(Gx**2 * Gy);z
437
2*B[word: xxy] + 2*B[word: xyx] + 2*B[word: yxx]
438
sage: z.parent() is F
439
True
440
441
However, `\GF{7}` does not coerce to `\ZZ`, so the shuffle
442
algebra over `\GF{7}` does not coerce to the one over `\ZZ`::
443
444
sage: G.coerce(x^3*y)
445
Traceback (most recent call last):
446
...
447
TypeError: no canonical coercion from Shuffle Algebra on 3 generators
448
['x', 'y', 'z'] over Finite Field of size 7 to Shuffle Algebra on 3
449
generators ['x', 'y', 'z'] over Integer Ring
450
"""
451
try:
452
R = x.parent()
453
454
# shuffle algebras in the same variables over any base
455
# that coerces in:
456
if isinstance(R,ShuffleAlgebra):
457
if R.variable_names() == self.variable_names():
458
if self.has_coerce_map_from(R.base_ring()):
459
return self(x)
460
else:
461
raise TypeError("no natural map between bases of shuffle algebras")
462
463
if isinstance(R, DualPBWBasis):
464
return self(R.expansion(x))
465
466
except AttributeError:
467
pass
468
469
# any ring that coerces to the base ring of this shuffle algebra.
470
return self._coerce_try(x, [self.base_ring()])
471
472
def _coerce_map_from_(self, R):
473
r"""
474
Return ``True`` if there is a coercion from ``R`` into ``self``
475
and ``False`` otherwise.
476
477
The things that coerce into ``self`` are
478
479
- Shuffle Algebras in the same variables over a base with a coercion
480
map into ``self.base_ring()``.
481
482
- Anything with a coercion into ``self.base_ring()``.
483
484
TESTS::
485
486
sage: F = ShuffleAlgebra(ZZ, 'xyz')
487
sage: G = ShuffleAlgebra(QQ, 'xyz')
488
sage: H = ShuffleAlgebra(ZZ, 'y')
489
sage: F._coerce_map_from_(G)
490
False
491
sage: G._coerce_map_from_(F)
492
True
493
sage: F._coerce_map_from_(H)
494
False
495
sage: F._coerce_map_from_(QQ)
496
False
497
sage: G._coerce_map_from_(QQ)
498
True
499
sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z'))
500
False
501
sage: F._coerce_map_from_(F.dual_pbw_basis())
502
True
503
"""
504
# shuffle algebras in the same variable over any base that coerces in:
505
if isinstance(R, ShuffleAlgebra):
506
if R.variable_names() == self.variable_names():
507
if self.base_ring().has_coerce_map_from(R.base_ring()):
508
return True
509
else:
510
return False
511
512
if isinstance(R, DualPBWBasis):
513
return self.has_coerce_map_from(R._alg)
514
515
return self.base_ring().has_coerce_map_from(R)
516
517
def dual_pbw_basis(self):
518
"""
519
Return the dual PBW of ``self``.
520
521
EXAMPLES::
522
523
sage: A = ShuffleAlgebra(QQ, 'ab')
524
sage: A.dual_pbw_basis()
525
The dual Poincare-Birkhoff-Witt basis of Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field
526
"""
527
return DualPBWBasis(self.base_ring(), self._alphabet)
528
529
def to_dual_pbw_element(self, w):
530
"""
531
Return the element `w` of ``self`` expressed in the dual PBW basis.
532
533
INPUT:
534
535
- ``w`` -- an element of the shuffle algebra
536
537
EXAMPLES::
538
539
sage: A = ShuffleAlgebra(QQ, 'ab')
540
sage: f = 2 * A(Word()) + A(Word('ab')); f
541
2*B[word: ] + B[word: ab]
542
sage: A.to_dual_pbw_element(f)
543
2*S[word: ] + S[word: ab]
544
sage: A.to_dual_pbw_element(A.one())
545
S[word: ]
546
sage: S = A.dual_pbw_basis()
547
sage: elt = S.expansion_on_basis(Word('abba')); elt
548
2*B[word: aabb] + B[word: abab] + B[word: abba]
549
sage: A.to_dual_pbw_element(elt)
550
S[word: abba]
551
sage: A.to_dual_pbw_element(2*A(Word('aabb')) + A(Word('abab')))
552
S[word: abab]
553
sage: S.expansion(S('abab'))
554
2*B[word: aabb] + B[word: abab]
555
"""
556
D = self.dual_pbw_basis()
557
l = {}
558
W = self.basis().keys()
559
while w != self.zero():
560
support = [W(i[0]) for i in list(w)]
561
min_elt = W(support[0])
562
if len(support) > 1:
563
for word in support[1:len(support)-1]:
564
if min_elt.lex_less(word):
565
min_elt = W(word)
566
coeff = list(w)[support.index(min_elt)][1]
567
l[min_elt] = l.get(min_elt, 0) + coeff
568
w = w - coeff * D.expansion_on_basis(W(min_elt))
569
570
return D.sum_of_terms([(m, c) for m,c in l.items() if c != 0])
571
572
class DualPBWBasis(CombinatorialFreeModule):
573
r"""
574
The basis dual to the Poincare-Birkhoff-Witt basis of the free algebra.
575
576
We recursively define the dual PBW basis as the basis of the
577
shuffle algebra given by
578
579
.. MATH::
580
581
S_w = \begin{cases}
582
w & |w| = 1, \\
583
x S_u & w = xu \text{ and } w \in \mathrm{Lyn}(X), \\
584
\displaystyle \frac{S_{\ell_{i_1}}^{\ast \alpha_1} \ast \cdots
585
\ast S_{\ell_{i_k}}^{\ast \alpha_k}}{\alpha_1! \cdots \alpha_k!} &
586
w = \ell_{i_1}^{\alpha_1} \cdots \ell_{i_k}^{\alpha_k} \text{ with }
587
\ell_1 > \cdots > \ell_k \in \mathrm{Lyn}(X).
588
\end{cases}
589
590
where `S \ast T` denotes the shuffle product of `S` and `T` and
591
`\mathrm{Lyn}(X)` is the set of Lyndon words in the alphabet `X`.
592
593
The definition may be found in Theorem 5.3 of [Reuten1993]_.
594
595
INPUT:
596
597
- ``R`` -- ring
598
599
- ``names`` -- names of the generators (string or an alphabet)
600
601
REFERENCES:
602
603
.. [Reuten1993] C. Reutenauer. *Free Lie Algebras*. Number 7 in
604
London Math. Soc. Monogr. (N.S.). Oxford University Press. (1993).
605
606
EXAMPLES::
607
608
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
609
sage: S
610
The dual Poincare-Birkhoff-Witt basis of Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field
611
sage: S.one()
612
S[word: ]
613
sage: S.one_basis()
614
word:
615
sage: T = ShuffleAlgebra(QQ, 'abcd').dual_pbw_basis(); T
616
The dual Poincare-Birkhoff-Witt basis of Shuffle Algebra on 4 generators ['a', 'b', 'c', 'd'] over Rational Field
617
sage: T.algebra_generators()
618
(S[word: a], S[word: b], S[word: c], S[word: d])
619
620
TESTS:
621
622
We check conversion between the bases::
623
624
sage: A = ShuffleAlgebra(QQ, 'ab')
625
sage: S = A.dual_pbw_basis()
626
sage: W = Words('ab', 5)
627
sage: all(S(A(S(w))) == S(w) for w in W)
628
True
629
sage: all(A(S(A(w))) == A(w) for w in W)
630
True
631
"""
632
@staticmethod
633
def __classcall_private__(cls, R, names):
634
"""
635
Normalize input to ensure a unique representation.
636
637
EXAMPLES::
638
639
sage: from sage.algebras.shuffle_algebra import DualPBWBasis
640
sage: D1 = DualPBWBasis(QQ, 'ab')
641
sage: D2 = DualPBWBasis(QQ, Alphabet('ab'))
642
sage: D1 is D2
643
True
644
"""
645
return super(DualPBWBasis, cls).__classcall__(cls, R, Alphabet(names))
646
647
def __init__(self, R, names):
648
"""
649
Initialize ``self``.
650
651
EXAMPLES::
652
653
sage: D = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
654
sage: TestSuite(D).run()
655
"""
656
self._alphabet = names
657
self._alg = ShuffleAlgebra(R, names)
658
CombinatorialFreeModule.__init__(self, R, Words(names), prefix='S',
659
category=(AlgebrasWithBasis(R), CommutativeAlgebras(R), CoalgebrasWithBasis(R)))
660
661
def _repr_(self):
662
"""
663
Return a string representation of ``self``.
664
665
EXAMPLES::
666
667
sage: ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
668
The dual Poincare-Birkhoff-Witt basis of Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field
669
"""
670
return "The dual Poincare-Birkhoff-Witt basis of {}".format(self._alg)
671
672
def _element_constructor_(self, x):
673
"""
674
Construct an element of ``self`` from ``x``.
675
676
EXAMPLES::
677
678
sage: A = ShuffleAlgebra(QQ, 'ab')
679
sage: S = A.dual_pbw_basis()
680
sage: S('abaab')
681
S[word: abaab]
682
sage: S(Word('aba'))
683
S[word: aba]
684
sage: S(A('ab'))
685
S[word: ab]
686
"""
687
if isinstance(x, (str, FiniteWord_class)):
688
W = self.basis().keys()
689
x = W(x)
690
elif isinstance(x.parent(), ShuffleAlgebra):
691
return self._alg.to_dual_pbw_element(self._alg(x))
692
return super(DualPBWBasis, self)._element_constructor_(x)
693
694
def _coerce_map_from_(self, R):
695
"""
696
Return ``True`` if there is a coercion from ``R`` into ``self`` and
697
``False`` otherwise. The things that coerce into ``self`` are:
698
699
- Anything that coerces into the associated shuffle algebra of ``self``
700
701
TESTS::
702
703
sage: F = ShuffleAlgebra(ZZ, 'xyz').dual_pbw_basis()
704
sage: G = ShuffleAlgebra(QQ, 'xyz').dual_pbw_basis()
705
sage: H = ShuffleAlgebra(ZZ, 'y').dual_pbw_basis()
706
sage: F._coerce_map_from_(G)
707
False
708
sage: G._coerce_map_from_(F)
709
True
710
sage: F._coerce_map_from_(H)
711
False
712
sage: F._coerce_map_from_(QQ)
713
False
714
sage: G._coerce_map_from_(QQ)
715
True
716
sage: F.has_coerce_map_from(PolynomialRing(ZZ, 3, 'x,y,z'))
717
False
718
sage: F._coerce_map_from_(F._alg)
719
True
720
"""
721
return self._alg.has_coerce_map_from(R)
722
723
def one_basis(self):
724
"""
725
Return the indexing element of the basis element `1`.
726
727
EXAMPLES::
728
729
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
730
sage: S.one_basis()
731
word:
732
"""
733
W = self.basis().keys()
734
return W([])
735
736
def algebra_generators(self):
737
"""
738
Return the algebra generators of ``self``.
739
740
EXAMPLES::
741
742
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
743
sage: S.algebra_generators()
744
(S[word: a], S[word: b])
745
"""
746
W = self.basis().keys()
747
return tuple(self.monomial(W(a)) for a in self._alphabet)
748
749
gens = algebra_generators
750
751
def gen(self, i):
752
"""
753
Return the ``i``-th generator of ``self``.
754
755
EXAMPLES::
756
757
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
758
sage: S.gen(0)
759
S[word: a]
760
sage: S.gen(1)
761
S[word: b]
762
"""
763
return self.algebra_generators()[i]
764
765
def shuffle_algebra(self):
766
"""
767
Return the associated shuffle algebra of ``self``.
768
769
EXAMPLES::
770
771
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
772
sage: S.shuffle_algebra()
773
Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field
774
"""
775
return self._alg
776
777
def product(self, u, v):
778
"""
779
Return the product of two elements ``u`` and ``v``.
780
781
EXAMPLES::
782
783
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
784
sage: a,b = S.gens()
785
sage: S.product(a, b)
786
S[word: ba]
787
sage: S.product(b, a)
788
S[word: ba]
789
sage: S.product(b^2*a, a*b*a)
790
36*S[word: bbbaaa]
791
792
TESTS:
793
794
Check that multiplication agrees with the multiplication in the
795
shuffle algebra::
796
797
sage: A = ShuffleAlgebra(QQ, 'ab')
798
sage: S = A.dual_pbw_basis()
799
sage: a,b = S.gens()
800
sage: A(a*b)
801
B[word: ab] + B[word: ba]
802
sage: A(a*b*a)
803
2*B[word: aab] + 2*B[word: aba] + 2*B[word: baa]
804
sage: S(A(a)*A(b)*A(a)) == a*b*a
805
True
806
"""
807
return self(self.expansion(u) * self.expansion(v))
808
809
@lazy_attribute
810
def expansion(self):
811
"""
812
Return the morphism corresponding to the expansion into words of
813
the shuffle algebra.
814
815
EXAMPLES::
816
817
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
818
sage: f = S('ab') + S('aba')
819
sage: S.expansion(f)
820
2*B[word: aab] + B[word: ab] + B[word: aba]
821
"""
822
return self.module_morphism(self.expansion_on_basis, codomain=self._alg)
823
824
@cached_method
825
def expansion_on_basis(self, w):
826
r"""
827
Return the expansion of `S_w` in words of the shuffle algebra.
828
829
INPUT:
830
831
- ``w`` -- a word
832
833
EXAMPLES::
834
835
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
836
sage: S.expansion_on_basis(Word())
837
B[word: ]
838
sage: S.expansion_on_basis(Word()).parent()
839
Shuffle Algebra on 2 generators ['a', 'b'] over Rational Field
840
sage: S.expansion_on_basis(Word('abba'))
841
2*B[word: aabb] + B[word: abab] + B[word: abba]
842
sage: S.expansion_on_basis(Word())
843
B[word: ]
844
sage: S.expansion_on_basis(Word('abab'))
845
2*B[word: aabb] + B[word: abab]
846
"""
847
from sage.functions.other import factorial
848
if len(w) == 0:
849
return self._alg.one()
850
if len(w) == 1:
851
return self._alg.monomial(w)
852
853
if w.is_lyndon():
854
W = self.basis().keys()
855
letter = W(w[0])
856
expansion = self.expansion_on_basis(W(w[1:]))
857
return self._alg.sum_of_terms([(letter * i, c) for i,c in expansion])
858
859
lf = w.lyndon_factorization()
860
powers = {}
861
for i in lf:
862
powers[i] = powers.get(i, 0) + 1
863
denom = prod(factorial(p) for p in powers.values())
864
result = self._alg.prod(self.expansion_on_basis(i) for i in lf)
865
return self._alg(result / denom)
866
867
class Element(CombinatorialFreeModule.Element):
868
"""
869
An element in the dual PBW basis.
870
"""
871
def expand(self):
872
"""
873
Expand ``self`` in words of the shuffle algebra.
874
875
EXAMPLES::
876
877
sage: S = ShuffleAlgebra(QQ, 'ab').dual_pbw_basis()
878
sage: f = S('ab') + S('bab')
879
sage: f.expand()
880
B[word: ab] + 2*B[word: abb] + B[word: bab]
881
"""
882
return self.parent().expansion(self)
883
884
885