Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/free_module.py
4047 views
1
"""
2
Free modules
3
"""
4
#*****************************************************************************
5
# Copyright (C) 2007 Mike Hansen <[email protected]>,
6
# 2007-2009 Nicolas M. Thiery <nthiery at users.sf.net>
7
# 2010 Christian Stump <[email protected]>
8
#
9
# Distributed under the terms of the GNU General Public License (GPL)
10
# http://www.gnu.org/licenses/
11
#*****************************************************************************
12
from sage.structure.unique_representation import UniqueRepresentation
13
from sage.structure.element import Element
14
from sage.structure.parent import Parent
15
from sage.structure.sage_object import have_same_parent
16
from sage.modules.free_module_element import vector
17
from sage.misc.misc import repr_lincomb
18
from sage.modules.module import Module
19
from sage.rings.all import Integer
20
import sage.structure.element
21
from sage.combinat.family import Family
22
from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
23
from sage.combinat.cartesian_product import CartesianProduct
24
from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets
25
from sage.misc.cachefunc import cached_method
26
from sage.misc.all import lazy_attribute
27
from sage.categories.poor_man_map import PoorManMap
28
from sage.categories.all import ModulesWithBasis
29
from sage.combinat.dict_addition import dict_addition, dict_linear_combination
30
31
# TODO: move the content of this class to CombinatorialFreeModule.Element and ModulesWithBasis.Element
32
class CombinatorialFreeModuleElement(Element):
33
def __init__(self, M, x):
34
"""
35
Create a combinatorial module element. This should never be
36
called directly, but only through the parent combinatorial
37
free module's :meth:`__call__` method.
38
39
TESTS::
40
41
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
42
sage: B = F.basis()
43
sage: f = B['a'] + 3*B['c']; f
44
B['a'] + 3*B['c']
45
sage: f == loads(dumps(f))
46
True
47
"""
48
Element.__init__(self, M)
49
self._monomial_coefficients = x
50
51
def __iter__(self):
52
"""
53
EXAMPLES::
54
55
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
56
sage: B = F.basis()
57
sage: f = B['a'] + 3*B['c']
58
sage: [i for i in sorted(f)]
59
[('a', 1), ('c', 3)]
60
61
::
62
63
sage: s = SFASchur(QQ)
64
sage: a = s([2,1]) + s([3])
65
sage: [i for i in sorted(a)]
66
[([2, 1], 1), ([3], 1)]
67
"""
68
return self._monomial_coefficients.iteritems()
69
70
def __contains__(self, x):
71
"""
72
Returns whether or not a combinatorial object x indexing a basis
73
element is in the support of self.
74
75
EXAMPLES::
76
77
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
78
sage: B = F.basis()
79
sage: f = B['a'] + 3*B['c']
80
sage: 'a' in f
81
True
82
sage: 'b' in f
83
False
84
85
::
86
87
sage: s = SFASchur(QQ)
88
sage: a = s([2,1]) + s([3])
89
sage: Partition([2,1]) in a
90
True
91
sage: Partition([1,1,1]) in a
92
False
93
"""
94
return x in self._monomial_coefficients and self._monomial_coefficients[x] != 0
95
96
def monomial_coefficients(self):
97
"""
98
Return the internal dictionary which has the combinatorial objects
99
indexing the basis as keys and their corresponding coefficients as
100
values.
101
102
EXAMPLES::
103
104
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
105
sage: B = F.basis()
106
sage: f = B['a'] + 3*B['c']
107
sage: d = f.monomial_coefficients()
108
sage: d['a']
109
1
110
sage: d['c']
111
3
112
113
To run through the monomials of an element, it is better to
114
use the idiom::
115
116
sage: for (t,c) in f:
117
... print t,c
118
a 1
119
c 3
120
121
::
122
123
sage: s = SFASchur(QQ)
124
sage: a = s([2,1])+2*s([3,2])
125
sage: d = a.monomial_coefficients()
126
sage: type(d)
127
<type 'dict'>
128
sage: d[ Partition([2,1]) ]
129
1
130
sage: d[ Partition([3,2]) ]
131
2
132
"""
133
return self._monomial_coefficients
134
135
def _sorted_items_for_printing(self):
136
"""
137
Returns the items (i.e terms) of ``self``, sorted for printing
138
139
EXAMPLES::
140
141
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
142
sage: B = F.basis()
143
sage: f = B['a'] + 2*B['c'] + 3 * B['b']
144
sage: f._sorted_items_for_printing()
145
[('a', 1), ('b', 3), ('c', 2)]
146
sage: F.print_options(monomial_cmp = lambda x,y: -cmp(x,y))
147
sage: f._sorted_items_for_printing()
148
[('c', 2), ('b', 3), ('a', 1)]
149
150
.. seealso:: :meth:`_repr_`, :meth:`_latex_`, :meth:`print_options`
151
"""
152
print_options = self.parent().print_options()
153
v = self._monomial_coefficients.items()
154
try:
155
v.sort(cmp = print_options['monomial_cmp'],
156
key = lambda (monomial,coeff): monomial)
157
except StandardError: # Sorting the output is a plus, but if we can't, no big deal
158
pass
159
return v
160
161
def _repr_(self):
162
"""
163
EXAMPLES::
164
165
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='F')
166
sage: e = F.basis()
167
sage: e['a'] + 2*e['b'] # indirect doctest
168
F['a'] + 2*F['b']
169
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='')
170
sage: e = F.basis()
171
sage: e['a'] + 2*e['b'] # indirect doctest
172
['a'] + 2*['b']
173
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix='', scalar_mult=' ', bracket=False)
174
sage: e = F.basis()
175
sage: e['a'] + 2*e['b'] # indirect doctest
176
'a' + 2 'b'
177
178
Controling the order of terms by providing a comparison
179
function on elements of the support::
180
181
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'],
182
... monomial_cmp = lambda x,y: cmp(y,x))
183
sage: e = F.basis()
184
sage: e['a'] + 3*e['b'] + 2*e['c']
185
2*B['c'] + 3*B['b'] + B['a']
186
187
sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'],
188
... monomial_cmp = lambda x,y: cmp(x[1],y[1]))
189
sage: e = F.basis()
190
sage: e['ac'] + 3*e['ba'] + 2*e['cb']
191
3*B['ba'] + 2*B['cb'] + B['ac']
192
"""
193
return repr_lincomb(self._sorted_items_for_printing(),
194
scalar_mult=self.parent()._print_options['scalar_mult'],
195
repr_monomial = self.parent()._repr_term,
196
strip_one = True)
197
198
def _latex_(self):
199
"""
200
EXAMPLES::
201
202
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
203
sage: B = F.basis()
204
sage: f = B['a'] + 3*B['c']
205
sage: latex(f)
206
B_{a} + 3B_{c}
207
208
::
209
210
sage: QS3 = SymmetricGroupAlgebra(QQ,3)
211
sage: a = 2 + QS3([2,1,3])
212
sage: latex(a) #indirect doctest
213
2[1, 2, 3] + [2, 1, 3]
214
215
::
216
217
sage: F = CombinatorialFreeModule(QQ, ['a','b'], prefix='beta', latex_prefix='\\beta')
218
sage: x = F.an_element()
219
sage: x
220
2*beta['a'] + 2*beta['b']
221
sage: latex(x)
222
2\beta_{a} + 2\beta_{b}
223
224
Controling the order of terms by providing a comparison
225
function on elements of the support::
226
227
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'],
228
... monomial_cmp = lambda x,y: cmp(y,x))
229
sage: e = F.basis()
230
sage: latex(e['a'] + 3*e['b'] + 2*e['c'])
231
2B_{c} + 3B_{b} + B_{a}
232
233
sage: F = CombinatorialFreeModule(QQ, ['ac', 'ba', 'cb'],
234
... monomial_cmp = lambda x,y: cmp(x[1],y[1]))
235
sage: e = F.basis()
236
sage: latex(e['ac'] + 3*e['ba'] + 2*e['cb'])
237
3B_{ba} + 2B_{cb} + B_{ac}
238
"""
239
return repr_lincomb(self._sorted_items_for_printing(),
240
scalar_mult = self.parent()._print_options['scalar_mult'],
241
latex_scalar_mult = self.parent()._print_options['latex_scalar_mult'],
242
repr_monomial = self.parent()._latex_term,
243
is_latex=True, strip_one = True)
244
245
def __eq__(self, other):
246
"""
247
EXAMPLES::
248
249
sage: F1 = CombinatorialFreeModule(QQ, [1, 2, 3])
250
sage: F2 = CombinatorialFreeModule(QQ, [1, 2, 3], prefix = "g")
251
sage: F1.zero() == F1.zero()
252
True
253
sage: F1.zero() == F1.an_element()
254
False
255
sage: F1.an_element() == F1.an_element()
256
True
257
258
Currently, if ``self`` and ``other`` do not have the same
259
parent, coercions are attempted::
260
261
sage: F1.zero() == 0
262
True
263
sage: F1.zero() == F2.zero()
264
False
265
sage: F1.an_element() == None
266
False
267
268
sage: F = AlgebrasWithBasis(QQ).example()
269
sage: F.one() == 1
270
True
271
sage: 1 == F.one()
272
True
273
sage: 2 * F.one() == int(2)
274
True
275
sage: int(2) == 2 * F.one()
276
True
277
278
sage: S = SymmetricFunctions(QQ); s = S.s(); p = S.p()
279
sage: p[2] == s[2] - s[1, 1]
280
True
281
sage: p[2] == s[2]
282
False
283
284
This feature is disputable, in particular since it can make
285
equality testing costly. It may be removed at some point.
286
287
Equality testing can be a bit tricky when the order of terms
288
can vary because their indices are incomparable with
289
``cmp``. The following test did fail before :trac:`12489` ::
290
291
sage: F = CombinatorialFreeModule(QQ, Subsets([1,2,3]))
292
sage: x = F.an_element()
293
sage: (x+F.zero()).terms() # random
294
[2*B[{1}], 3*B[{2}], B[{}]]
295
sage: x.terms() # random
296
[2*B[{1}], B[{}], 3*B[{2}]]
297
sage: x+F.zero() == x
298
True
299
300
TESTS::
301
302
sage: TestSuite(F1).run()
303
sage: TestSuite(F).run()
304
"""
305
if have_same_parent(self, other):
306
return self._monomial_coefficients == other._monomial_coefficients
307
from sage.structure.element import get_coercion_model
308
import operator
309
try:
310
return get_coercion_model().bin_op(self, other, operator.eq)
311
except TypeError:
312
return False
313
314
def __ne__(left, right):
315
"""
316
EXAMPLES::
317
318
sage: F1 = CombinatorialFreeModule(QQ, ['a','b','c'])
319
sage: F1.an_element() != F1.an_element()
320
False
321
sage: F1.an_element() != F1.zero()
322
True
323
"""
324
return not left == right
325
326
def __cmp__(left, right):
327
"""
328
The ordering is the one on the underlying sorted list of
329
(monomial,coefficients) pairs.
330
331
EXAMPLES::
332
333
sage: s = SFASchur(QQ)
334
sage: a = s([2,1])
335
sage: b = s([1,1,1])
336
sage: cmp(a,b) #indirect doctest
337
1
338
"""
339
if have_same_parent(left, right) and left._monomial_coefficients == right._monomial_coefficients:
340
return 0
341
nonzero = lambda mc: mc[1] != 0
342
v = filter(nonzero, left._monomial_coefficients.items())
343
v.sort()
344
w = filter(nonzero, right._monomial_coefficients.items())
345
w.sort()
346
return cmp(v, w)
347
348
def _add_(self, other):
349
"""
350
EXAMPLES::
351
352
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
353
sage: B = F.basis()
354
sage: B['a'] + 3*B['c']
355
B['a'] + 3*B['c']
356
357
::
358
359
sage: s = SFASchur(QQ)
360
sage: s([2,1]) + s([5,4]) # indirect doctest
361
s[2, 1] + s[5, 4]
362
sage: a = s([2,1]) + 0
363
sage: len(a.monomial_coefficients())
364
1
365
"""
366
367
assert hasattr( other, 'parent' ) and other.parent() == self.parent()
368
369
F = self.parent()
370
return F._from_dict( dict_addition( [ self._monomial_coefficients, other._monomial_coefficients ] ), remove_zeros=False )
371
372
def _neg_(self):
373
"""
374
EXAMPLES::
375
376
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
377
sage: B = F.basis()
378
sage: f = B['a'] + 3*B['c']
379
sage: -f
380
-B['a'] - 3*B['c']
381
382
::
383
384
sage: s = SFASchur(QQ)
385
sage: -s([2,1]) # indirect doctest
386
-s[2, 1]
387
"""
388
F = self.parent()
389
return F._from_dict( dict_linear_combination( [ ( self._monomial_coefficients, -1 ) ] ), remove_zeros=False )
390
391
def _sub_(self, other):
392
"""
393
EXAMPLES::
394
395
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
396
sage: B = F.basis()
397
sage: B['a'] - 3*B['c']
398
B['a'] - 3*B['c']
399
400
::
401
402
sage: s = SFASchur(QQ)
403
sage: s([2,1]) - s([5,4]) # indirect doctest
404
s[2, 1] - s[5, 4]
405
"""
406
assert hasattr( other, 'parent' ) and other.parent() == self.parent()
407
F = self.parent()
408
return F._from_dict( dict_linear_combination( [ ( self._monomial_coefficients, 1 ), (other._monomial_coefficients, -1 ) ] ), remove_zeros=False )
409
410
def _coefficient_fast(self, m, default=None):
411
"""
412
Returns the coefficient of m in self, where m is key in
413
self._monomial_coefficients.
414
415
EXAMPLES::
416
417
sage: p = Partition([2,1])
418
sage: q = Partition([1,1,1])
419
sage: s = SFASchur(QQ)
420
sage: a = s(p)
421
sage: a._coefficient_fast([2,1])
422
Traceback (most recent call last):
423
...
424
TypeError: unhashable type: 'list'
425
426
::
427
428
sage: a._coefficient_fast(p)
429
1
430
sage: a._coefficient_fast(p, 2)
431
1
432
sage: a._coefficient_fast(q)
433
0
434
sage: a._coefficient_fast(q, 2)
435
2
436
sage: a[p]
437
1
438
sage: a[q]
439
0
440
"""
441
if default is None:
442
default = self.base_ring()(0)
443
return self._monomial_coefficients.get(m, default)
444
445
__getitem__ = _coefficient_fast
446
447
def coefficient(self, m):
448
"""
449
EXAMPLES::
450
451
sage: s = CombinatorialFreeModule(QQ, Partitions())
452
sage: z = s([4]) - 2*s([2,1]) + s([1,1,1]) + s([1])
453
sage: z.coefficient([4])
454
1
455
sage: z.coefficient([2,1])
456
-2
457
sage: z.coefficient(Partition([2,1]))
458
-2
459
sage: z.coefficient([1,2])
460
Traceback (most recent call last):
461
...
462
AssertionError: [1, 2] should be an element of Partitions
463
sage: z.coefficient(Composition([2,1]))
464
Traceback (most recent call last):
465
...
466
AssertionError: [2, 1] should be an element of Partitions
467
468
Test that coefficient also works for those parents that do not yet have an element_class::
469
470
sage: G = DihedralGroup(3)
471
sage: F = CombinatorialFreeModule(QQ, G)
472
sage: hasattr(G, "element_class")
473
False
474
sage: g = G.an_element()
475
sage: (2*F.monomial(g)).coefficient(g)
476
2
477
"""
478
# NT: coefficient_fast should be the default, just with appropriate assertions
479
# that can be turned on or off
480
C = self.parent()._basis_keys
481
assert m in C, "%s should be an element of %s"%(m, C)
482
if hasattr(C, "element_class") and not isinstance(m, C.element_class):
483
m = C(m)
484
return self._coefficient_fast(m)
485
486
487
def is_zero(self):
488
"""
489
Returns True if and only self == 0.
490
491
EXAMPLES::
492
493
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
494
sage: B = F.basis()
495
sage: f = B['a'] - 3*B['c']
496
sage: f.is_zero()
497
False
498
sage: F.zero().is_zero()
499
True
500
501
::
502
503
sage: s = SFASchur(QQ)
504
sage: s([2,1]).is_zero()
505
False
506
sage: s(0).is_zero()
507
True
508
sage: (s([2,1]) - s([2,1])).is_zero()
509
True
510
"""
511
BR = self.parent().base_ring()
512
zero = BR( 0 )
513
return all( v == zero for v in self._monomial_coefficients.values() )
514
515
def __len__(self):
516
"""
517
Returns the number of basis elements of self with nonzero
518
coefficients.
519
520
EXAMPLES::
521
522
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
523
sage: B = F.basis()
524
sage: f = B['a'] - 3*B['c']
525
sage: len(f)
526
2
527
528
::
529
530
sage: s = SFASchur(QQ)
531
sage: z = s([4]) + s([2,1]) + s([1,1,1]) + s([1])
532
sage: len(z)
533
4
534
"""
535
return self.length()
536
537
def length(self):
538
"""
539
Returns the number of basis elements of self with nonzero
540
coefficients.
541
542
EXAMPLES::
543
544
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
545
sage: B = F.basis()
546
sage: f = B['a'] - 3*B['c']
547
sage: f.length()
548
2
549
550
::
551
552
sage: s = SFASchur(QQ)
553
sage: z = s([4]) + s([2,1]) + s([1,1,1]) + s([1])
554
sage: z.length()
555
4
556
"""
557
BR = self.parent().base_ring()
558
zero = BR( 0 )
559
return len( [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ] )
560
561
def support(self):
562
"""
563
Returns a list of the combinatorial objects indexing the basis
564
elements of self which non-zero coefficients.
565
566
EXAMPLES::
567
568
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
569
sage: B = F.basis()
570
sage: f = B['a'] - 3*B['c']
571
sage: f.support()
572
['a', 'c']
573
574
::
575
576
sage: s = SFASchur(QQ)
577
sage: z = s([4]) + s([2,1]) + s([1,1,1]) + s([1])
578
sage: z.support()
579
[[1], [1, 1, 1], [2, 1], [4]]
580
"""
581
BR = self.parent().base_ring()
582
zero = BR( 0 )
583
584
supp = [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ]
585
supp.sort()
586
587
return supp
588
589
def monomials(self):
590
"""
591
EXAMPLES::
592
593
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
594
sage: B = F.basis()
595
sage: f = B['a'] + 2*B['c']
596
sage: f.monomials()
597
[B['a'], B['c']]
598
"""
599
P = self.parent()
600
BR = P.base_ring()
601
zero = BR( 0 )
602
one = BR( 1 )
603
604
supp = [ key for key, coeff in self._monomial_coefficients.iteritems() if coeff != zero ]
605
supp.sort()
606
607
return [ P._from_dict( { key : one }, remove_zeros=False ) for key in supp ]
608
609
def terms(self):
610
"""
611
Returns a list of the terms of ``self``
612
613
.. seealso:: :meth:`monomials`
614
615
EXAMPLES::
616
617
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
618
sage: B = F.basis()
619
sage: f = B['a'] + 2*B['c']
620
sage: f.terms()
621
[B['a'], 2*B['c']]
622
"""
623
BR = self.parent().base_ring()
624
zero = BR( 0 )
625
v = [ ( key, value ) for key, value in self._monomial_coefficients.iteritems() if value != zero ]
626
v.sort()
627
from_dict = self.parent()._from_dict
628
return [ from_dict( { key : value } ) for key,value in v ]
629
630
def coefficients(self):
631
"""
632
Returns a list of the coefficients appearing on the basis elements in
633
self.
634
635
EXAMPLES::
636
637
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
638
sage: B = F.basis()
639
sage: f = B['a'] - 3*B['c']
640
sage: f.coefficients()
641
[1, -3]
642
643
::
644
645
sage: s = SFASchur(QQ)
646
sage: z = s([4]) + s([2,1]) + s([1,1,1]) + s([1])
647
sage: z.coefficients()
648
[1, 1, 1, 1]
649
"""
650
BR = self.parent().base_ring()
651
zero = BR( 0 )
652
v = [ ( key, value ) for key, value in self._monomial_coefficients.iteritems() if value != zero ]
653
v.sort()
654
return [ value for key,value in v ]
655
656
def _vector_(self, new_base_ring=None):
657
"""
658
Returns a vector version of self. If ''new_base_ring'' is specified,
659
then in returns a vector over ''new_base_ring''.
660
661
EXAMPLES::
662
663
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
664
sage: B = F.basis()
665
sage: f = B['a'] - 3*B['c']
666
sage: vector(f)
667
(1, 0, -3)
668
669
::
670
671
sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
672
sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1])
673
sage: a._vector_()
674
(2, 0, 0, 0, 0, 4)
675
sage: vector(a)
676
(2, 0, 0, 0, 0, 4)
677
sage: a._vector_(RDF)
678
(2.0, 0.0, 0.0, 0.0, 0.0, 4.0)
679
"""
680
parent = self.parent()
681
cc = parent.get_order()
682
683
if new_base_ring is None:
684
new_base_ring = parent.base_ring()
685
# FIXME: should return a sparse vector
686
return vector(new_base_ring,
687
[new_base_ring(self._monomial_coefficients.get(m, 0))
688
for m in list(cc)])
689
690
def to_vector(self):
691
"""
692
Returns a vector version of self.
693
694
EXAMPLES::
695
696
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
697
sage: B = F.basis()
698
sage: f = B['a'] - 3*B['c']
699
sage: f.to_vector()
700
(1, 0, -3)
701
702
::
703
704
sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
705
sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1])
706
sage: a.to_vector()
707
(2, 0, 0, 0, 0, 4)
708
sage: a == QS3.from_vector(a.to_vector())
709
True
710
"""
711
return self._vector_()
712
713
def _acted_upon_(self, scalar, self_on_left = False):
714
"""
715
Returns the action of a scalar on self
716
717
EXAMPLES::
718
719
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
720
sage: B = F.basis()
721
sage: B['a']*(1/2) # indirect doctest
722
1/2*B['a']
723
sage: B['a']/2
724
1/2*B['a']
725
sage: B['a']*2 # indirect doctest
726
2*B['a']
727
sage: B['a']*int(2) # indirect doctest
728
2*B['a']
729
730
sage: 1/2*B['a']
731
1/2*B['a']
732
sage: 2*B['a'] # indirect doctest
733
2*B['a']
734
sage: int(2)*B['a'] # indirect doctest
735
2*B['a']
736
737
TESTS::
738
739
sage: F.get_action(QQ, operator.mul, True)
740
Right action by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field
741
sage: F.get_action(QQ, operator.mul, False)
742
Left action by Rational Field on Free module generated by {'a', 'b', 'c'} over Rational Field
743
sage: F.get_action(ZZ, operator.mul, True)
744
Right action by Integer Ring on Free module generated by {'a', 'b', 'c'} over Rational Field
745
sage: F.get_action(F, operator.mul, True)
746
sage: F.get_action(F, operator.mul, False)
747
748
This also works when a coercion of the coefficient is needed, for
749
example with polynomials or fraction fields #8832::
750
751
sage: P.<q> = QQ['q']
752
sage: V = CombinatorialFreeModule(P, Permutations())
753
sage: el = V(Permutation([3,1,2]))
754
sage: (3/2)*el
755
3/2*B[[3, 1, 2]]
756
757
sage: P.<q> = QQ['q']
758
sage: F = FractionField(P)
759
sage: V = CombinatorialFreeModule(F, Words())
760
sage: w = Words()('abc')
761
sage: (1+q)*V(w)
762
(q+1)*B[word: abc]
763
sage: ((1+q)/q)*V(w)
764
((q+1)/q)*B[word: abc]
765
766
TODO:
767
- add non commutative tests
768
"""
769
# With the current design, the coercion model does not have
770
# enough information to detect apriori that this method only
771
# accepts scalars; so it tries on some elements(), and we need
772
# to make sure to report an error.
773
if hasattr( scalar, 'parent' ) and scalar.parent() != self.base_ring():
774
# Temporary needed by coercion (see Polynomial/FractionField tests).
775
if self.base_ring().has_coerce_map_from(scalar.parent()):
776
scalar = self.base_ring()( scalar )
777
else:
778
return None
779
780
F = self.parent()
781
D = self._monomial_coefficients
782
if self_on_left:
783
D = dict_linear_combination( [ ( D, scalar ) ], factor_on_left = False )
784
else:
785
D = dict_linear_combination( [ ( D, scalar ) ] )
786
787
return F._from_dict( D, remove_zeros=False )
788
789
# For backward compatibility
790
_lmul_ = _acted_upon_
791
_rmul_ = _acted_upon_
792
793
def __div__(self, x, self_on_left=False ):
794
"""
795
Division by coefficients
796
797
EXAMPLES::
798
799
sage: F = CombinatorialFreeModule(QQ, [1,2,3])
800
sage: x = F._from_dict({1:2, 2:3})
801
sage: x/2
802
B[1] + 3/2*B[2]
803
804
::
805
806
sage: F = CombinatorialFreeModule(QQ, [1,2,3])
807
sage: B = F.basis()
808
sage: f = 2*B[2] + 4*B[3]
809
sage: f/2
810
B[2] + 2*B[3]
811
"""
812
if self.base_ring().is_field():
813
F = self.parent()
814
x = self.base_ring()( x )
815
x_inv = x**-1
816
D = self._monomial_coefficients
817
if self_on_left:
818
D = dict_linear_combination( [ ( D, x_inv ) ], factor_on_left=False )
819
else:
820
D = dict_linear_combination( [ ( D, x_inv ) ] )
821
822
return F._from_dict( D, remove_zeros=False )
823
else:
824
return self.map_coefficients(lambda c: _divide_if_possible(c, x))
825
826
def _divide_if_possible(x, y):
827
"""
828
EXAMPLES::
829
830
sage: from sage.combinat.free_module import _divide_if_possible
831
sage: _divide_if_possible(4, 2)
832
2
833
sage: _.parent()
834
Integer Ring
835
836
::
837
838
sage: _divide_if_possible(4, 3)
839
Traceback (most recent call last):
840
...
841
ValueError: 4 is not divisible by 3
842
"""
843
q, r = x.quo_rem(y)
844
if r != 0:
845
raise ValueError, "%s is not divisible by %s"%(x, y)
846
else:
847
return q
848
849
class CombinatorialFreeModule(UniqueRepresentation, Module):
850
r"""
851
Class for free modules with a named basis
852
853
INPUT:
854
855
- ``R`` - base ring
856
857
- ``basis_keys`` - list, tuple, family, set, etc. defining the
858
indexing set for the basis of this module
859
860
- ``element_class`` - the class of which elements of this module
861
should be instances (optional, default None, in which case the
862
elements are instances of
863
:class:`CombinatorialFreeModuleElement`)
864
865
- ``category`` - the category in which this module lies (optional,
866
default None, in which case use the "category of modules with
867
basis" over the base ring ``R``)
868
869
Options controlling the printing of elements:
870
871
- ``prefix`` - string, prefix used for printing elements of this
872
module (optional, default 'B'). With the default, a monomial
873
indexed by 'a' would be printed as ``B['a']``.
874
875
- ``latex_prefix`` - string or None, prefix used in the LaTeX
876
representation of elements (optional, default None). If this is
877
anything except the empty string, it prints the index as a
878
subscript. If this is None, it uses the setting for ``prefix``,
879
so if ``prefix`` is set to "B", then a monomial indexed by 'a'
880
would be printed as ``B_{a}``. If this is the empty string, then
881
don't print monomials as subscripts: the monomial indexed by 'a'
882
would be printed as ``a``, or as ``[a]`` if ``latex_bracket`` is
883
True.
884
885
- ``bracket`` - None, bool, string, or list or tuple of
886
strings (optional, default None): if None, use the value of the
887
attribute ``self._repr_option_bracket``, which has default value
888
True. (``self._repr_option_bracket`` is available for backwards
889
compatibility. Users should set ``bracket`` instead. If
890
``bracket`` is set to anything except None, it overrides
891
the value of ``self._repr_option_bracket``.) If False, do not
892
include brackets when printing elements: a monomial indexed by
893
'a' would be printed as ``B'a'``, and a monomial indexed by
894
(1,2,3) would be printed as ``B(1,2,3)``. If True, use "[" and
895
"]" as brackets. If it is one of "[", "(", or "{", use it and
896
its partner as brackets. If it is any other string, use it as
897
both brackets. If it is a list or tuple of strings, use the
898
first entry as the left bracket and the second entry as the
899
right bracket.
900
901
- ``latex_bracket`` - bool, string, or list or tuple of strings
902
(optional, default False): if False, do not include brackets in
903
the LaTeX representation of elements. This option is only
904
relevant if ``latex_prefix`` is the empty string; otherwise,
905
brackets are not used regardless. If True, use "\\left[" and
906
"\\right]" as brackets. If this is one of "[", "(", "\\{", "|",
907
or "||", use it and its partner, prepended with "\\left" and
908
"\\right", as brackets. If this is any other string, use it as
909
both brackets. If this is a list or tuple of strings, use the
910
first entry as the left bracket and the second entry as the
911
right bracket.
912
913
- ``scalar_mult`` - string to use for scalar multiplication in
914
the print representation (optional, default "*")
915
916
- ``latex_scalar_mult`` - string or None (optional, default None),
917
string to use for scalar multiplication in the latex
918
representation. If None, use the empty string if ``scalar_mult``
919
is set to "*", otherwise use the value of ``scalar_mult``.
920
921
- ``tensor_symbol`` - string or None (optional, default None),
922
string to use for tensor product in the print representation. If
923
None, use the ``sage.categories.tensor.symbol``.
924
925
- ``monomial_cmp`` - a comparison function (optional, default cmp),
926
to use for sorting elements in the output of elements
927
928
.. note:: These print options may also be accessed and modified using the
929
:meth:`print_options` method, after the module has been defined.
930
931
EXAMPLES:
932
933
We construct a free module whose basis is indexed by the letters a, b, c::
934
935
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
936
sage: F
937
Free module generated by {'a', 'b', 'c'} over Rational Field
938
939
Its basis is a family, indexed by a, b, c::
940
941
sage: e = F.basis()
942
sage: e
943
Finite family {'a': B['a'], 'c': B['c'], 'b': B['b']}
944
945
::
946
947
sage: [x for x in e]
948
[B['a'], B['b'], B['c']]
949
sage: [k for k in e.keys()]
950
['a', 'b', 'c']
951
952
Let us construct some elements, and compute with them::
953
954
sage: e['a']
955
B['a']
956
sage: 2*e['a']
957
2*B['a']
958
sage: e['a'] + 3*e['b']
959
B['a'] + 3*B['b']
960
961
Some uses of
962
:meth:`sage.categories.commutative_additive_semigroups.CommutativeAdditiveSemigroups.ParentMethods.summation`
963
and :meth:`.sum`::
964
965
sage: F = CombinatorialFreeModule(QQ, [1,2,3,4])
966
sage: F.summation(F.monomial(1), F.monomial(3))
967
B[1] + B[3]
968
969
sage: F = CombinatorialFreeModule(QQ, [1,2,3,4])
970
sage: F.sum(F.monomial(i) for i in [1,2,3])
971
B[1] + B[2] + B[3]
972
973
Note that free modules with a given basis and parameters are unique::
974
975
sage: F1 = CombinatorialFreeModule(QQ, (1,2,3,4))
976
sage: F1 is F
977
True
978
979
The constructed free module depends on the order of the basis and
980
on the other parameters, like the prefix::
981
982
sage: F1 = CombinatorialFreeModule(QQ, (1,2,3,4))
983
sage: F1 is F
984
True
985
sage: F1 = CombinatorialFreeModule(QQ, [4,3,2,1])
986
sage: F1 == F
987
False
988
sage: F2 = CombinatorialFreeModule(QQ, [1,2,3,4], prefix='F')
989
sage: F2 == F
990
False
991
992
Because of this, if you create a free module with certain
993
parameters and then modify its prefix or other print options, this
994
affects all modules which were defined using the same parameters::
995
996
sage: F2.print_options(prefix='x')
997
sage: F2.prefix()
998
'x'
999
sage: F3 = CombinatorialFreeModule(QQ, [1,2,3,4], prefix='F')
1000
sage: F3 is F2 # F3 was defined just like F2
1001
True
1002
sage: F3.prefix()
1003
'x'
1004
sage: F4 = CombinatorialFreeModule(QQ, [1,2,3,4], prefix='F', bracket=True)
1005
sage: F4 == F2 # F4 was NOT defined just like F2
1006
False
1007
sage: F4.prefix()
1008
'F'
1009
1010
The default category is the category of modules with basis over
1011
the base ring::
1012
1013
sage: CombinatorialFreeModule(GF(3), ((1,2), (3,4))).category()
1014
Category of modules with basis over Finite Field of size 3
1015
1016
See :mod:`sage.categories.examples.algebras_with_basis` and
1017
:mod:`sage.categories.examples.hopf_algebras_with_basis` for
1018
illustrations of the use of the ``category`` keyword, and see
1019
:class:`sage.combinat.root_system.weight_space.WeightSpace` for an
1020
example of the use of ``element_class``.
1021
1022
Customizing print and LaTeX representations of elements::
1023
1024
sage: F = CombinatorialFreeModule(QQ, ['a','b'], prefix='x')
1025
sage: e = F.basis()
1026
sage: e['a'] - 3 * e['b']
1027
x['a'] - 3*x['b']
1028
1029
sage: F.print_options(prefix='x', scalar_mult=' ', bracket='{')
1030
sage: e['a'] - 3 * e['b']
1031
x{'a'} - 3 x{'b'}
1032
sage: latex(e['a'] - 3 * e['b'])
1033
x_{a} + \left(-3\right) x_{b}
1034
1035
sage: F.print_options(latex_prefix='y')
1036
sage: latex(e['a'] - 3 * e['b'])
1037
y_{a} + \left(-3\right) y_{b}
1038
1039
sage: F.print_options(monomial_cmp = lambda x,y: -cmp(x,y))
1040
sage: e['a'] - 3 * e['b']
1041
-3 x{'b'} + x{'a'}
1042
1043
sage: F = CombinatorialFreeModule(QQ, [(1,2), (3,4)])
1044
sage: e = F.basis()
1045
sage: e[(1,2)] - 3 * e[(3,4)]
1046
B[(1, 2)] - 3*B[(3, 4)]
1047
1048
sage: F.print_options(bracket=['_{', '}'])
1049
sage: e[(1,2)] - 3 * e[(3,4)]
1050
B_{(1, 2)} - 3*B_{(3, 4)}
1051
1052
sage: F.print_options(prefix='', bracket=False)
1053
sage: e[(1,2)] - 3 * e[(3,4)]
1054
(1, 2) - 3*(3, 4)
1055
1056
"""
1057
1058
@staticmethod
1059
def __classcall_private__(cls, base_ring, basis_keys, category = None, **keywords):
1060
"""
1061
TESTS::
1062
1063
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
1064
sage: G = CombinatorialFreeModule(QQ, ('a','b','c'))
1065
sage: F is G
1066
True
1067
1068
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'], latex_bracket=['LEFT', 'RIGHT'])
1069
sage: F.print_options()['latex_bracket']
1070
('LEFT', 'RIGHT')
1071
1072
sage: F is G
1073
False
1074
1075
We check that the category is properly straightened::
1076
1077
sage: F = CombinatorialFreeModule(QQ, ['a','b'])
1078
sage: F1 = CombinatorialFreeModule(QQ, ['a','b'], category = ModulesWithBasis(QQ))
1079
sage: F2 = CombinatorialFreeModule(QQ, ['a','b'], category = [ModulesWithBasis(QQ)])
1080
sage: F3 = CombinatorialFreeModule(QQ, ['a','b'], category = (ModulesWithBasis(QQ),))
1081
sage: F4 = CombinatorialFreeModule(QQ, ['a','b'], category = (ModulesWithBasis(QQ),CommutativeAdditiveSemigroups()))
1082
sage: F5 = CombinatorialFreeModule(QQ, ['a','b'], category = (ModulesWithBasis(QQ),Category.join((LeftModules(QQ), RightModules(QQ)))))
1083
sage: F1 is F, F2 is F, F3 is F, F4 is F, F5 is F
1084
(True, True, True, True, True)
1085
1086
sage: G = CombinatorialFreeModule(QQ, ['a','b'], category = AlgebrasWithBasis(QQ))
1087
sage: F is G
1088
False
1089
"""
1090
if isinstance(basis_keys, (list, tuple)):
1091
basis_keys = FiniteEnumeratedSet(basis_keys)
1092
category = ModulesWithBasis(base_ring).or_subcategory(category)
1093
# bracket or latex_bracket might be lists, so convert
1094
# them to tuples so that they're hashable.
1095
bracket = keywords.get('bracket', None)
1096
if isinstance(bracket, list):
1097
keywords['bracket'] = tuple(bracket)
1098
latex_bracket = keywords.get('latex_bracket', None)
1099
if isinstance(latex_bracket, list):
1100
keywords['latex_bracket'] = tuple(latex_bracket)
1101
return super(CombinatorialFreeModule, cls).__classcall__(cls, base_ring, basis_keys, category = category, **keywords)
1102
1103
Element = CombinatorialFreeModuleElement
1104
1105
def __init__(self, R, basis_keys, element_class = None, category = None, **kwds):
1106
r"""
1107
TESTS::
1108
1109
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
1110
1111
sage: F.category()
1112
Category of modules with basis over Rational Field
1113
1114
One may specify the category this module belongs to::
1115
1116
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'], category=AlgebrasWithBasis(QQ))
1117
sage: F.category()
1118
Category of algebras with basis over Rational Field
1119
1120
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'], category = FiniteDimensionalModulesWithBasis(QQ))
1121
sage: F.basis()
1122
Finite family {'a': B['a'], 'c': B['c'], 'b': B['b']}
1123
sage: F.category()
1124
Category of finite dimensional modules with basis over Rational Field
1125
sage: TestSuite(F).run()
1126
1127
TESTS:
1128
1129
Regression test for #10127: ``self._basis_keys`` needs to be
1130
set early enough, in case the initialization of the categories
1131
use ``self.basis().keys()``. This occured on several occasions
1132
in non trivial constructions. In the following example,
1133
:class:`AlgebrasWithBasis` constructs ``Homset(self,self)`` to
1134
extend by bilinearity method ``product_on_basis``, which in
1135
turn triggers ``self._repr_()``::
1136
1137
sage: class MyAlgebra(CombinatorialFreeModule):
1138
... def _repr_(self):
1139
... return "MyAlgebra on %s"%(self.basis().keys())
1140
... def product_on_basis(self,i,j):
1141
... pass
1142
sage: MyAlgebra(ZZ, ZZ, category = AlgebrasWithBasis(QQ))
1143
MyAlgebra on Integer Ring
1144
1145
A simpler example would be welcome!
1146
1147
We check that unknown options are caught::
1148
1149
sage: CombinatorialFreeModule(ZZ, [1,2,3], keyy=2)
1150
Traceback (most recent call last):
1151
...
1152
ValueError: keyy is not a valid print option.
1153
"""
1154
#Make sure R is a ring with unit element
1155
from sage.categories.all import Rings
1156
if R not in Rings():
1157
raise TypeError, "Argument R must be a ring."
1158
1159
if category is None:
1160
category = ModulesWithBasis(R)
1161
1162
if element_class is not None:
1163
self.Element = element_class
1164
1165
# The following is needed by e.g. root systems that don't call
1166
# the classcall and passes lists as basis_keys
1167
if isinstance(basis_keys, (list, tuple)):
1168
basis_keys = FiniteEnumeratedSet(basis_keys)
1169
if not hasattr(self, "_name"):
1170
self._name = "Free module generated by %s"%(basis_keys,) # note: cc may be a tuple!
1171
self._basis_keys = basis_keys # Needs to be done early: #10127
1172
1173
Parent.__init__(self, base = R, category = category,
1174
# Could we get rid of this?
1175
element_constructor = self._element_constructor_)
1176
1177
1178
self._order = None
1179
1180
# printing options for elements (set when initializing self).
1181
# This includes self._repr_option_bracket (kept for backwards
1182
# compatibility, declared to be True by default, needs to be
1183
# overridden explicitly).
1184
self._print_options = {'prefix': "B",
1185
'bracket': None,
1186
'latex_bracket': False,
1187
'latex_prefix': None,
1188
'scalar_mult': "*",
1189
'latex_scalar_mult': None,
1190
'tensor_symbol': None,
1191
'monomial_cmp': cmp}
1192
# 'bracket': its default value here is None, meaning that
1193
# the value of self._repr_option_bracket is used; the default
1194
# value of that attribute is True -- see immediately before
1195
# the method _repr_term. If 'bracket' is any value
1196
# except None, then it overrides the value of
1197
# self._repr_option_bracket. Future users might consider
1198
# using 'bracket' instead of _repr_option_bracket.
1199
1200
# ignore the optional 'key' since it only affects UniqueRepresentation
1201
kwds.pop('key', None)
1202
self.print_options(**kwds)
1203
1204
# mostly for backward compatibility
1205
@lazy_attribute
1206
def _element_class(self):
1207
"""
1208
TESTS::
1209
1210
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
1211
sage: F._element_class
1212
<class 'sage.combinat.free_module.CombinatorialFreeModule_with_category.element_class'>
1213
"""
1214
return self.element_class
1215
1216
@cached_method
1217
def basis(self):
1218
"""
1219
Returns the basis of self.
1220
1221
EXAMPLES::
1222
1223
sage: F = CombinatorialFreeModule(QQ, ['a','b','c'])
1224
sage: F.basis()
1225
Finite family {'a': B['a'], 'c': B['c'], 'b': B['b']}
1226
1227
::
1228
1229
sage: QS3 = SymmetricGroupAlgebra(QQ,3)
1230
sage: list(QS3.basis())
1231
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
1232
"""
1233
return Family(self._basis_keys, self.monomial) #.
1234
1235
def _an_element_(self):
1236
"""
1237
EXAMPLES::
1238
1239
sage: CombinatorialFreeModule(QQ, ("a", "b", "c")).an_element()
1240
2*B['a'] + 2*B['b'] + 3*B['c']
1241
sage: CombinatorialFreeModule(QQ, ("a", "b", "c"))._an_element_()
1242
2*B['a'] + 2*B['b'] + 3*B['c']
1243
sage: CombinatorialFreeModule(QQ, ()).an_element()
1244
0
1245
sage: CombinatorialFreeModule(QQ, ZZ).an_element()
1246
3*B[-1] + B[0] + 3*B[1]
1247
sage: CombinatorialFreeModule(QQ, RR).an_element()
1248
B[1.00000000000000]
1249
"""
1250
# Try a couple heuristics to build a not completely trivial
1251
# element, while handling cases where R.an_element is not
1252
# implemented, or R has no iterator, or R has few elements.
1253
x = self.zero()
1254
I = self.basis().keys()
1255
R = self.base_ring()
1256
try:
1257
x = x + self.monomial(I.an_element())
1258
except:
1259
pass
1260
try:
1261
g = iter(self.basis().keys())
1262
for c in range(1,4):
1263
x = x + self.term(g.next(), R(c))
1264
except:
1265
pass
1266
return x
1267
1268
# What semantic do we want for containment?
1269
# Accepting anything that can be coerced is not reasonnable, especially
1270
# if we allow coercion from the enumerated set.
1271
# Accepting anything that can be converted is an option, but that would
1272
# be expensive. So far, x in self if x.parent() == self
1273
1274
def __contains__(self, x):
1275
"""
1276
TESTS::
1277
1278
sage: F = CombinatorialFreeModule(QQ,["a", "b"])
1279
sage: G = CombinatorialFreeModule(ZZ,["a", "b"])
1280
sage: F.monomial("a") in F
1281
True
1282
sage: G.monomial("a") in F
1283
False
1284
sage: "a" in F
1285
False
1286
sage: 5/3 in F
1287
False
1288
"""
1289
return sage.structure.element.parent(x) == self # is self?
1290
1291
def _element_constructor_(self, x):
1292
"""
1293
Coerce x into self.
1294
1295
EXAMPLES::
1296
1297
sage: F = CombinatorialFreeModule(QQ,["a", "b"])
1298
sage: F(F.monomial("a")) # indirect doctest
1299
B['a']
1300
1301
Do not rely on the following feature which may be removed in the future::
1302
1303
sage: QS3 = SymmetricGroupAlgebra(QQ,3)
1304
sage: QS3([2,3,1]) # indirect doctest
1305
[2, 3, 1]
1306
1307
instead, use::
1308
1309
sage: P = QS3.basis().keys()
1310
sage: QS3.monomial(P([2,3,1])) # indirect doctest
1311
[2, 3, 1]
1312
1313
or:
1314
sage: B = QS3.basis()
1315
sage: B[P([2,3,1])]
1316
[2, 3, 1]
1317
1318
TODO: The symmetric group algebra (and in general,
1319
combinatorial free modules on word-like object could instead
1320
provide an appropriate short-hand syntax QS3[2,3,1]).
1321
1322
Rationale: this conversion is ambiguous in situations like::
1323
1324
sage: F = CombinatorialFreeModule(QQ,[0,1])
1325
1326
Is ``0`` the zero of the base ring, or the index of a basis
1327
element? I.e. should the result be ``0`` or ``B[0]``?
1328
1329
sage: F = CombinatorialFreeModule(QQ,[0,1])
1330
sage: F(0) # this feature may eventually disappear
1331
0
1332
sage: F(1)
1333
Traceback (most recent call last):
1334
...
1335
TypeError: do not know how to make x (= 1) an element of Free module generated by ... over Rational Field
1336
1337
It is preferable not to rely either on the above, and instead, use::
1338
1339
sage: F.zero()
1340
0
1341
1342
Note that, on the other hand, conversions from the ground ring
1343
are systematically defined (and mathematically meaningful) for
1344
algebras.
1345
1346
Conversions between distinct free modules are not allowed any
1347
more::
1348
1349
sage: F = CombinatorialFreeModule(ZZ, ["a", "b"]); F.rename("F")
1350
sage: G = CombinatorialFreeModule(QQ, ["a", "b"]); G.rename("G")
1351
sage: H = CombinatorialFreeModule(ZZ, ["a", "b", "c"]); H.rename("H")
1352
sage: G(F.monomial("a"))
1353
Traceback (most recent call last):
1354
...
1355
TypeError: do not know how to make x (= B['a']) an element of self (=G)
1356
sage: H(F.monomial("a"))
1357
Traceback (most recent call last):
1358
...
1359
TypeError: do not know how to make x (= B['a']) an element of self (=H)
1360
1361
Here is a real life example illustrating that this yielded
1362
mathematically wrong results::
1363
1364
sage: S = SymmetricFunctions(QQ)
1365
sage: s = S.s(); p = S.p()
1366
sage: ss = tensor([s,s]); pp = tensor([p,p])
1367
sage: a = tensor((s[5],s[5]))
1368
sage: pp(a) # used to yield p[[5]] # p[[5]]
1369
Traceback (most recent call last):
1370
...
1371
NotImplementedError
1372
1373
Extensions of the ground ring should probably be reintroduced
1374
at some point, but via coercions, and with stronger sanity
1375
checks (ensuring that the codomain is really obtained by
1376
extending the scalar of the domain; checking that they share
1377
the same class is not sufficient).
1378
1379
TESTS:
1380
1381
Conversion from the ground ring is implemented for algebras::
1382
1383
sage: QS3 = SymmetricGroupAlgebra(QQ,3)
1384
sage: QS3(2)
1385
2*[1, 2, 3]
1386
"""
1387
R = self.base_ring()
1388
eclass = self.element_class
1389
1390
#Coerce ints to Integers
1391
if isinstance(x, int):
1392
x = Integer(x)
1393
1394
# if hasattr(self, '_coerce_start'):
1395
# try:
1396
# return self._coerce_start(x)
1397
# except TypeError:
1398
# pass
1399
1400
# x is an element of the same type of combinatorial free module
1401
# (disabled: this yields mathematically wrong results)
1402
#if hasattr(x, 'parent') and x.parent().__class__ is self.__class__:
1403
# P = x.parent()
1404
# #same base ring
1405
# if P is self:
1406
# return x
1407
# #different base ring -- coerce the coefficients from into R
1408
# else:
1409
# return eclass(self, dict([ (e1,R(e2)) for e1,e2 in x._monomial_coefficients.items()]))
1410
#x is an element of the ground ring (will be disabled at some point: not so useful)
1411
if x in R:
1412
if x == 0:
1413
return self.zero()
1414
else:
1415
raise TypeError, "do not know how to make x (= %s) an element of %s"%(x, self)
1416
#x is an element of the basis enumerated set;
1417
# This is a very ugly way of testing this
1418
elif ((hasattr(self._basis_keys, 'element_class') and
1419
isinstance(self._basis_keys.element_class, type) and
1420
isinstance(x, self._basis_keys.element_class))
1421
or (sage.structure.element.parent(x) == self._basis_keys)):
1422
return self.monomial(x)
1423
elif x in self._basis_keys:
1424
return self.monomial(self._basis_keys(x))
1425
else:
1426
if hasattr(self, '_coerce_end'):
1427
try:
1428
return self._coerce_end(x)
1429
except TypeError:
1430
pass
1431
raise TypeError, "do not know how to make x (= %s) an element of self (=%s)"%(x,self)
1432
1433
def _an_element_impl(self):
1434
"""
1435
Returns an element of self, namely the zero element.
1436
1437
EXAMPLES::
1438
1439
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1440
sage: F._an_element_impl()
1441
0
1442
sage: _.parent() is F
1443
True
1444
"""
1445
return self.element_class(self, {})
1446
1447
def combinatorial_class(self):
1448
"""
1449
Returns the combinatorial class that indexes the basis elements.
1450
1451
Deprecated: use self.basis().keys() instead.
1452
1453
EXAMPLES::
1454
1455
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1456
sage: F.combinatorial_class()
1457
doctest:...: DeprecationWarning: "FM.combinatorial_class()" is deprecated. Use "F.basis().keys()" instead !
1458
{'a', 'b', 'c'}
1459
1460
::
1461
1462
sage: s = SFASchur(QQ)
1463
sage: s.combinatorial_class()
1464
Partitions
1465
"""
1466
from sage.misc.misc import deprecation
1467
deprecation('"FM.combinatorial_class()" is deprecated. Use "F.basis().keys()" instead !')
1468
return self._basis_keys
1469
1470
def dimension(self):
1471
"""
1472
Returns the dimension of the combinatorial algebra (which is given
1473
by the number of elements in the basis).
1474
1475
EXAMPLES::
1476
1477
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1478
sage: F.dimension()
1479
3
1480
sage: F.basis().cardinality()
1481
3
1482
sage: F.basis().keys().cardinality()
1483
3
1484
1485
::
1486
1487
sage: s = SFASchur(QQ)
1488
sage: s.dimension()
1489
+Infinity
1490
"""
1491
return self._basis_keys.cardinality()
1492
1493
def set_order(self, order):
1494
"""
1495
Sets the order of the elements of the basis.
1496
1497
If :meth:`set_order` has not been called, then the ordering is
1498
the one used in the generation of the elements of self's
1499
associated enumerated set.
1500
1501
EXAMPLES::
1502
1503
sage: QS2 = SymmetricGroupAlgebra(QQ,2)
1504
sage: b = list(QS2.basis().keys())
1505
sage: b.reverse()
1506
sage: QS2.set_order(b)
1507
sage: QS2.get_order()
1508
[[2, 1], [1, 2]]
1509
"""
1510
self._order = order
1511
1512
def get_order(self):
1513
"""
1514
Returns the order of the elements in the basis.
1515
1516
EXAMPLES::
1517
1518
sage: QS2 = SymmetricGroupAlgebra(QQ,2)
1519
sage: QS2.get_order() # note: order changed on 2009-03-13
1520
[[2, 1], [1, 2]]
1521
"""
1522
if self._order is None:
1523
self._order = list(self.basis().keys())
1524
return self._order
1525
1526
def from_vector(self, vector):
1527
"""
1528
Build an element of self from a (sparse) vector
1529
1530
See self.get_order and the method to_vector of the elements of self
1531
1532
EXAMPLES::
1533
1534
sage: QS3 = SymmetricGroupAlgebra(QQ, 3)
1535
sage: b = QS3.from_vector(vector((2, 0, 0, 0, 0, 4))); b
1536
2*[1, 2, 3] + 4*[3, 2, 1]
1537
sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1])
1538
sage: a == b
1539
True
1540
"""
1541
cc = self.get_order()
1542
return self._from_dict(dict( (cc[index], coeff) for (index,coeff) in vector.iteritems()))
1543
1544
def prefix(self):
1545
"""
1546
Returns the prefix used when displaying elements of self.
1547
1548
EXAMPLES::
1549
1550
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1551
sage: F.prefix()
1552
'B'
1553
1554
::
1555
1556
sage: X = SchubertPolynomialRing(QQ)
1557
sage: X.prefix()
1558
'X'
1559
"""
1560
return self._print_options['prefix']
1561
1562
def print_options(self, **kwds):
1563
"""
1564
Return the current print options, or set an option.
1565
1566
INPUT: all of the input is optional; if present, it should be
1567
in the form of keyword pairs, such as
1568
``latex_bracket='('``. The allowable keywords are:
1569
1570
- ``prefix``
1571
- ``latex_prefix``
1572
- ``bracket``
1573
- ``latex_bracket``
1574
- ``scalar_mult``
1575
- ``latex_scalar_mult``
1576
- ``tensor_symbol``
1577
- ``monomial_cmp``
1578
1579
See the documentation for :class:`CombinatorialFreeModule` for
1580
descriptions of the effects of setting each of these options.
1581
1582
OUTPUT: if the user provides any input, set the appropriate
1583
option(s) and return nothing. Otherwise, return the
1584
dictionary of settings for print and LaTeX representations.
1585
1586
EXAMPLES::
1587
1588
sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix='x')
1589
sage: F.print_options()
1590
{...'prefix': 'x'...}
1591
sage: F.print_options(bracket='(')
1592
sage: F.print_options()
1593
{...'bracket': '('...}
1594
1595
TESTS::
1596
1597
sage: sorted(F.print_options().items())
1598
[('bracket', '('), ('latex_bracket', False), ('latex_prefix', None), ('latex_scalar_mult', None), ('monomial_cmp', <built-in function cmp>), ('prefix', 'x'), ('scalar_mult', '*'), ('tensor_symbol', None)]
1599
1600
"""
1601
# don't just use kwds.get(...) because I want to distinguish
1602
# between an argument like "option=None" and the option not
1603
# being there altogether.
1604
if kwds:
1605
for option in kwds:
1606
if option in ['prefix', 'latex_prefix', 'bracket', 'latex_bracket',
1607
'scalar_mult', 'latex_scalar_mult', 'tensor_symbol',
1608
'monomial_cmp'
1609
]:
1610
self._print_options[option] = kwds[option]
1611
else:
1612
raise ValueError, '%s is not a valid print option.' % option
1613
else:
1614
return self._print_options
1615
1616
_repr_option_bracket = True
1617
1618
def _repr_term(self, m):
1619
"""
1620
Returns a string representing the basis element indexed by m.
1621
1622
The output can be customized by setting any of the following
1623
options when initializing the module:
1624
1625
- prefix
1626
- bracket
1627
- scalar_mult
1628
1629
Alternatively, one can use the :meth:`print_options` method
1630
to achieve the same effect. To modify the bracket setting,
1631
one can also set ``self._repr_option_bracket`` as long as one
1632
has *not* set the ``bracket`` option: if the
1633
``bracket`` option is anything but ``None``, it overrides
1634
the value of ``self._repr_option_bracket``.
1635
1636
See the documentation for :class:`CombinatorialFreeModule` for
1637
details on the initialization options.
1638
1639
.. todo:: rename to ``_repr_monomial``
1640
1641
EXAMPLES::
1642
1643
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1644
sage: e = F.basis()
1645
sage: e['a'] + 2*e['b'] # indirect doctest
1646
B['a'] + 2*B['b']
1647
1648
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix="F")
1649
sage: e = F.basis()
1650
sage: e['a'] + 2*e['b'] # indirect doctest
1651
F['a'] + 2*F['b']
1652
1653
sage: QS3 = CombinatorialFreeModule(QQ, Permutations(3), prefix="")
1654
sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1])
1655
sage: a # indirect doctest
1656
2*[[1, 2, 3]] + 4*[[3, 2, 1]]
1657
1658
sage: QS3.print_options(bracket = False)
1659
sage: a # indirect doctest
1660
2*[1, 2, 3] + 4*[3, 2, 1]
1661
1662
sage: QS3.print_options(prefix='')
1663
sage: a # indirect doctest
1664
2*[1, 2, 3] + 4*[3, 2, 1]
1665
1666
sage: QS3.print_options(bracket="|", scalar_mult=" *@* ")
1667
sage: a # indirect doctest
1668
2 *@* |[1, 2, 3]| + 4 *@* |[3, 2, 1]|
1669
1670
TESTS::
1671
1672
sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), ('c','d')])
1673
sage: e = F.basis()
1674
sage: e[('a','b')] + 2*e[('c','d')] # indirect doctest
1675
B[('a', 'b')] + 2*B[('c', 'd')]
1676
"""
1677
bracket = self._print_options.get('bracket', None)
1678
bracket_d = {"{": "}", "[": "]", "(": ")"}
1679
if bracket is None:
1680
bracket = self._repr_option_bracket
1681
if bracket is True:
1682
left = "["
1683
right = "]"
1684
elif bracket is False:
1685
left = ""
1686
right = ""
1687
elif isinstance(bracket, (tuple, list)):
1688
left = bracket[0]
1689
right = bracket[1]
1690
elif bracket in bracket_d:
1691
left = bracket
1692
right = bracket_d[bracket]
1693
else:
1694
left = bracket
1695
right = bracket
1696
return self.prefix() + left + repr(m) + right # mind the (m), to accept a tuple for m
1697
1698
def _latex_term(self, m):
1699
"""
1700
Returns a string for the LaTeX code for the basis element
1701
indexed by m.
1702
1703
The output can be customized by setting any of the following
1704
options when initializing the module:
1705
1706
- prefix
1707
- latex_prefix
1708
- latex_bracket
1709
1710
(Alternatively, one can use the :meth:`print_options` method
1711
to achieve the same effect.)
1712
1713
See the documentation for :class:`CombinatorialFreeModule` for
1714
details on the initialization options.
1715
1716
.. todo:: rename to ``_latex_monomial``
1717
1718
EXAMPLES::
1719
1720
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1721
sage: e = F.basis()
1722
sage: latex(e['a'] + 2*e['b']) # indirect doctest
1723
B_{a} + 2B_{b}
1724
1725
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'], prefix="C")
1726
sage: e = F.basis()
1727
sage: latex(e['a'] + 2*e['b']) # indirect doctest
1728
C_{a} + 2C_{b}
1729
1730
sage: QS3 = CombinatorialFreeModule(QQ, Permutations(3), prefix="", scalar_mult="*")
1731
sage: a = 2*QS3([1,2,3])+4*QS3([3,2,1])
1732
sage: latex(a) # indirect doctest
1733
2[1, 2, 3] + 4[3, 2, 1]
1734
sage: QS3.print_options(latex_bracket=True)
1735
sage: latex(a) # indirect doctest
1736
2\left[[1, 2, 3]\right] + 4\left[[3, 2, 1]\right]
1737
sage: QS3.print_options(latex_bracket="(")
1738
sage: latex(a) # indirect doctest
1739
2\left([1, 2, 3]\right) + 4\left([3, 2, 1]\right)
1740
sage: QS3.print_options(latex_bracket=('\\myleftbracket', '\\myrightbracket'))
1741
sage: latex(a) # indirect doctest
1742
2\myleftbracket[1, 2, 3]\myrightbracket + 4\myleftbracket[3, 2, 1]\myrightbracket
1743
1744
TESTS::
1745
1746
sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), (0,1,2)])
1747
sage: e = F.basis()
1748
sage: latex(e[('a','b')]) # indirect doctest
1749
B_{\left(a, b\right)}
1750
sage: latex(2*e[(0,1,2)]) # indirect doctest
1751
2B_{\left(0, 1, 2\right)}
1752
sage: F = CombinatorialFreeModule(QQ, [('a', 'b'), (0,1,2)], prefix="")
1753
sage: e = F.basis()
1754
sage: latex(2*e[(0,1,2)]) # indirect doctest
1755
2\left(0, 1, 2\right)
1756
"""
1757
from sage.misc.latex import latex
1758
1759
s = latex(m)
1760
if s.find('\\verb') != -1:
1761
import re
1762
s = re.sub("\\\\verb(.)(.*?)\\1", "\\2", s)
1763
s = s.replace("\\phantom{x}", " ")
1764
1765
# dictionary with left-right pairs of "brackets". put pairs
1766
# in here accept \\left and \\right as prefixes.
1767
bracket_d = {"{": "\\}", "[": "]", "(": ")", "\\{": "\\}",
1768
"|": "|", "||": "||"}
1769
bracket = self._print_options.get('latex_bracket', False)
1770
if bracket is True:
1771
left = "\\left["
1772
right = "\\right]"
1773
elif bracket is False:
1774
left = ""
1775
right = ""
1776
elif isinstance(bracket, (tuple, list)):
1777
left = bracket[0]
1778
right = bracket[1]
1779
elif bracket in bracket_d:
1780
left = bracket
1781
right = bracket_d[bracket]
1782
if left == "{":
1783
left = "\\{"
1784
left = "\\left" + left
1785
right = "\\right" + right
1786
else:
1787
left = bracket
1788
right = bracket
1789
prefix = self._print_options.get('latex_prefix')
1790
if prefix is None:
1791
prefix = self._print_options.get('prefix')
1792
if prefix == "":
1793
return left + s + right
1794
return "%s_{%s}" % (prefix, s)
1795
1796
def __cmp__(self, other):
1797
"""
1798
EXAMPLES::
1799
1800
sage: XQ = SchubertPolynomialRing(QQ)
1801
sage: XZ = SchubertPolynomialRing(ZZ)
1802
sage: XQ == XZ #indirect doctest
1803
False
1804
sage: XQ == XQ
1805
True
1806
"""
1807
if not isinstance(other, self.__class__):
1808
return -1
1809
c = cmp(self.base_ring(), other.base_ring())
1810
if c: return c
1811
return 0
1812
1813
def _apply_module_morphism( self, x, on_basis, codomain=False ):
1814
"""
1815
Returns the image of x under the module morphism defined by
1816
extending f by linearity.
1817
1818
INPUT:
1819
1820
1821
- ``x`` : a element of self
1822
1823
- ``on_basis`` - a function that takes in a combinatorial
1824
object indexing a basis element and returns an element of the
1825
codomain
1826
1827
- ``codomain`` (optional) - the codomain of the morphism, otherwise it is computed using on_basis
1828
1829
1830
EXAMPLES::
1831
1832
sage: s = SFASchur(QQ)
1833
sage: a = s([3]) + s([2,1]) + s([1,1,1])
1834
sage: b = 2*a
1835
sage: f = lambda part: Integer( len(part) )
1836
sage: s._apply_module_morphism(a, f) #1+2+3
1837
6
1838
sage: s._apply_module_morphism(b, f) #2*(1+2+3)
1839
12
1840
"""
1841
1842
if x == self.zero():
1843
if not codomain:
1844
B = self.basis()
1845
keys = list( B.keys() )
1846
if len( keys ) > 0:
1847
key = keys[0]
1848
codomain = on_basis( key ).parent()
1849
else:
1850
raise ValueError, 'Codomain could not be determined'
1851
1852
return codomain.zero()
1853
1854
else:
1855
if not codomain:
1856
keys = x.support()
1857
key = keys[0]
1858
codomain = on_basis( key ).parent()
1859
1860
if hasattr( codomain, 'linear_combination' ):
1861
return codomain.linear_combination( ( on_basis( key ), coeff ) for key, coeff in x._monomial_coefficients.iteritems() )
1862
else:
1863
return_sum = codomain.zero()
1864
for key, coeff in x._monomial_coefficients.iteritems():
1865
return_sum += coeff * on_basis( key )
1866
return return_sum
1867
1868
def _apply_module_endomorphism(self, x, on_basis):
1869
"""
1870
This takes in a function from the basis elements to the elements of
1871
self and applies it linearly to a. Note that
1872
_apply_module_endomorphism does not require multiplication on
1873
self to be defined.
1874
1875
EXAMPLES::
1876
1877
sage: s = SFASchur(QQ)
1878
sage: f = lambda part: 2*s(part.conjugate())
1879
sage: s._apply_module_endomorphism( s([2,1]) + s([1,1,1]), f)
1880
2*s[2, 1] + 2*s[3]
1881
"""
1882
return self.linear_combination( ( on_basis( key ), coeff ) for key, coeff in x._monomial_coefficients.iteritems() )
1883
1884
def sum(self, iter_of_elements):
1885
"""
1886
overrides method inherited from commutative additive monoid as it is much faster on dicts directly
1887
1888
INPUT:
1889
1890
- ``iter_of_elements``: iterator of elements of ``self``
1891
1892
Returns the sum of all elements in ``iter_of_elements``
1893
1894
EXAMPLES::
1895
1896
sage: F = CombinatorialFreeModule(QQ,[1,2])
1897
sage: f = F.an_element(); f
1898
2*B[1] + 2*B[2]
1899
sage: F.sum( f for _ in range(5) )
1900
10*B[1] + 10*B[2]
1901
"""
1902
1903
D = dict_addition( element._monomial_coefficients for element in iter_of_elements )
1904
return self._from_dict( D, remove_zeros=False )
1905
1906
def linear_combination( self, iter_of_elements_coeff, factor_on_left=True ):
1907
"""
1908
INPUT:
1909
1910
- ``iter_of_elements_coeff`` -- iterator of pairs ``(element, coeff)``
1911
with ``element`` in ``self`` and ``coeff`` in ``self.base_ring()``
1912
1913
- ``factor_on_left`` (optional) -- if ``True``, the coefficients are
1914
multiplied from the left if ``False``, the coefficients are
1915
multiplied from the right
1916
1917
Returns the linear combination `\lambda_1 v_1 + ... + \lambda_k v_k`
1918
(resp. the linear combination `v_1 \lambda_1 + ... + v_k \lambda_k`)
1919
where ``iter_of_elements_coeff`` iterates through the sequence
1920
`(\lambda_1, v_1) ... (\lambda_k, v_k)`.
1921
1922
EXAMPLES::
1923
1924
sage: F = CombinatorialFreeModule(QQ,[1,2])
1925
sage: f = F.an_element(); f
1926
2*B[1] + 2*B[2]
1927
sage: F.linear_combination( (f,i) for i in range(5) )
1928
20*B[1] + 20*B[2]
1929
"""
1930
return self._from_dict( dict_linear_combination( ( ( element._monomial_coefficients, coeff ) for element, coeff in iter_of_elements_coeff ), factor_on_left=factor_on_left ), remove_zeros=False )
1931
1932
def term(self, index, coeff=None):
1933
"""
1934
Constructs a term in ``self``
1935
1936
INPUT:
1937
1938
- ``index`` -- the index of a basis element
1939
- ``coeff`` -- an element of the coefficient ring (default: one)
1940
1941
EXAMPLES::
1942
1943
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1944
sage: F.term('a',3)
1945
3*B['a']
1946
sage: F.term('a')
1947
B['a']
1948
1949
Design: should this do coercion on the coefficient ring?
1950
"""
1951
if coeff is None:
1952
coeff = self.base_ring().one()
1953
return self._from_dict( {index: coeff} )
1954
1955
def _monomial(self, index):
1956
"""
1957
TESTS::
1958
1959
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1960
sage: F._monomial('a')
1961
B['a']
1962
"""
1963
return self._from_dict( {index: self.base_ring().one()}, remove_zeros = False )
1964
1965
# This is generic, and should be lifted into modules_with_basis
1966
@lazy_attribute
1967
def monomial(self):
1968
"""
1969
INPUT:
1970
1971
- ''i''
1972
1973
Returns the basis element indexed by i
1974
1975
EXAMPLES::
1976
1977
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1978
sage: F.monomial('a')
1979
B['a']
1980
1981
F.monomial is in fact (almost) a map::
1982
1983
sage: F.monomial
1984
Term map from {'a', 'b', 'c'} to Free module generated by {'a', 'b', 'c'} over Rational Field
1985
"""
1986
# Should use a real Map, as soon as combinatorial_classes are enumerated sets, and therefore parents
1987
return PoorManMap(self._monomial, domain = self._basis_keys, codomain = self, name = "Term map")
1988
1989
def _sum_of_monomials(self, indices):
1990
"""
1991
TESTS::
1992
1993
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
1994
sage: F._sum_of_monomials(['a', 'b'])
1995
B['a'] + B['b']
1996
"""
1997
# TODO: optimize by calling directly _from_dict if we
1998
# know that all indices are distinct as sum_of_terms;
1999
# otherwise, maybe call dict_addition directly
2000
return self.sum(self.monomial(index) for index in indices)
2001
2002
@lazy_attribute
2003
def sum_of_monomials(self):
2004
"""
2005
INPUT:
2006
2007
- ''indices'' -- an list (or iterable) of indices of basis elements
2008
2009
Returns the sum of the corresponding basis elements
2010
2011
EXAMPLES::
2012
2013
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
2014
sage: F.sum_of_monomials(['a', 'b'])
2015
B['a'] + B['b']
2016
2017
sage: F.sum_of_monomials(['a', 'b', 'a'])
2018
2*B['a'] + B['b']
2019
2020
F.sum_of_monomials is in fact (almost) a map::
2021
2022
sage: F.sum_of_monomials
2023
A map to Free module generated by {'a', 'b', 'c'} over Rational Field
2024
"""
2025
# domain = sets of self.combinatorial_class(),
2026
return PoorManMap(self._sum_of_monomials, codomain = self)
2027
2028
def sum_of_terms(self, terms, distinct=False):
2029
"""
2030
Constructs a sum of terms of ``self``
2031
2032
INPUT:
2033
2034
- ``terms`` -- a list (or iterable) of pairs (index, coeff)
2035
- ``distinct`` -- whether the indices are guaranteed to be distinct (default: ``False``)
2036
2037
EXAMPLES::
2038
2039
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
2040
sage: F.sum_of_terms([('a',2), ('c',3)])
2041
2*B['a'] + 3*B['c']
2042
2043
If ``distinct`` is True, then the construction is optimized::
2044
2045
sage: F.sum_of_terms([('a',2), ('c',3)], distinct = True)
2046
2*B['a'] + 3*B['c']
2047
2048
.. warning::
2049
2050
Use ``distinct=True`` only if you are sure that the
2051
indices are indeed distinct::
2052
2053
sage: F.sum_of_terms([('a',2), ('a',3)], distinct = True)
2054
3*B['a']
2055
2056
Extreme case::
2057
2058
sage: F.sum_of_terms([])
2059
0
2060
"""
2061
if distinct:
2062
return self._from_dict(dict(terms))
2063
return self.sum(self.term(index, coeff) for (index, coeff) in terms)
2064
2065
def monomial_or_zero_if_none(self, i):
2066
"""
2067
EXAMPLES::
2068
2069
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
2070
sage: F.monomial_or_zero_if_none('a')
2071
B['a']
2072
sage: F.monomial_or_zero_if_none(None)
2073
0
2074
"""
2075
if i == None:
2076
return self.zero()
2077
return self.monomial(i)
2078
2079
@cached_method
2080
def zero(self):
2081
"""
2082
EXAMPLES::
2083
2084
sage: F = CombinatorialFreeModule(QQ, ['a', 'b', 'c'])
2085
sage: F.zero()
2086
0
2087
"""
2088
return self._from_dict({}, remove_zeros=False)
2089
2090
def _from_dict( self, d, coerce=False, remove_zeros=True ):
2091
"""
2092
Construct an element of ``self`` from an `{index: coefficient}` dictionary
2093
2094
INPUT:
2095
2096
- ``d`` -- a dictionary ``{index: coeff}`` where each ``index`` is the
2097
index of a basis element and each ``coeff`` belongs to the
2098
coefficient ring ``self.base_ring()``
2099
2100
- ``coerce`` -- a boolean (default: ``False``), whether to coerce the
2101
``coeff``s to the coefficient ring
2102
2103
- ``remove_zeros`` -- a boolean (default: ``True``), if some
2104
``coeff``s may be zero and should therefore be removed
2105
2106
EXAMPLES::
2107
2108
sage: e = SFAElementary(QQ)
2109
sage: s = SFASchur(QQ)
2110
sage: a = e([2,1]) + e([1,1,1]); a
2111
e[1, 1, 1] + e[2, 1]
2112
sage: s._from_dict(a.monomial_coefficients())
2113
s[1, 1, 1] + s[2, 1]
2114
2115
If the optional argument ``coerce`` is ``True``, then the
2116
coefficients are coerced into the base ring of ``self``::
2117
2118
sage: part = Partition([2,1])
2119
sage: d = {part:1}
2120
sage: a = s._from_dict(d,coerce=True); a
2121
s[2, 1]
2122
sage: a.coefficient(part).parent()
2123
Rational Field
2124
2125
With ``remove_zeros=True``, zero coefficients are removed::
2126
2127
sage: s._from_dict({part:0})
2128
0
2129
2130
.. warning::
2131
2132
With ``remove_zeros=True``, it is assumed that no
2133
coefficient of the dictionary is zero. Otherwise, this may
2134
lead to illegal results::
2135
2136
sage: list(s._from_dict({part:0}, remove_zeros=False))
2137
[([2, 1], 0)]
2138
"""
2139
assert isinstance(d, dict)
2140
if coerce:
2141
R = self.base_ring()
2142
d = dict( (key, R(coeff)) for key,coeff in d.iteritems())
2143
if remove_zeros:
2144
d = dict( (key, coeff) for key, coeff in d.iteritems() if coeff)
2145
return self.element_class( self, d )
2146
2147
2148
class CombinatorialFreeModule_Tensor(CombinatorialFreeModule):
2149
"""
2150
Tensor Product of Free Modules
2151
2152
EXAMPLES:
2153
2154
We construct two free modules, assign them short names, and construct their tensor product::
2155
2156
sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F"
2157
sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G"
2158
sage: T = tensor([F, G]); T
2159
F # G
2160
2161
sage: T.category()
2162
Category of tensor products of modules with basis over Integer Ring
2163
2164
sage: T.construction() # todo: not implemented
2165
[tensor, ]
2166
2167
T is a free module, with same base ring as F and G::
2168
2169
sage: T.base_ring()
2170
Integer Ring
2171
2172
The basis of T is indexed by tuples of basis indices of F and G::
2173
2174
sage: T.basis().keys()
2175
Image of Cartesian product of {1, 2}, {3, 4} by <type 'tuple'>
2176
sage: T.basis().keys().list()
2177
[(1, 3), (1, 4), (2, 3), (2, 4)]
2178
2179
FIXME: Should elements of a CartesianProduct be tuples (making them hashable)?
2180
2181
Here are the basis elements themselves::
2182
2183
sage: T.basis().cardinality()
2184
4
2185
sage: list(T.basis())
2186
[B[1] # B[3], B[1] # B[4], B[2] # B[3], B[2] # B[4]]
2187
2188
The tensor product is associative and flattens sub tensor products::
2189
2190
sage: H = CombinatorialFreeModule(ZZ, [5,6]); H.rename("H")
2191
sage: tensor([F, tensor([G, H])])
2192
F # G # H
2193
sage: tensor([tensor([F, G]), H])
2194
F # G # H
2195
sage: tensor([F, G, H])
2196
F # G # H
2197
2198
We now compute the tensor product of elements of free modules::
2199
2200
sage: f = F.monomial(1) + 2 * F.monomial(2)
2201
sage: g = 2*G.monomial(3) + G.monomial(4)
2202
sage: h = H.monomial(5) + H.monomial(6)
2203
sage: tensor([f, g])
2204
2*B[1] # B[3] + B[1] # B[4] + 4*B[2] # B[3] + 2*B[2] # B[4]
2205
2206
Again, the tensor product is associative on elements::
2207
2208
sage: tensor([f, tensor([g, h])]) == tensor([f, g, h])
2209
True
2210
sage: tensor([tensor([f, g]), h]) == tensor([f, g, h])
2211
True
2212
2213
Note further that the tensor product spaces need not preexist::
2214
2215
sage: t = tensor([f, g, h])
2216
sage: t.parent()
2217
F # G # H
2218
2219
2220
TESTS::
2221
2222
sage: tensor([tensor([F, G]), H]) == tensor([F, G, H])
2223
True
2224
sage: tensor([F, tensor([G, H])]) == tensor([F, G, H])
2225
True
2226
"""
2227
@staticmethod
2228
def __classcall_private__(cls, modules, **options):
2229
"""
2230
TESTS::
2231
2232
sage: F = CombinatorialFreeModule(ZZ, [1,2])
2233
sage: G = CombinatorialFreeModule(ZZ, [3,4])
2234
sage: H = CombinatorialFreeModule(ZZ, [4])
2235
sage: tensor([tensor([F, G]), H]) == tensor([F, G, H])
2236
True
2237
sage: tensor([F, tensor([G, H])]) == tensor([F, G, H])
2238
True
2239
"""
2240
assert(len(modules) > 0)
2241
R = modules[0].base_ring()
2242
assert(all(module in ModulesWithBasis(R)) for module in modules)
2243
# should check the base ring
2244
# flatten the list of modules so that tensor(A, tensor(B,C)) gets rewritten into tensor(A, B, C)
2245
modules = sum([module._sets if isinstance(module, CombinatorialFreeModule_Tensor) else (module,) for module in modules], ())
2246
return super(CombinatorialFreeModule.Tensor, cls).__classcall__(cls, modules, **options)
2247
2248
2249
def __init__(self, modules, **options):
2250
"""
2251
TESTS::
2252
2253
sage: F = CombinatorialFreeModule(ZZ, [1,2]); F
2254
F
2255
"""
2256
from sage.categories.tensor import tensor
2257
self._sets = modules
2258
CombinatorialFreeModule.__init__(self, modules[0].base_ring(), CartesianProduct(*[module.basis().keys() for module in modules]).map(tuple), **options)
2259
# the following is not the best option, but it's better than nothing.
2260
self._print_options['tensor_symbol'] = options.get('tensor_symbol', tensor.symbol)
2261
2262
def _repr_(self):
2263
"""
2264
This is customizable by setting
2265
``self.print_options('tensor_symbol'=...)``.
2266
2267
TESTS::
2268
2269
sage: F = CombinatorialFreeModule(ZZ, [1,2,3])
2270
sage: G = CombinatorialFreeModule(ZZ, [1,2,3,8])
2271
sage: F.rename("F")
2272
sage: G.rename("G")
2273
sage: T = tensor([F, G])
2274
sage: T # indirect doctest
2275
F # G
2276
sage: T.print_options(tensor_symbol= ' @ ') # note the spaces
2277
sage: T # indirect doctest
2278
F @ G
2279
"""
2280
from sage.categories.tensor import tensor
2281
if hasattr(self, "_print_options"):
2282
symb = self._print_options['tensor_symbol']
2283
if symb is None:
2284
symb = tensor.symbol
2285
else:
2286
symb = tensor.symbol
2287
return symb.join(["%s"%module for module in self._sets])
2288
# TODO: make this overridable by setting _name
2289
2290
def _latex_(self):
2291
"""
2292
TESTS::
2293
2294
sage: F = CombinatorialFreeModule(ZZ, [1,2,3])
2295
sage: G = CombinatorialFreeModule(ZZ, [1,2,3,8])
2296
sage: F.rename("F")
2297
sage: G.rename("G")
2298
sage: latex(tensor([F, F, G])) # indirect doctest
2299
\verb|F| \otimes \verb|F| \otimes \verb|G|
2300
sage: F._latex_ = lambda : "F"
2301
sage: G._latex_ = lambda : "G"
2302
sage: latex(tensor([F, F, G])) # indirect doctest
2303
F \otimes F \otimes G
2304
"""
2305
from sage.misc.latex import latex
2306
symb = " \\otimes "
2307
return symb.join(["%s"%latex(module) for module in self._sets])
2308
2309
def _repr_term(self, term):
2310
"""
2311
TESTS::
2312
2313
sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix="F")
2314
sage: G = CombinatorialFreeModule(ZZ, [1,2,3,4], prefix="G")
2315
sage: f = F.monomial(1) + 2 * F.monomial(2)
2316
sage: g = 2*G.monomial(3) + G.monomial(4)
2317
sage: tensor([f, g]) # indirect doctest
2318
2*F[1] # G[3] + F[1] # G[4] + 4*F[2] # G[3] + 2*F[2] # G[4]
2319
"""
2320
from sage.categories.tensor import tensor
2321
if hasattr(self, "_print_options"):
2322
symb = self._print_options['tensor_symbol']
2323
if symb is None:
2324
symb = tensor.symbol
2325
else:
2326
symb = tensor.symbol
2327
return symb.join(module._repr_term(t) for (module, t) in zip(self._sets, term))
2328
2329
def _latex_term(self, term):
2330
"""
2331
TESTS::
2332
2333
sage: F = CombinatorialFreeModule(ZZ, [1,2,3], prefix='x')
2334
sage: G = CombinatorialFreeModule(ZZ, [1,2,3,4], prefix='y')
2335
sage: f = F.monomial(1) + 2 * F.monomial(2)
2336
sage: g = 2*G.monomial(3) + G.monomial(4)
2337
sage: latex(tensor([f, g])) # indirect doctest
2338
2x_{1} \otimes y_{3} + x_{1} \otimes y_{4} + 4x_{2} \otimes y_{3} + 2x_{2} \otimes y_{4}
2339
"""
2340
symb = " \\otimes "
2341
return symb.join(module._latex_term(t) for (module, t) in zip(self._sets, term))
2342
2343
@cached_method
2344
def tensor_constructor(self, modules):
2345
r"""
2346
INPUT:
2347
2348
- ``modules`` -- a tuple `(F_1,\dots,F_n)` of
2349
free modules whose tensor product is self
2350
2351
Returns the canonical multilinear morphism from
2352
`F_1 \times \dots \times F_n` to `F_1 \otimes \dots \otimes F_n`
2353
2354
EXAMPLES::
2355
2356
sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F"
2357
sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G"
2358
sage: H = CombinatorialFreeModule(ZZ, [5,6]); H.rename("H")
2359
2360
sage: f = F.monomial(1) + 2 * F.monomial(2)
2361
sage: g = 2*G.monomial(3) + G.monomial(4)
2362
sage: h = H.monomial(5) + H.monomial(6)
2363
2364
sage: FG = tensor([F, G ])
2365
sage: phi_fg = FG.tensor_constructor((F, G))
2366
sage: phi_fg(f,g)
2367
2*B[1] # B[3] + B[1] # B[4] + 4*B[2] # B[3] + 2*B[2] # B[4]
2368
2369
sage: FGH = tensor([F, G, H])
2370
sage: phi_fgh = FGH.tensor_constructor((F, G, H))
2371
sage: phi_fgh(f, g, h)
2372
2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6]
2373
2374
sage: phi_fg_h = FGH.tensor_constructor((FG, H))
2375
sage: phi_fg_h(phi_fg(f, g), h)
2376
2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6]
2377
"""
2378
assert(module in ModulesWithBasis(self.base_ring()) for module in modules)
2379
assert(sage.categories.tensor.tensor(modules) == self)
2380
# a list l such that l[i] is True if modules[i] is readily a tensor product
2381
is_tensor = [isinstance(module, CombinatorialFreeModule_Tensor) for module in modules]
2382
# the tensor_constructor, on basis elements
2383
result = self.monomial * CartesianProductWithFlattening(is_tensor) #.
2384
# TODO: make this into an element of Hom( A x B, C ) when those will exist
2385
for i in range(0, len(modules)):
2386
result = modules[i]._module_morphism(result, position = i, codomain = self)
2387
return result
2388
2389
def _tensor_of_elements(self, elements):
2390
"""
2391
Returns the tensor product of the specified elements.
2392
The result should be in self.
2393
2394
EXAMPLES::
2395
2396
sage: F = CombinatorialFreeModule(ZZ, [1,2]); F.__custom_name = "F"
2397
sage: G = CombinatorialFreeModule(ZZ, [3,4]); G.__custom_name = "G"
2398
sage: H = CombinatorialFreeModule(ZZ, [5,6]); H.rename("H")
2399
2400
sage: f = F.monomial(1) + 2 * F.monomial(2)
2401
sage: g = 2*G.monomial(3) + G.monomial(4)
2402
sage: h = H.monomial(5) + H.monomial(6)
2403
2404
sage: GH = tensor([G, H])
2405
sage: gh = GH._tensor_of_elements([g, h]); gh
2406
2*B[3] # B[5] + 2*B[3] # B[6] + B[4] # B[5] + B[4] # B[6]
2407
2408
sage: FGH = tensor([F, G, H])
2409
sage: FGH._tensor_of_elements([f, g, h])
2410
2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6]
2411
2412
sage: FGH._tensor_of_elements([f, gh])
2413
2*B[1] # B[3] # B[5] + 2*B[1] # B[3] # B[6] + B[1] # B[4] # B[5] + B[1] # B[4] # B[6] + 4*B[2] # B[3] # B[5] + 4*B[2] # B[3] # B[6] + 2*B[2] # B[4] # B[5] + 2*B[2] # B[4] # B[6]
2414
"""
2415
return self.tensor_constructor(tuple(element.parent() for element in elements))(*elements)
2416
2417
class CartesianProductWithFlattening(object):
2418
"""
2419
A class for cartesian product constructor, with partial flattening
2420
"""
2421
def __init__(self, flatten):
2422
"""
2423
INPUT:
2424
2425
- ``flatten`` -- a tuple of booleans
2426
2427
This constructs a callable which accepts ``len(flatten)``
2428
arguments, and builds a tuple out them. When ``flatten[i]``,
2429
the i-th argument itself should be a tuple which is flattened
2430
in the result.
2431
2432
sage: from sage.combinat.free_module import CartesianProductWithFlattening
2433
sage: CartesianProductWithFlattening([True, False, True, True])
2434
<sage.combinat.free_module.CartesianProductWithFlattening object at ...>
2435
2436
"""
2437
self._flatten = flatten
2438
2439
def __call__(self, *indices):
2440
"""
2441
EXAMPLES::
2442
2443
sage: from sage.combinat.free_module import CartesianProductWithFlattening
2444
sage: cp = CartesianProductWithFlattening([True, False, True, True])
2445
sage: cp((1,2), (3,4), (5,6), (7,8))
2446
(1, 2, (3, 4), 5, 6, 7, 8)
2447
sage: cp((1,2,3), 4, (5,6), (7,8))
2448
(1, 2, 3, 4, 5, 6, 7, 8)
2449
2450
"""
2451
return sum( (i if flatten else (i,) for (i,flatten) in zip(indices, self._flatten) ), ())
2452
2453
2454
# TODO: find a way to avoid this hack to allow for cross references
2455
CombinatorialFreeModule.Tensor = CombinatorialFreeModule_Tensor
2456
2457
2458
class CombinatorialFreeModule_CartesianProduct(CombinatorialFreeModule):
2459
"""
2460
An implementation of cartesian products of modules with basis
2461
2462
EXAMPLES:
2463
2464
We construct two free modules, assign them short names, and construct their cartesian product::
2465
2466
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F"
2467
sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G"
2468
sage: H = CombinatorialFreeModule(ZZ, [4,7]); H.__custom_name = "H"
2469
sage: S = cartesian_product([F, G])
2470
sage: S
2471
F (+) G
2472
sage: S.basis()
2473
Lazy family (Term map from Disjoint union of Family ({4, 5}, {4, 6}) to F (+) G(i))_{i in Disjoint union of Family ({4, 5}, {4, 6})}
2474
2475
Note that the indices of the basis elements of F and G intersect non
2476
trivially. This is handled by forcing the union to be disjoint::
2477
2478
sage: list(S.basis())
2479
[B[(0, 4)], B[(0, 5)], B[(1, 4)], B[(1, 6)]]
2480
2481
We now compute the cartesian product of elements of free modules::
2482
2483
sage: f = F.monomial(4) + 2 * F.monomial(5)
2484
sage: g = 2*G.monomial(4) + G.monomial(6)
2485
sage: h = H.monomial(4) + H.monomial(7)
2486
sage: cartesian_product([f,g])
2487
B[(0, 4)] + 2*B[(0, 5)] + 2*B[(1, 4)] + B[(1, 6)]
2488
sage: cartesian_product([f,g,h])
2489
B[(0, 4)] + 2*B[(0, 5)] + 2*B[(1, 4)] + B[(1, 6)] + B[(2, 4)] + B[(2, 7)]
2490
sage: cartesian_product([f,g,h]).parent()
2491
F (+) G (+) H
2492
2493
TODO: choose an appropriate semantic for cartesian products of cartesian products (associativity?)::
2494
2495
sage: S = cartesian_product([cartesian_product([F, G]), H]) # todo: not implemented
2496
F (+) G (+) H
2497
"""
2498
2499
def __init__(self, modules, **options):
2500
r"""
2501
TESTS::
2502
2503
sage: F = CombinatorialFreeModule(ZZ, [2,4,5])
2504
sage: G = CombinatorialFreeModule(ZZ, [2,4,7])
2505
sage: cartesian_product([F, G])
2506
Free module generated by {2, 4, 5} over Integer Ring (+) Free module generated by {2, 4, 7} over Integer Ring
2507
"""
2508
assert(len(modules) > 0) # TODO: generalize to a family or tuple
2509
R = modules[0].base_ring()
2510
assert(all(module in ModulesWithBasis(R)) for module in modules)
2511
# should check the base ring
2512
self._sets = modules
2513
CombinatorialFreeModule.__init__(self, R,
2514
DisjointUnionEnumeratedSets(
2515
[module.basis().keys() for module in modules], keepkey=True),
2516
**options)
2517
2518
def _sets_keys(self):
2519
"""
2520
In waiting for self._sets.keys()
2521
2522
TESTS::
2523
2524
sage: F = CombinatorialFreeModule(ZZ, [2,4,5])
2525
sage: G = CombinatorialFreeModule(ZZ, [2,4,7])
2526
sage: CP = cartesian_product([F, G])
2527
sage: CP._sets_keys()
2528
[0, 1]
2529
"""
2530
return range(len(self._sets))
2531
2532
def _repr_(self):
2533
"""
2534
TESTS::
2535
2536
sage: F = CombinatorialFreeModule(ZZ, [2,4,5])
2537
sage: CP = cartesian_product([F, F]); CP # indirect doctest
2538
Free module generated by {2, 4, 5} over Integer Ring (+) Free module generated by {2, 4, 5} over Integer Ring
2539
sage: F.__custom_name = "F"; CP
2540
F (+) F
2541
"""
2542
from sage.categories.cartesian_product import cartesian_product
2543
return cartesian_product.symbol.join(["%s"%module for module in self._sets])
2544
# TODO: make this overridable by setting _name
2545
2546
@cached_method
2547
def summand_embedding(self, i):
2548
"""
2549
Returns the natural embedding morphism of the i-th summand of self into self
2550
2551
INPUTS:
2552
2553
- ``i`` -- an integer
2554
2555
EXAMPLES::
2556
2557
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F"
2558
sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G"
2559
sage: S = cartesian_product([F, G])
2560
sage: phi = S.summand_embedding(0)
2561
sage: phi(F.monomial(4) + 2 * F.monomial(5))
2562
B[(0, 4)] + 2*B[(0, 5)]
2563
sage: phi(F.monomial(4) + 2 * F.monomial(6)).parent() == S
2564
True
2565
sage: phi(G.monomial(4)) # not implemented Should raise an error! problem: G(F.monomial(4)) does not complain!!!!
2566
"""
2567
assert i in self._sets_keys()
2568
return self._sets[i]._module_morphism(lambda t: self.monomial((i,t)), codomain = self)
2569
2570
@cached_method
2571
def summand_projection(self, i):
2572
"""
2573
Returns the natural projection onto the i-th summand of self
2574
2575
INPUTS:
2576
2577
- ``i`` -- an integer
2578
2579
EXAMPLE::
2580
2581
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F"
2582
sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G"
2583
sage: S = cartesian_product([F, G])
2584
sage: x = S.monomial((0,4)) + 2 * S.monomial((0,5)) + 3 * S.monomial((1,6))
2585
sage: S.summand_projection(0)(x)
2586
B[4] + 2*B[5]
2587
sage: S.summand_projection(1)(x)
2588
3*B[6]
2589
sage: S.summand_projection(0)(x).parent() == F
2590
True
2591
sage: S.summand_projection(1)(x).parent() == G
2592
True
2593
"""
2594
assert i in self._sets_keys()
2595
module = self._sets[i]
2596
return self._module_morphism(lambda (j,t): module.monomial(t) if i == j else module.zero(), codomain = module)
2597
2598
def _cartesian_product_of_elements(self, elements):
2599
"""
2600
Returns the cartesian product of the elements
2601
2602
INPUT:
2603
2604
- ``elements`` - a tuple with one element of each summand of self
2605
2606
EXAMPLES::
2607
2608
sage: F = CombinatorialFreeModule(ZZ, [4,5]); F.__custom_name = "F"
2609
sage: G = CombinatorialFreeModule(ZZ, [4,6]); G.__custom_name = "G"
2610
sage: S = cartesian_product([F, G])
2611
sage: f = F.monomial(4) + 2 * F.monomial(5)
2612
sage: g = 2*G.monomial(4) + G.monomial(6)
2613
sage: S._cartesian_product_of_elements([f, g])
2614
B[(0, 4)] + 2*B[(0, 5)] + 2*B[(1, 4)] + B[(1, 6)]
2615
sage: S._cartesian_product_of_elements([f, g]).parent() == S
2616
True
2617
2618
"""
2619
return self.sum(self.summand_embedding(i)(elements[i]) for i in self._sets_keys())
2620
2621
class Element(CombinatorialFreeModule.Element): # TODO: get rid of this inheritance
2622
pass
2623
2624
CombinatorialFreeModule.CartesianProduct = CombinatorialFreeModule_CartesianProduct
2625
2626