Skip to content

Commit 1d10db3

Browse files
Release Managervbraun
Release Manager
authored andcommitted
Trac #15476: Involutions on NSym and QSym part I
This got much longer than I expected it to be because there are three "classical" involution on each of NSym and QSym and each can be computed on various bases. I ended up implementing only two of the involutions (star=rho and psi), leaving out omega (which is just a rescaled antipode) for part II. The patch also speeds up NSym's Verschiebung on certain bases, fixes some doc, moves a reference, and changes various invocations of `Composition(spam)` to `Compositions()(spam)` for speed reasons (when `spam` really is just a list). Could a `_Compositions` (akin to `_Partitions`) be of use? URL: http://trac.sagemath.org/15476 Reported by: darij Ticket author(s): Darij Grinberg Reviewer(s): Travis Scrimshaw
2 parents 6410ed0 + a88a476 commit 1d10db3

File tree

5 files changed

+1642
-76
lines changed

5 files changed

+1642
-76
lines changed

src/sage/combinat/composition.py

+59-27
Original file line numberDiff line numberDiff line change
@@ -195,18 +195,35 @@ def __setstate__(self, state):
195195
self._set_parent(state[0])
196196
self.__dict__ = state[1]
197197

198-
@combinatorial_map(name='conjugate')
198+
@combinatorial_map(order=2, name='conjugate')
199199
def conjugate(self):
200200
r"""
201201
Return the conjugate of the composition ``self``.
202202
203-
Algorithm from mupad-combinat.
203+
The conjugate of a composition `I` is defined as the
204+
complement (see :meth:`complement`) of the reverse composition
205+
(see :meth:`reversed`) of `I`.
206+
207+
An equivalent definition of the conjugate goes by saying that
208+
the ribbon shape of the conjugate of a composition `I` is the
209+
conjugate of the ribbon shape of `I`. (The ribbon shape of a
210+
composition is returned by :meth:`to_skew_partition`.)
211+
212+
This implementation uses the algorithm from mupad-combinat.
204213
205214
EXAMPLES::
206215
207216
sage: Composition([1, 1, 3, 1, 2, 1, 3]).conjugate()
208217
[1, 1, 3, 3, 1, 3]
209218
219+
The ribbon shape of the conjugate of `I` is the conjugate of
220+
the ribbon shape of `I`::
221+
222+
sage: all( I.conjugate().to_skew_partition()
223+
....: == I.to_skew_partition().conjugate()
224+
....: for I in Compositions(4) )
225+
True
226+
210227
TESTS::
211228
212229
sage: parent(list(Compositions(1))[0].conjugate())
@@ -227,23 +244,40 @@ def conjugate(self):
227244

228245
return self.parent()([cocjg[0]] + [cocjg[i]-cocjg[i-1]+1 for i in range(1,len(cocjg))])
229246

230-
@combinatorial_map(name='reversed')
247+
@combinatorial_map(order=2, name='reversed')
231248
def reversed(self):
232-
"""
249+
r"""
233250
Return the reverse composition of ``self``.
234251
252+
The reverse composition of a composition `(i_1, i_2, \ldots, i_k)`
253+
is defined as the composition `(i_k, i_{k-1}, \ldots, i_1)`.
254+
235255
EXAMPLES::
236256
237257
sage: Composition([1, 1, 3, 1, 2, 1, 3]).reversed()
238258
[3, 1, 2, 1, 3, 1, 1]
239259
"""
240260
return self.parent()(reversed(self))
241261

242-
@combinatorial_map(name = 'complement')
262+
@combinatorial_map(order=2, name='complement')
243263
def complement(self):
244-
"""
245-
Return the complement composition of ``self``. The complement is the
246-
reverse of the conjugate composition of ``self``.
264+
r"""
265+
Return the complement of the composition ``self``.
266+
267+
The complement of a composition `I` is defined as follows:
268+
269+
If `I` is the empty composition, then the complement is the empty
270+
composition as well. Otherwise, let `S` be the descent set of `I`
271+
(that is, the subset
272+
`\{ i_1, i_1 + i_2, \ldots, i_1 + i_2 + \cdots + i_{k-1} \}`
273+
of `\{ 1, 2, \ldots, |I|-1 \}`, where `I` is written as
274+
`(i_1, i_2, \ldots, i_k)`). Then, the complement of `I` is
275+
defined as the composition of size `|I|` whose descent set is
276+
`\{ 1, 2, \ldots, |I|-1 \} \setminus S`.
277+
278+
The complement of a composition `I` also is the reverse
279+
composition (:meth:`reversed`) of the conjugate
280+
(:meth:`conjugate`) of `I`.
247281
248282
EXAMPLES::
249283
@@ -268,7 +302,7 @@ def __add__(self, other):
268302
sage: Composition([]) + Composition([]) == Composition([])
269303
True
270304
"""
271-
return Composition(list(self)+list(other))
305+
return Compositions()(list(self)+list(other))
272306

273307
def size(self):
274308
"""
@@ -305,7 +339,7 @@ def sum(compositions):
305339
sage: Composition.sum([]) == Composition([])
306340
True
307341
"""
308-
return sum(compositions, Composition([]))
342+
return sum(compositions, Compositions()([]))
309343

310344
def near_concatenation(self, other):
311345
r"""
@@ -341,7 +375,7 @@ def near_concatenation(self, other):
341375
"""
342376
if len(self) == 0 or len(other) == 0:
343377
return None
344-
return Composition(list(self)[:-1] + [self[-1] + other[0]] + list(other)[1:])
378+
return Compositions()(list(self)[:-1] + [self[-1] + other[0]] + list(other)[1:])
345379

346380
def ribbon_decomposition(self, other, check=True):
347381
r"""
@@ -422,13 +456,6 @@ def ribbon_decomposition(self, other, check=True):
422456
...
423457
ValueError: [3, 1, 1, 3, 1] is not the same size as [4, 3, 1]
424458
425-
REFERENCES:
426-
427-
.. [NCSF1] Israel Gelfand, D. Krob, Alain Lascoux, B. Leclerc,
428-
V. S. Retakh, J.-Y. Thibon,
429-
*Noncommutative symmetric functions*.
430-
:arxiv:`hep-th/9407124v1`
431-
432459
AUTHORS:
433460
434461
- Darij Grinberg (2013-08-29)
@@ -452,7 +479,7 @@ def ribbon_decomposition(self, other, check=True):
452479
try:
453480
i = I_iter.next()
454481
except StopIteration:
455-
factors.append(Composition(current_factor))
482+
factors.append(Compositions()(current_factor))
456483
return (tuple(factors), tuple(signs))
457484
if current_factor_size + i <= j:
458485
current_factor.append(i)
@@ -465,7 +492,7 @@ def ribbon_decomposition(self, other, check=True):
465492
current_factor.append(j - current_factor_size)
466493
i -= j - current_factor_size
467494
signs.append(1)
468-
factors.append(Composition(current_factor))
495+
factors.append(Compositions()(current_factor))
469496
break
470497

471498
return (tuple(factors), tuple(signs))
@@ -584,7 +611,7 @@ def join(self, other, check=True):
584611
try:
585612
i = I_iter.next()
586613
except StopIteration:
587-
return Composition(factors)
614+
return Compositions()(factors)
588615
if current_factor_size + i <= j:
589616
factors.append(i)
590617
current_factor_size += i
@@ -595,7 +622,7 @@ def join(self, other, check=True):
595622
i -= j - current_factor_size
596623
break
597624

598-
return Composition(factors)
625+
return Compositions()(factors)
599626

600627
sup = join
601628

@@ -705,7 +732,7 @@ def meet(self, other, check=True):
705732
i = I_iter.next()
706733
except StopIteration:
707734
factors.append(current_part)
708-
return Composition(factors)
735+
return Compositions()(factors)
709736
if current_factor_size + i <= j:
710737
current_part += i
711738
current_factor_size += i
@@ -719,7 +746,7 @@ def meet(self, other, check=True):
719746
current_part += j - current_factor_size
720747
break
721748

722-
return Composition(factors)
749+
return Compositions()(factors)
723750

724751
inf = meet
725752

@@ -816,7 +843,7 @@ def fatten(self, grouping):
816843
for i in range(len(grouping)):
817844
result[i] = sum(self[j:j+grouping[i]])
818845
j += grouping[i]
819-
return Composition(result)
846+
return Compositions()(result)
820847

821848
def fatter(self):
822849
"""
@@ -894,7 +921,7 @@ def refinement_splitting(self, J):
894921
sum1 += new_comp[-1]
895922
if sum1 > sum2:
896923
raise ValueError("composition J (= %s) does not refine self (= %s)"%(I, J))
897-
decomp.append(Composition(new_comp))
924+
decomp.append(Compositions()(new_comp))
898925
return decomp
899926

900927
def refinement_splitting_lengths(self, J):
@@ -921,7 +948,7 @@ def refinement_splitting_lengths(self, J):
921948
...
922949
ValueError: composition J (= [2, 1]) does not refine self (= [1, 2])
923950
"""
924-
return Composition(map(len,self.refinement_splitting(J)))
951+
return Compositions()(map(len,self.refinement_splitting(J)))
925952

926953
refinement = deprecated_function_alias(13243, refinement_splitting_lengths)
927954

@@ -1685,6 +1712,11 @@ def from_subset(self, S, n):
16851712
sage: Compositions().from_subset({2,1,5,9}, 12)
16861713
[1, 1, 3, 4, 3]
16871714
1715+
sage: Compositions().from_subset([], 12)
1716+
[12]
1717+
sage: Compositions().from_subset([], 0)
1718+
[]
1719+
16881720
TESTS::
16891721
16901722
sage: Compositions().from_subset([2,1,5,9],9)

src/sage/combinat/ncsf_qsym/generic_basis_code.py

+60-5
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
from sage.misc.cachefunc import cached_method
3030
from sage.categories.realizations import Category_realization_of_parent
3131
from sage.categories.modules_with_basis import ModulesWithBasis, ModuleMorphismByLinearity
32-
from sage.combinat.composition import Composition
32+
from sage.combinat.composition import Compositions, Composition
3333
from sage.combinat.partition import Partition
3434
from sage.combinat.permutation import Permutations
3535
from sage.rings.integer import Integer
@@ -114,9 +114,9 @@ def __getitem__(self, c, *rest):
114114
assert len(rest) == 0
115115
else:
116116
if len(rest) > 0 or isinstance(c, (int, Integer)):
117-
c = Composition([c] + list(rest))
117+
c = self._basis_keys([c] + list(rest))
118118
else:
119-
c = Composition(list(c))
119+
c = self._basis_keys(list(c))
120120
return self.monomial(c)
121121

122122
# could go to Algebras(...).Graded().Connected() or Modules(...).Graded().Connected()
@@ -137,7 +137,7 @@ def one_basis(self):
137137
sage: parent(L).one_basis()
138138
[]
139139
"""
140-
return Composition([])
140+
return Compositions()([])
141141

142142
# Combinatorial rules
143143

@@ -294,7 +294,7 @@ def sum_of_partition_rearrangements(self, par):
294294
sage: elementary.sum_of_partition_rearrangements(Partition([]))
295295
L[]
296296
"""
297-
return self.sum_of_monomials( Composition(comp) for comp in Permutations(par) )
297+
return self.sum_of_monomials( self._basis_keys(comp) for comp in Permutations(par) )
298298

299299
def _comp_to_par(self, comp):
300300
"""
@@ -680,6 +680,61 @@ def counit_on_basis(self, I):
680680
else:
681681
return self.base_ring().one()
682682

683+
def degree_negation(self, element):
684+
r"""
685+
Return the image of ``element`` under the degree negation
686+
automorphism of ``self``.
687+
688+
The degree negation is the automorphism which scales every
689+
homogeneous element of degree `k` by `(-1)^k` (for all `k`).
690+
691+
INPUT:
692+
693+
- ``element`` -- element of ``self``
694+
695+
EXAMPLES::
696+
697+
sage: NSym = NonCommutativeSymmetricFunctions(ZZ)
698+
sage: S = NSym.S()
699+
sage: f = 2*S[2,1] + 4*S[1,1] - 5*S[1,2] - 3*S[[]]
700+
sage: S.degree_negation(f)
701+
-3*S[] + 4*S[1, 1] + 5*S[1, 2] - 2*S[2, 1]
702+
703+
sage: QSym = QuasiSymmetricFunctions(QQ)
704+
sage: dI = QSym.dualImmaculate()
705+
sage: f = -3*dI[2,1] + 4*dI[2] + 2*dI[1]
706+
sage: dI.degree_negation(f)
707+
-2*dI[1] + 4*dI[2] + 3*dI[2, 1]
708+
709+
TESTS:
710+
711+
Using :meth:`degree_negation` on an element of a different
712+
basis works correctly::
713+
714+
sage: NSym = NonCommutativeSymmetricFunctions(QQ)
715+
sage: S = NSym.S()
716+
sage: Phi = NSym.Phi()
717+
sage: S.degree_negation(Phi[2])
718+
-S[1, 1] + 2*S[2]
719+
sage: S.degree_negation(Phi[3])
720+
-S[1, 1, 1] + 3/2*S[1, 2] + 3/2*S[2, 1] - 3*S[3]
721+
sage: Phi.degree_negation(S[3])
722+
-1/6*Phi[1, 1, 1] - 1/4*Phi[1, 2] - 1/4*Phi[2, 1] - 1/3*Phi[3]
723+
724+
The zero element behaves well::
725+
726+
sage: a = Phi.degree_negation(S.zero()); a
727+
0
728+
sage: parent(a)
729+
Non-Commutative Symmetric Functions over the Rational Field in the Phi basis
730+
731+
.. TODO::
732+
733+
Generalize this to all graded vector spaces?
734+
"""
735+
return self.sum_of_terms([ (lam, (-1)**(sum(lam)%2) * a)
736+
for lam, a in self(element) ])
737+
683738
class ElementMethods:
684739

685740
def duality_pairing(self, y):

0 commit comments

Comments
 (0)