Skip to content

Commit b17e117

Browse files
author
Release Manager
committed
Trac #34518: Expand Schubert polynomials in the Key basis
Expand Schubert polynomials in the Key Basis using the peelable tableaux algorithm of Reiner and Shimozono. URL: https://trac.sagemath.org/34518 Reported by: tkarn Ticket author(s): Trevor K. Karn Reviewer(s): Travis Scrimshaw
2 parents 12f635e + a4107d3 commit b17e117

File tree

3 files changed

+129
-5
lines changed

3 files changed

+129
-5
lines changed

src/sage/combinat/diagram.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1013,15 +1013,28 @@ def peelable_tableaux(self):
10131013
10141014
This implementation uses the algorithm suggested in Remark 25
10151015
of [RS1995]_.
1016+
1017+
TESTS:
1018+
1019+
Corner case::
1020+
1021+
sage: from sage.combinat.diagram import NorthwestDiagram
1022+
sage: D = NorthwestDiagram([])
1023+
sage: D.peelable_tableaux()
1024+
{[]}
10161025
"""
10171026
# TODO: There is a condition on the first column (if the rows in Dhat
10181027
# are a subset of the rows in the first column) which simplifies the
10191028
# description without performing JDT, so we should implement that
10201029

1030+
# empty diagram case
1031+
if not self:
1032+
return set([Tableau([])])
1033+
10211034
# if there is a single column in the diagram then there is only
10221035
# one posslbe peelable tableau.
10231036
if self._n_nonempty_cols == 1:
1024-
return {Tableau([[i+1] for i, j in self.cells()])}
1037+
return set([Tableau([[i+1] for i, j in self.cells()])])
10251038

10261039
first_col = min(j for i, j in self._cells)
10271040

src/sage/combinat/key_polynomial.py

Lines changed: 80 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,7 @@ def _coerce_map_from_(self, R):
479479
EXAMPLES::
480480
481481
sage: k = KeyPolynomials(QQ)
482-
sage: m1 = k([3,2,4,0]); m1
482+
sage: m1 = k([3, 2, 4, 0]); m1
483483
k[3, 2, 4]
484484
sage: m2 = k(Composition([3, 2, 4])); m2
485485
k[3, 2, 4]
@@ -490,10 +490,19 @@ def _coerce_map_from_(self, R):
490490
sage: z = R.gen()
491491
sage: z[0] * k([4, 3, 3, 2])
492492
k[5, 3, 3, 2]
493+
494+
sage: X = SchubertPolynomialRing(QQ)
495+
sage: k(X([4, 3, 2, 1]))
496+
k[3, 2, 1]
493497
"""
494498
P = self._polynomial_ring
495499
if R is P:
496500
return self.from_polynomial
501+
502+
from sage.combinat.schubert_polynomial import SchubertPolynomialRing_xbasis
503+
if isinstance(R, SchubertPolynomialRing_xbasis):
504+
return self.from_schubert_polynomial
505+
497506
phi = P.coerce_map_from(R)
498507
if phi is not None:
499508
return self.coerce_map_from(P) * phi
@@ -645,6 +654,76 @@ def from_polynomial(self, f):
645654

646655
return out
647656

657+
def from_schubert_polynomial(self, x):
658+
r"""
659+
Expand a Schubert polynomial in the key basis.
660+
661+
EXAMPLES::
662+
663+
sage: k = KeyPolynomials(ZZ)
664+
sage: X = SchubertPolynomialRing(ZZ)
665+
sage: f = X([2,1,5,4,3])
666+
sage: k.from_schubert_polynomial(f)
667+
k[1, 0, 2, 1] + k[2, 0, 2] + k[3, 0, 0, 1]
668+
sage: k.from_schubert_polynomial(2)
669+
2*k[]
670+
sage: k(f)
671+
k[1, 0, 2, 1] + k[2, 0, 2] + k[3, 0, 0, 1]
672+
673+
sage: k = KeyPolynomials(GF(7), 4)
674+
sage: k.from_schubert_polynomial(f)
675+
k[1, 0, 2, 1] + k[2, 0, 2, 0] + k[3, 0, 0, 1]
676+
677+
TESTS::
678+
679+
sage: k = KeyPolynomials(ZZ)
680+
sage: k.from_schubert_polynomial(k([3,2]))
681+
Traceback (most recent call last):
682+
...
683+
ValueError: not a Schubert polynomial
684+
685+
sage: k = KeyPolynomials(ZZ)
686+
sage: X = SchubertPolynomialRing(ZZ)
687+
sage: it = iter(Compositions())
688+
sage: for _ in range(50):
689+
....: C = next(it)
690+
....: assert k.from_schubert_polynomial(X(k[C])) == k[C], C
691+
692+
sage: k = KeyPolynomials(ZZ, 4)
693+
sage: X = SchubertPolynomialRing(ZZ)
694+
sage: it = iter(k.basis().keys())
695+
sage: for _ in range(50):
696+
....: C = next(it)
697+
....: assert k.from_schubert_polynomial(X(k[C])) == k[C], C
698+
"""
699+
if x in self.base_ring():
700+
return self(x)
701+
702+
from sage.combinat.schubert_polynomial import SchubertPolynomial_class
703+
if not isinstance(x, SchubertPolynomial_class):
704+
raise ValueError('not a Schubert polynomial')
705+
706+
from sage.combinat.diagram import RotheDiagram
707+
out = self.zero()
708+
if self._k is not None:
709+
def build_elt(wt):
710+
wt = list(wt)
711+
wt += [0] * (self._k - len(wt))
712+
return self[wt]
713+
else:
714+
def build_elt(wt):
715+
return self[wt]
716+
717+
for m, c in x.monomial_coefficients().items():
718+
D = RotheDiagram(m)
719+
a = self.zero()
720+
for d in D.peelable_tableaux():
721+
a += build_elt(d.left_key_tableau().weight())
722+
out += c * a
723+
724+
return out
725+
726+
648727
def divided_difference(f, i):
649728
r"""
650729
Apply the ``i``-th divided difference operator to the polynomial ``f``.

src/sage/combinat/schubert_polynomial.py

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,15 +73,18 @@
7373
#
7474
# https://www.gnu.org/licenses/
7575
# ****************************************************************************
76-
from sage.combinat.free_module import CombinatorialFreeModule
7776
from sage.categories.all import GradedAlgebrasWithBasis
77+
from sage.combinat.free_module import CombinatorialFreeModule
78+
from sage.combinat.key_polynomial import KeyPolynomial
79+
from sage.combinat.permutation import Permutations, Permutation
80+
from sage.misc.cachefunc import cached_method
7881
from sage.rings.integer import Integer
7982
from sage.rings.integer_ring import ZZ
83+
from sage.rings.polynomial.infinite_polynomial_element import InfinitePolynomial_sparse
8084
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
8185
from sage.rings.polynomial.multi_polynomial import is_MPolynomial
82-
from sage.combinat.permutation import Permutations, Permutation
86+
8387
import sage.libs.symmetrica.all as symmetrica
84-
from sage.misc.cachefunc import cached_method
8588

8689

8790
def SchubertPolynomialRing(R):
@@ -414,6 +417,17 @@ def _element_constructor_(self, x):
414417
sage: X(x1^2*x2)
415418
X[3, 2, 1]
416419
420+
sage: S.<x> = InfinitePolynomialRing(QQ)
421+
sage: X(x[0]^2*x[1])
422+
X[3, 2, 1]
423+
sage: X(x[0]*x[1]^2*x[2]^2*x[3] + x[0]^2*x[1]^2*x[2]*x[3] + x[0]^2*x[1]*x[2]^2*x[3])
424+
X[2, 4, 5, 3, 1]
425+
426+
sage: from sage.combinat.key_polynomial import KeyPolynomialBasis
427+
sage: k = KeyPolynomialBasis(QQ)
428+
sage: X(k([3,2,1]))
429+
X[4, 3, 2, 1]
430+
417431
TESTS:
418432
419433
We check that :trac:`12924` is fixed::
@@ -429,6 +443,15 @@ def _element_constructor_(self, x):
429443
430444
sage: X([])
431445
X[1]
446+
447+
Check the round trip from key polynomials::
448+
449+
sage: k = KeyPolynomials(ZZ)
450+
sage: X = SchubertPolynomialRing(ZZ)
451+
sage: it = iter(Permutations())
452+
sage: for _ in range(50):
453+
....: P = next(it)
454+
....: assert X(k(X(P))) == X(P), P
432455
"""
433456
if isinstance(x, list):
434457
# checking the input to avoid symmetrica crashing Sage, see trac 12924
@@ -441,6 +464,14 @@ def _element_constructor_(self, x):
441464
return self._from_dict({perm: self.base_ring().one()})
442465
elif is_MPolynomial(x):
443466
return symmetrica.t_POLYNOM_SCHUBERT(x)
467+
elif isinstance(x, InfinitePolynomial_sparse):
468+
R = x.polynomial().parent()
469+
# massage the term order to be what symmetrica expects
470+
S = PolynomialRing(R.base_ring(),
471+
names=list(map(repr, reversed(R.gens()))))
472+
return symmetrica.t_POLYNOM_SCHUBERT(S(x.polynomial()))
473+
elif isinstance(x, KeyPolynomial):
474+
return self(x.expand())
444475
else:
445476
raise TypeError
446477

@@ -468,3 +499,4 @@ def product_on_basis(self, left, right):
468499
X[4, 2, 1, 3]
469500
"""
470501
return symmetrica.mult_schubert_schubert(left, right)
502+

0 commit comments

Comments
 (0)