Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/combinatorial_algebra.py
4057 views
1
r"""
2
Combinatorial Algebras
3
4
A combinatorial algebra is an algebra whose basis elements are
5
indexed by a combinatorial class. Some examples of combinatorial
6
algebras are the symmetric group algebra of order n (indexed by
7
permutations of size n) and the algebra of symmetric functions
8
(indexed by integer partitions).
9
10
The CombinatorialAlgebra base class makes it easy to define and
11
work with new combinatorial algebras in Sage. For example, the
12
following code constructs an algebra which models the power-sum
13
symmetric functions.
14
15
::
16
17
sage: class PowerSums(CombinatorialAlgebra):
18
... def __init__(self, R):
19
... self._one = Partition([])
20
... self._name = 'Power-sum symmetric functions'
21
... CombinatorialAlgebra.__init__(self, R, Partitions())
22
... self.print_options(prefix='p')
23
...
24
... def _multiply_basis(self, a, b):
25
... l = list(a)+list(b)
26
... l.sort(reverse=True)
27
... return Partition(l)
28
...
29
30
::
31
32
sage: ps = PowerSums(QQ); ps
33
Power-sum symmetric functions over Rational Field
34
sage: ps([2,1])^2
35
p[2, 2, 1, 1]
36
sage: ps([2,1])+2*ps([1,1,1])
37
2*p[1, 1, 1] + p[2, 1]
38
sage: ps(2)
39
2*p[]
40
41
The important things to define are ._basis_keys which
42
specifies the combinatorial class that indexes the basis elements,
43
._one which specifies the identity element in the algebra, ._name
44
which specifies the name of the algebra, .print_options is used to set
45
the print options for the elements, and finally a _multiply
46
or _multiply_basis method that defines the multiplication in the
47
algebra.
48
"""
49
#*****************************************************************************
50
# Copyright (C) 2007 Mike Hansen <[email protected]>,
51
#
52
# Distributed under the terms of the GNU General Public License (GPL)
53
#
54
# This code is distributed in the hope that it will be useful,
55
# but WITHOUT ANY WARRANTY; without even the implied warranty of
56
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
57
# General Public License for more details.
58
#
59
# The full text of the GPL is available at:
60
#
61
# http://www.gnu.org/licenses/
62
#*****************************************************************************
63
64
#from sage.algebras.algebra import Algebra
65
#from sage.algebras.algebra_element import AlgebraElement
66
from sage.combinat.free_module import CombinatorialFreeModule
67
from sage.misc.misc import repr_lincomb
68
from sage.misc.cachefunc import cached_method
69
from sage.categories.all import AlgebrasWithBasis
70
71
# TODO: migrate this completely to the combinatorial free module + categories framework
72
73
# for backward compatibility
74
CombinatorialAlgebraElement = CombinatorialFreeModule.Element
75
76
# Not used anymore
77
class CombinatorialAlgebraElementOld(CombinatorialFreeModule.Element):
78
# def __init__(self, A, x):
79
# """
80
# Create a combinatorial algebra element x. This should never
81
# be called directly, but only through the parent combinatorial
82
# algebra's __call__ method.
83
84
# TESTS:
85
# sage: s = SFASchur(QQ)
86
# sage: a = s._element_class(s, {Partition([2,1]):QQ(2)}); a
87
# 2*s[2, 1]
88
# sage: a == loads(dumps(a))
89
# True
90
# """
91
# AlgebraElement.__init__(self, A)
92
# self._monomial_coefficients = x
93
94
95
def _mul_(self, y):
96
"""
97
EXAMPLES::
98
99
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
100
sage: a = s([2])
101
sage: a._mul_(a) #indirect doctest
102
s[2, 2] + s[3, 1] + s[4]
103
"""
104
return self.parent().product(self, y)
105
106
107
def __invert__(self):
108
"""
109
EXAMPLES::
110
111
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
112
sage: ~s(2)
113
1/2*s[]
114
sage: ~s([2,1])
115
Traceback (most recent call last):
116
...
117
ValueError: cannot invert self (= s[2, 1])
118
"""
119
mcs = self._monomial_coefficients
120
one = self.parent()._one
121
if len(mcs) == 1 and one in mcs:
122
return self.parent()( ~mcs[ one ] )
123
else:
124
raise ValueError, "cannot invert self (= %s)"%self
125
126
127
128
129
130
def __repr__(self):
131
"""
132
EXAMPLES::
133
134
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
135
sage: a = 2 + s([3,2,1])
136
sage: print a.__repr__()
137
2*s[] + s[3, 2, 1]
138
"""
139
v = self._monomial_coefficients.items()
140
v.sort()
141
prefix = self.parent().prefix()
142
retur = repr_lincomb( [(prefix + repr(m), c) for m,c in v ], strip_one = True)
143
144
class CombinatorialAlgebra(CombinatorialFreeModule):
145
"""
146
147
Deprecated! Don't use!
148
149
"""
150
151
# For backward compatibility
152
@cached_method
153
def one_basis(self):
154
"""
155
TESTS::
156
157
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
158
sage: s.one_basis()
159
[]
160
"""
161
return self._one
162
163
def __init__(self, R, cc = None, element_class = None, category = None):
164
"""
165
TESTS::
166
167
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
168
sage: TestSuite(s).run()
169
"""
170
#Check to make sure that the user defines the necessary
171
#attributes / methods to make the combinatorial module
172
#work
173
required = ['_one',]
174
for r in required:
175
if not hasattr(self, r):
176
raise ValueError, "%s is required"%r
177
if not hasattr(self, '_multiply') and not hasattr(self, '_multiply_basis'):
178
raise ValueError, "either _multiply or _multiply_basis is required"
179
180
#Create a class for the elements of this combinatorial algebra
181
#We need to do this so to distinguish between element of different
182
#combinatorial algebras
183
# if element_class is None:
184
# if not hasattr(self, '_element_class'):
185
# class CAElement(CombinatorialAlgebraElement):
186
# pass
187
# self._element_class = CAElement
188
# else:
189
# self._element_class = element_class
190
191
if category is None:
192
category = AlgebrasWithBasis(R)
193
194
# for backward compatibility
195
if cc is None and hasattr(self, "_basis_keys"):
196
cc = self._basis_keys
197
assert(cc is not None)
198
199
CombinatorialFreeModule.__init__(self, R, cc, element_class, category = category)
200
201
# see sage.combinat.free_module._repr_term
202
# this emulates the _repr_ of CombinatorialAlgebraElement did not add brackets around the basis indices
203
_repr_option_bracket = False
204
205
def __call__(self, x):
206
"""
207
TESTS::
208
209
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
210
sage: s([2])
211
s[2]
212
"""
213
try:
214
return CombinatorialFreeModule.__call__(self, x)
215
except TypeError:
216
pass
217
218
R = self.base_ring()
219
eclass = self._element_class
220
#Coerce elements of the base ring
221
if not hasattr(x, 'parent'):
222
x = R(x)
223
if x.parent() == R:
224
if x == R(0):
225
return eclass(self, {})
226
else:
227
return eclass(self, {self._one:x})
228
#Coerce things that coerce into the base ring
229
elif R.has_coerce_map_from(x.parent()):
230
rx = R(x)
231
if rx == R(0):
232
return eclass(self, {})
233
else:
234
return eclass(self, {self._one:R(x)})
235
236
raise TypeError, "do not know how to make x (= %s) an element of self (=%s)"%(x,self)
237
238
def _an_element_impl(self):
239
"""
240
Returns an element of self, namely the unit element.
241
242
EXAMPLES::
243
244
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
245
sage: s._an_element_impl()
246
s[]
247
sage: _.parent() is s
248
True
249
"""
250
return self._element_class(self, {self._one:self.base_ring()(1)})
251
252
def _coerce_impl(self, x):
253
"""
254
EXAMPLES::
255
256
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
257
sage: s(2) # indirect doctest
258
2*s[]
259
"""
260
try:
261
R = x.parent()
262
if R.__class__ is self.__class__:
263
#Only perform the coercion if we can go from the base
264
#ring of x to the base ring of self
265
if self.base_ring().has_coerce_map_from( R.base_ring() ):
266
return self(x)
267
except AttributeError:
268
pass
269
270
# any ring that coerces to the base ring
271
return self._coerce_try(x, [self.base_ring()])
272
273
def product(self, left, right):
274
"""
275
Returns left\*right where left and right are elements of self.
276
product() uses either _multiply or _multiply basis to carry out
277
the actual multiplication.
278
279
EXAMPLES::
280
281
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
282
sage: a = s([2])
283
sage: s.product(a,a)
284
s[2, 2] + s[3, 1] + s[4]
285
sage: ZS3 = SymmetricGroupAlgebra(ZZ, 3)
286
sage: a = 2 + ZS3([2,1,3])
287
sage: a*a
288
5*[1, 2, 3] + 4*[2, 1, 3]
289
sage: H3 = HeckeAlgebraSymmetricGroupT(QQ,3)
290
sage: j2 = H3.jucys_murphy(2)
291
sage: j2*j2
292
(q^3-q^2+q)*T[1, 2, 3] + (q^3-q^2+q-1)*T[2, 1, 3]
293
sage: X = SchubertPolynomialRing(ZZ)
294
sage: X([1,2,3])*X([2,1,3])
295
X[2, 1]
296
"""
297
A = left.parent()
298
ABR = A.base_ring()
299
ABRzero = ABR(0)
300
z_elt = {}
301
302
#Do the case where the user specifies how to multiply basis elements
303
if hasattr(self, '_multiply_basis'):
304
for (left_m, left_c) in left._monomial_coefficients.iteritems():
305
for (right_m, right_c) in right._monomial_coefficients.iteritems():
306
res = self._multiply_basis(left_m, right_m)
307
coeffprod = left_c * right_c
308
#Handle the case where the user returns a dictionary
309
#where the keys are the monomials and the values are
310
#the coefficients. If res is not a dictionary, then
311
#it is assumed to be an element of self
312
if not isinstance(res, dict):
313
if isinstance(res, self._element_class):
314
res = res._monomial_coefficients
315
else:
316
z_elt[res] = z_elt.get(res, ABRzero) + coeffprod
317
continue
318
for m, c in res.iteritems():
319
z_elt[m] = z_elt.get(m, ABRzero) + coeffprod * c
320
321
#We assume that the user handles the multiplication correctly on
322
#his or her own, and returns a dict with monomials as keys and
323
#coefficients as values
324
else:
325
m = self._multiply(left, right)
326
if isinstance(m, self._element_class):
327
return m
328
if not isinstance(m, dict):
329
z_elt = m.monomial_coefficients()
330
else:
331
z_elt = m
332
333
#Remove all entries that are equal to 0
334
BR = self.base_ring()
335
zero = BR(0)
336
del_list = []
337
for m, c in z_elt.iteritems():
338
if c == zero:
339
del_list.append(m)
340
for m in del_list:
341
del z_elt[m]
342
343
return self._from_dict(z_elt)
344
345
class TestAlgebra(CombinatorialAlgebra):
346
347
_name = "TestAlgebra"
348
349
def __init__(self, R):
350
"""
351
TESTS::
352
353
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
354
sage: TestSuite(s).run()
355
"""
356
from sage.combinat.partition import Partition, Partitions
357
self._one = Partition([])
358
CombinatorialAlgebra.__init__(self, R, cc=Partitions())
359
360
def _multiply_basis(self, part1, part2):
361
"""
362
TESTS::
363
364
sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
365
sage: a = s([2])
366
sage: a._mul_(a) #indirect doctest
367
s[2, 2] + s[3, 1] + s[4]
368
"""
369
from sage.combinat.sf.sfa import SFASchur
370
S = SFASchur(self.base_ring())
371
return self.sum_of_terms(S(part1) * S(part2))
372
373
def prefix(self):
374
"""
375
TESTS::
376
377
sage: sa = sage.combinat.combinatorial_algebra.TestAlgebra(QQ)
378
sage: x = sa([2]); x # indirect doctest
379
s[2]
380
"""
381
return "s"
382
383