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