Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

188

189

190

191

192

193

194

195

196

197

198

199

200

201

202

203

204

205

206

207

208

209

210

211

212

213

214

215

216

217

218

219

220

221

222

223

224

225

226

227

228

229

230

231

232

233

234

235

236

237

238

239

240

241

242

243

244

245

246

247

248

249

250

251

252

253

254

255

256

257

258

259

260

261

262

263

264

265

266

267

268

269

270

271

272

273

274

275

276

277

278

279

280

281

282

283

284

285

286

287

288

289

290

291

292

293

294

295

296

297

298

299

300

301

302

303

304

305

306

307

308

309

310

311

312

313

314

315

316

317

318

319

320

321

322

323

324

325

326

327

328

329

330

331

332

333

334

335

336

337

338

339

340

341

342

343

344

345

346

347

348

349

350

351

352

353

354

355

356

357

358

359

360

361

362

363

364

365

366

367

368

369

370

371

372

373

374

375

376

377

378

379

380

381

382

r""" 

Combinatorial Algebras 

 

A combinatorial algebra is an algebra whose basis elements are 

indexed by a combinatorial class. Some examples of combinatorial 

algebras are the symmetric group algebra of order n (indexed by 

permutations of size n) and the algebra of symmetric functions 

(indexed by integer partitions). 

 

The CombinatorialAlgebra base class makes it easy to define and 

work with new combinatorial algebras in Sage. For example, the 

following code constructs an algebra which models the power-sum 

symmetric functions. 

 

:: 

 

sage: class PowerSums(CombinatorialAlgebra): 

....: def __init__(self, R): 

....: self._one = Partition([]) 

....: self._name = 'Power-sum symmetric functions' 

....: CombinatorialAlgebra.__init__(self, R, Partitions()) 

....: self.print_options(prefix='p') 

....: def _multiply_basis(self, a, b): 

....: l = list(a)+list(b) 

....: l.sort(reverse=True) 

....: return Partition(l) 

 

:: 

 

sage: ps = PowerSums(QQ); ps 

Power-sum symmetric functions over Rational Field 

sage: ps([2,1])^2 

p[2, 2, 1, 1] 

sage: ps([2,1])+2*ps([1,1,1]) 

2*p[1, 1, 1] + p[2, 1] 

sage: ps(2) 

2*p[] 

 

The important things to define are ._indices which 

specifies the combinatorial class that indexes the basis elements, 

._one which specifies the identity element in the algebra, ._name 

which specifies the name of the algebra, .print_options is used to set 

the print options for the elements, and finally a _multiply 

or _multiply_basis method that defines the multiplication in the 

algebra. 

""" 

#***************************************************************************** 

# Copyright (C) 2007 Mike Hansen <mhansen@gmail.com>, 

# 

# Distributed under the terms of the GNU General Public License (GPL) 

# 

# This code is distributed in the hope that it will be useful, 

# but WITHOUT ANY WARRANTY; without even the implied warranty of 

# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 

# General Public License for more details. 

# 

# The full text of the GPL is available at: 

# 

# http://www.gnu.org/licenses/ 

#***************************************************************************** 

 

from sage.combinat.free_module import CombinatorialFreeModule 

from sage.misc.misc import repr_lincomb 

from sage.misc.cachefunc import cached_method 

from sage.categories.all import AlgebrasWithBasis 

from sage.structure.element import Element 

 

import six 

 

 

# TODO: migrate this completely to the combinatorial free module + categories framework 

 

# for backward compatibility 

CombinatorialAlgebraElement = CombinatorialFreeModule.Element 

 

# Not used anymore 

class CombinatorialAlgebraElementOld(CombinatorialFreeModule.Element): 

# def __init__(self, A, x): 

# """ 

# Create a combinatorial algebra element x. This should never 

# be called directly, but only through the parent combinatorial 

# algebra's __call__ method. 

 

# TESTS:: 

# 

# sage: s = SFASchur(QQ) 

# sage: a = s._element_class(s, {Partition([2,1]):QQ(2)}); a 

# 2*s[2, 1] 

# sage: a == loads(dumps(a)) 

# True 

# """ 

# AlgebraElement.__init__(self, A) 

# self._monomial_coefficients = x 

 

 

def _mul_(self, y): 

""" 

EXAMPLES:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: a = s([2]) 

sage: a._mul_(a) #indirect doctest 

s[2, 2] + s[3, 1] + s[4] 

""" 

return self.parent().product(self, y) 

 

 

def __invert__(self): 

""" 

EXAMPLES:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: ~s(2) 

1/2*s[] 

sage: ~s([2,1]) 

Traceback (most recent call last): 

... 

ValueError: cannot invert self (= s[2, 1]) 

""" 

mcs = self._monomial_coefficients 

one = self.parent()._one 

if len(mcs) == 1 and one in mcs: 

return self.parent()( ~mcs[ one ] ) 

else: 

raise ValueError("cannot invert self (= %s)"%self) 

 

 

 

 

 

def __repr__(self): 

""" 

EXAMPLES:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: a = 2 + s([3,2,1]) 

sage: print(a.__repr__()) 

2*s[] + s[3, 2, 1] 

""" 

v = sorted(self._monomial_coefficients.items()) 

prefix = self.parent().prefix() 

retur = repr_lincomb( [(prefix + repr(m), c) for m,c in v ], strip_one = True) 

 

class CombinatorialAlgebra(CombinatorialFreeModule): 

""" 

 

Deprecated! Don't use! 

 

""" 

 

# For backward compatibility 

@cached_method 

def one_basis(self): 

""" 

TESTS:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: s.one_basis() 

[] 

""" 

return self._one 

 

def __init__(self, R, cc = None, element_class = None, category = None): 

""" 

TESTS:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: TestSuite(s).run() 

""" 

#Check to make sure that the user defines the necessary 

#attributes / methods to make the combinatorial module 

#work 

required = ['_one',] 

for r in required: 

if not hasattr(self, r): 

raise ValueError("%s is required"%r) 

if not hasattr(self, '_multiply') and not hasattr(self, '_multiply_basis'): 

raise ValueError("either _multiply or _multiply_basis is required") 

 

#Create a class for the elements of this combinatorial algebra 

#We need to do this so to distinguish between element of different 

#combinatorial algebras 

# if element_class is None: 

# if not hasattr(self, '_element_class'): 

# class CAElement(CombinatorialAlgebraElement): 

# pass 

# self._element_class = CAElement 

# else: 

# self._element_class = element_class 

 

if category is None: 

category = AlgebrasWithBasis(R) 

 

# for backward compatibility 

if cc is None: 

if hasattr(self, "_indices"): 

cc = self._indices 

assert(cc is not None) 

 

CombinatorialFreeModule.__init__(self, R, cc, element_class, category = category) 

 

# see sage.combinat.free_module._repr_term 

# this emulates the _repr_ of CombinatorialAlgebraElement did not add brackets around the basis indices 

_repr_option_bracket = False 

 

def __call__(self, x): 

""" 

TESTS:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: s([2]) 

s[2] 

""" 

try: 

return CombinatorialFreeModule.__call__(self, x) 

except TypeError: 

pass 

 

R = self.base_ring() 

eclass = self._element_class 

#Coerce elements of the base ring 

if not isinstance(x, Element): 

x = R(x) 

if x.parent() == R: 

if x == R(0): 

return eclass(self, {}) 

else: 

return eclass(self, {self._one:x}) 

#Coerce things that coerce into the base ring 

elif R.has_coerce_map_from(x.parent()): 

rx = R(x) 

if rx == R(0): 

return eclass(self, {}) 

else: 

return eclass(self, {self._one:R(x)}) 

 

raise TypeError("do not know how to make x (= %s) an element of self (=%s)"%(x,self)) 

 

def _an_element_impl(self): 

""" 

Returns an element of self, namely the unit element. 

 

EXAMPLES:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: s._an_element_impl() 

s[] 

sage: _.parent() is s 

True 

""" 

return self._element_class(self, {self._one:self.base_ring()(1)}) 

 

def _coerce_impl(self, x): 

""" 

EXAMPLES:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: s(2) # indirect doctest 

2*s[] 

""" 

try: 

R = x.parent() 

if R.__class__ is self.__class__: 

#Only perform the coercion if we can go from the base 

#ring of x to the base ring of self 

if self.base_ring().has_coerce_map_from( R.base_ring() ): 

return self(x) 

except AttributeError: 

pass 

 

# any ring that coerces to the base ring 

return self._coerce_try(x, [self.base_ring()]) 

 

def product(self, left, right): 

""" 

Returns left\*right where left and right are elements of self. 

product() uses either _multiply or _multiply basis to carry out 

the actual multiplication. 

 

EXAMPLES:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: a = s([2]) 

sage: s.product(a,a) 

s[2, 2] + s[3, 1] + s[4] 

sage: ZS3 = SymmetricGroupAlgebra(ZZ, 3) 

sage: a = 2 + ZS3([2,1,3]) 

sage: a*a 

5*[1, 2, 3] + 4*[2, 1, 3] 

sage: H3 = HeckeAlgebraSymmetricGroupT(QQ,3) 

sage: j2 = H3.jucys_murphy(2) 

sage: j2*j2 

(q^3-q^2+q)*T[1, 2, 3] + (q^3-q^2+q-1)*T[2, 1, 3] 

sage: X = SchubertPolynomialRing(ZZ) 

sage: X([1,2,3])*X([2,1,3]) 

X[2, 1] 

""" 

A = left.parent() 

ABR = A.base_ring() 

ABRzero = ABR(0) 

z_elt = {} 

 

#Do the case where the user specifies how to multiply basis elements 

if hasattr(self, '_multiply_basis'): 

for (left_m, left_c) in six.iteritems(left._monomial_coefficients): 

for (right_m, right_c) in six.iteritems(right._monomial_coefficients): 

res = self._multiply_basis(left_m, right_m) 

coeffprod = left_c * right_c 

#Handle the case where the user returns a dictionary 

#where the keys are the monomials and the values are 

#the coefficients. If res is not a dictionary, then 

#it is assumed to be an element of self 

if not isinstance(res, dict): 

if isinstance(res, self._element_class): 

res = res._monomial_coefficients 

else: 

z_elt[res] = z_elt.get(res, ABRzero) + coeffprod 

continue 

for m, c in six.iteritems(res): 

z_elt[m] = z_elt.get(m, ABRzero) + coeffprod * c 

 

#We assume that the user handles the multiplication correctly on 

#his or her own, and returns a dict with monomials as keys and 

#coefficients as values 

else: 

m = self._multiply(left, right) 

if isinstance(m, self._element_class): 

return m 

if not isinstance(m, dict): 

z_elt = m.monomial_coefficients() 

else: 

z_elt = m 

 

#Remove all entries that are equal to 0 

BR = self.base_ring() 

zero = BR(0) 

del_list = [] 

for m, c in six.iteritems(z_elt): 

if c == zero: 

del_list.append(m) 

for m in del_list: 

del z_elt[m] 

 

return self._from_dict(z_elt) 

 

class TestAlgebra(CombinatorialAlgebra): 

 

_name = "TestAlgebra" 

 

def __init__(self, R): 

""" 

TESTS:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: TestSuite(s).run() 

""" 

from sage.combinat.partition import Partition, Partitions 

self._one = Partition([]) 

CombinatorialAlgebra.__init__(self, R, cc=Partitions()) 

 

def _multiply_basis(self, part1, part2): 

""" 

TESTS:: 

 

sage: s = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: a = s([2]) 

sage: a._mul_(a) #indirect doctest 

s[2, 2] + s[3, 1] + s[4] 

""" 

from sage.combinat.sf.sf import SymmetricFunctions 

S = SymmetricFunctions(self.base_ring()).schur() 

return self.sum_of_terms(S(part1) * S(part2)) 

 

def prefix(self): 

""" 

TESTS:: 

 

sage: sa = sage.combinat.combinatorial_algebra.TestAlgebra(QQ) 

sage: x = sa([2]); x # indirect doctest 

s[2] 

""" 

return "s"