Example 1:
Given that the monomial basis and homogeneous basis for symmetric functions are dual, define the Schur functions as the unique basis satisfying
From the definition, find the expansion of $s_\lambda$ into monomials for $|\lambda| \le 3$.
We must have $s_1 = m_1, s_{1,1} = m_{1,1}, s_{1,1,1} = m_{1,1,1}$. We know that $s_2 = as_{1,1} + m_2$. How do we find $a$?
We know that $\langle as_{1,1} + m_2, m_{1,1} \rangle = 0$. To evaluate this, we must find the expression of $m_{1,1}$ in the $h$ basis.
{{{id=116| SF = SymmetricFunctions(QQ) h = SF.homogeneous() m = SF.monomial() h(m[1,1]) /// h[1, 1] - h[2] }}}So $a(1) + 1(-1) = 0$ and $a = 1$. We've solved $|\lambda| = 2$ (if we believe that a solution exists). How did we do it?
{{{id=119| QQ /// Rational Field }}}In Sage, when we are working with algebras (such as the algebra of symmetric functions) the usual practice is to specify the coefficient ring. Sage can use that information (or not) when performing calculations. When working with symmetric functions, much of the underlying code implicitly assumes that the coefficient ring contains QQ. Use smaller rings (ZZ, finite fields) with caution and carefully check your results!
{{{id=120| SF = SymmetricFunctions(QQ) SF /// Symmetric Functions over Rational Field }}}SF now represents the algebra of symmetric functions over the raionals. We can't really compute with this until we've chosen a basis, so that is what we will do next. Note: typing SF.<tab> will show a list of all the (public) methods the object SF has. If you a know a bit of symmetric function theory, try this and see which you recognize as basis names.
{{{id=125| h = SF.homogeneous() h /// Symmetric Function Algebra over Rational Field, Homogeneous symmetric functions as basis }}} {{{id=123| m = SF.monomial() m /// Symmetric Function Algebra over Rational Field, Monomial symmetric functions as basis }}}We can now do computations in the algebra of symmetric functions using the two bases we have defined. We can write down explicit elements and perform explicit computations. For a generic algebra in sage with a basis labelled $m$ and indexed by Partitions, the syntax for accessing an element would typically be:
{{{id=126| m.basis()[Partition([1,1,1])] /// m[1, 1, 1] }}}Fortunately, the symmetric function algebra allows the shortcuts of:
{{{id=128| print m[1,1,1] print m[[1,1,1]] print m[Partition([1,1,1])] /// m[1, 1, 1] m[1, 1, 1] m[1, 1, 1] }}}Note that m[] will not work (for technical reasons, it would be very difficult to implement) but m[[]] works fine.
{{{id=131| m[[]] == 1 /// True }}}We can check that these are what we think they are by expanding them into variables:
{{{id=148| m[2,1].expand(3) /// x0^2*x1 + x0*x1^2 + x0^2*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 }}} {{{id=150| h[3].expand(3) /// x0^3 + x0^2*x1 + x0*x1^2 + x1^3 + x0^2*x2 + x0*x1*x2 + x1^2*x2 + x0*x2^2 + x1*x2^2 + x2^3 }}}The python language (and hence sage) tend to prefer counting starting from 0. If we want to expand into an alphabet that starting from x1, there is a trick to do that:
{{{id=151| X = ''.join('x%d,'%i for i in range(1,6)) X /// 'x1,x2,x3,x4,x5,' }}}X is now the string shown above (I won't explain the details here. If you're curious look up the documentation on the 'join' method of strings, the range command, and the details of python string formatting:). We can get rid of the terminal comma with slice notation:
{{{id=152| X = X[:-1] X /// 'x1,x2,x3,x4,x5' }}}and now we can expand a symmetric function in these symbols:
{{{id=154| m[1,1,1].expand(5,alphabet=X) /// x1*x2*x3 + x1*x2*x4 + x1*x3*x4 + x2*x3*x4 + x1*x2*x5 + x1*x3*x5 + x2*x3*x5 + x1*x4*x5 + x2*x4*x5 + x3*x4*x5 }}}Algebraic expressions can be written out and will be computed automatically:
{{{id=133| h[2,1]*(h[1]^3 + 2*h[3]) + 1 /// h[] + h[2, 1, 1, 1, 1] + 2*h[3, 2, 1] }}}Finally, we can change from one basis to another with the syntax desired_basis( something_in_the_algebra ). For example:
{{{id=135| h(m[1,1]) /// h[1, 1] - h[2] }}}We can use the same techniques to finish off the $|\lambda| = 3$ case. (Remember the original problem?) Recall that $s_{1,1,1} = m_{1,1,1}$. In the $h$ basis, this is:
{{{id=136| h(m[1,1,1]) /// h[1, 1, 1] - 2*h[2, 1] + h[3] }}}Recall that $s_{2,1} = am_{1,1,1} + m_{2,1}$. Solving $\langle s_{2,1} , s_{1,1,1} \rangle = 0$ gives $a = 2$.
Finally, we consider $s_{3} = am_{1,1,1} + bm_{2,1} + m_{3}$. From the scalar product with $s_{1,1,1}$ we see that $a - 2b + 1 = 0$. For the scalar product with $s_{2,1}$ we need to first convert to the $h$ basis.
{{{id=137| h(2*m[1,1,1] + m[2,1]) /// h[2, 1] - h[3] }}}Hence $b - 1 = 0$, so $b = 1$ and $a = 1$.
Did we get the right answer? Let's check. We can define the five classical bases of symmetric functions (e = elementary, h = complete homogeneous, m = monomial, p = power sum, s = Schur) in one command, as follows. (Note: there may be some warnings if existing names are over-written.)
{{{id=139| SF.inject_shorthands() /// /usr/local/src/sage/sage-4.4.4/local/lib/python2.6/site-packages/sage/combinat/sf/sf.py:380: RuntimeWarning: redefining global value `h` inject_variable(shorthand, getattr(self, shorthand)()) /usr/local/src/sage/sage-4.4.4/local/lib/python2.6/site-packages/sage/combinat/sf/sf.py:380: RuntimeWarning: redefining global value `e` inject_variable(shorthand, getattr(self, shorthand)()) /usr/local/src/sage/sage-4.4.4/local/lib/python2.6/site-packages/sage/combinat/sf/sf.py:380: RuntimeWarning: redefining global value `m` inject_variable(shorthand, getattr(self, shorthand)()) }}}Now we can see what we get if we express the Schur functions in the monomial basis:
{{{id=146| for mu in Partitions(3): print s[mu],' = ',m(s(mu)) /// s[3] = m[1, 1, 1] + m[2, 1] + m[3] s[2, 1] = 2*m[1, 1, 1] + m[2, 1] s[1, 1, 1] = m[1, 1, 1] }}}We can verify that the Schur functions are orthonormal and that m and h are dual:
{{{id=54| h[2,1].scalar(m[2,1]) /// 1 }}} {{{id=55| [h[2,1].scalar(m[mu]) for mu in Partitions(3)] /// [0, 1, 0] }}} {{{id=53| matrix([[h[mu].scalar(m[la]) for mu in Partitions(4)] for la in Partitions(4)]) /// [1 0 0 0 0] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 1 0] [0 0 0 0 1] }}} {{{id=290| for n in range(6): print matrix([[h[mu].scalar(m[la]) for mu in Partitions(n)] for la in Partitions(n)]) print /// [1] [1] [1 0] [0 1] [1 0 0] [0 1 0] [0 0 1] [1 0 0 0 0] [0 1 0 0 0] [0 0 1 0 0] [0 0 0 1 0] [0 0 0 0 1] [1 0 0 0 0 0 0] [0 1 0 0 0 0 0] [0 0 1 0 0 0 0] [0 0 0 1 0 0 0] [0 0 0 0 1 0 0] [0 0 0 0 0 1 0] [0 0 0 0 0 0 1] }}} {{{id=157| matrix([[s[la].scalar(s[mu]) for mu in Partitions(5)] for la in Partitions(5)]) /// [1 0 0 0 0 0 0] [0 1 0 0 0 0 0] [0 0 1 0 0 0 0] [0 0 0 1 0 0 0] [0 0 0 0 1 0 0] [0 0 0 0 0 1 0] [0 0 0 0 0 0 1] }}} {{{id=296| [mu for mu in Partitions(3)] /// [[3], [2, 1], [1, 1, 1]] }}}The syntax [f(x) for x in Set] is known as list comprehension.
Example 2: (Bases by orthongonality and triangularity)
A cute trick (really only intended for sage developers) is a way to define a new basis by orthogonality and triangularity properties. Here we create a basis that we label f, that will be the same as the Schur basis.
{{{id=164| from sage.combinat.sf.sfa import zee from sage.combinat.sf.orthotriang import SymmetricFunctionAlgebra_orthotriang f = SymmetricFunctionAlgebra_orthotriang(QQ, m, zee, 'f', 'Sure functions') /// }}}This works by assuming that there is an inner product in which the power sum basis is orthogonal but not orthonormal. The function zee used here accepts
a partition $\mu$ and returns $\langle p_\mu, p_\mu \rangle$. Surprisingly, there are multiple interesting bases that can be described in this way.
Exercise (for experienced sage users): Look at the source code to figure out how this all works!
Example 3: The Pieri rule
The Pieri rule says that the coefficient of $s_\lambda$ in $h_\mu$ is the number of semistandard (or column-strict) tableaux of shape $\lambda$ and with $\mu_1$ ones, $\mu_2$ twos, etc. Lets verify this in some examples:
{{{id=9| ST = SemistandardTableaux(5,[3,2]) ST /// Semistandard tableaux of size 5 and evaluation [3, 2] }}} {{{id=69| for t in ST: t.pp() print '-----------------' /// 1 1 1 2 2 ----------------- 1 1 1 2 2 ----------------- 1 1 1 2 2 ----------------- }}} {{{id=70| s(h[3,2]) /// s[3, 2] + s[4, 1] + s[5] }}} {{{id=166| s.sum_of_monomials( t.shape() for t in ST ) /// s[3, 2] + s[4, 1] + s[5] }}} {{{id=162| for mu in Partitions(5): print h[mu] == s.sum_of_monomials( t.shape() for t in SemistandardTableaux(5, mu)) /// True True True True True True True }}} {{{id=163| all( h[mu] == s.sum_of_monomials( t.shape() for t in SemistandardTableaux(10, mu)) for mu in Partitions(10) ) /// True }}}Example 4: k-Schur functions
We can work with more complicated bases of symmetric functions as well. Below we give an example of the non-trivial fact that $k$-Schur functions are Schur-positive.
{{{id=170| R.The above is an example of a non-trivial coefficient ring. A convenient ring to work over is QQ(t). This is the fraction field of ZZ[t]. We create this field in two steps. Then we create the ring of symmetric functions over this field and inject our usual shorthands. Finally, we create the ring of $k$-Schur functions by specifying the base ring, the level, and the value we want to use for the parameter t.
{{{id=185| ks3 /// k-Schur Functions at level 3 over Fraction Field of Univariate Polynomial Ring in t over Integer Ring }}}Note, these are computed in sage using the atom definition. We now expand some k-Schur functions in the Schur basis.
{{{id=172| for mu in Partitions(5,max_part=3): print ks3[mu],' = ',s(ks3[mu]) /// ks3[3, 2] = s[3, 2] + t*s[4, 1] + t^2*s[5] ks3[3, 1, 1] = s[3, 1, 1] + t*s[4, 1] ks3[2, 2, 1] = s[2, 2, 1] + t*s[3, 2] ks3[2, 1, 1, 1] = s[2, 1, 1, 1] + t*s[3, 1, 1] ks3[1, 1, 1, 1, 1] = s[1, 1, 1, 1, 1] + t*s[2, 1, 1, 1] + t^2*s[2, 2, 1] }}}We can define auxilliary functions to specify the parameter:
{{{id=171| def kill_t(poly): return poly.subs(t=0) /// }}} {{{id=176| def calm_t(poly): return poly.subs(t=1) /// }}}These functions are meant to be applied to polynomials (or rational functions) in $t$:
{{{id=175| print kill_t(3*t^2 + t - 1) print calm_t(3*t^2 + t - 1) /// -1 3 }}}We can use the map_coefficients method to apply these functions to all coefficients:
{{{id=173| print 'Under the map t goes to 0:' print for mu in Partitions(5, max_part=3): print ks3[mu],' -> ',s(ks3[mu]).map_coefficients(kill_t) /// Under the map t goes to 0: ks3[3, 2] -> s[3, 2] ks3[3, 1, 1] -> s[3, 1, 1] ks3[2, 2, 1] -> s[2, 2, 1] ks3[2, 1, 1, 1] -> s[2, 1, 1, 1] ks3[1, 1, 1, 1, 1] -> s[1, 1, 1, 1, 1] }}} {{{id=177| print 'Under the map t goes to 1:' print for mu in Partitions(5, max_part=3): print 'ks3%s'%mu,' -> ',s(ks3[mu]).map_coefficients(calm_t) /// Under the map t goes to 1: ks3[3, 2] -> s[3, 2] + s[4, 1] + s[5] ks3[3, 1, 1] -> s[3, 1, 1] + s[4, 1] ks3[2, 2, 1] -> s[2, 2, 1] + s[3, 2] ks3[2, 1, 1, 1] -> s[2, 1, 1, 1] + s[3, 1, 1] ks3[1, 1, 1, 1, 1] -> s[1, 1, 1, 1, 1] + s[2, 1, 1, 1] + s[2, 2, 1] }}}If we will always want the parameter to be 1, we can set it that way in the first place. Notice that we just use the base ring QQ here:
{{{id=178| ks3 = kSchurFunctions(QQ,3,1) SF = SymmetricFunctions(QQ) SF.inject_shorthands() /// }}} {{{id=180| for mu in Partitions(5,max_part=3): print 'ks3%s'%mu,' = ',s(ks3[mu]) /// ks3[3, 2] = s[3, 2] + s[4, 1] + s[5] ks3[3, 1, 1] = s[3, 1, 1] + s[4, 1] ks3[2, 2, 1] = s[2, 2, 1] + s[3, 2] ks3[2, 1, 1, 1] = s[2, 1, 1, 1] + s[3, 1, 1] ks3[1, 1, 1, 1, 1] = s[1, 1, 1, 1, 1] + s[2, 1, 1, 1] + s[2, 2, 1] }}}Functions in the sub-algebra $\QQ[h_1,h_2,h_3]$ can be expanded in terms of 3-Schur functions, when $t=1$:
{{{id=179| for mu in Partitions(5, max_part=3): print 'h%s'%mu,' = ',ks3(h[mu]) /// h[3, 2] = ks3[3, 2] h[3, 1, 1] = ks3[3, 1, 1] + ks3[3, 2] h[2, 2, 1] = ks3[2, 2, 1] + ks3[3, 1, 1] + ks3[3, 2] h[2, 1, 1, 1] = ks3[2, 1, 1, 1] + 2*ks3[2, 2, 1] + 2*ks3[3, 1, 1] + ks3[3, 2] h[1, 1, 1, 1, 1] = ks3[1, 1, 1, 1, 1] + 3*ks3[2, 1, 1, 1] + 4*ks3[2, 2, 1] + 3*ks3[3, 1, 1] + ks3[3, 2] }}}Example 5: Macdonald Polynomials
{{{id=197| R.= ZZ[] R = FractionField(R) P = MacdonaldPolynomialsP(R,q,t) # or Q, H, Ht, J, S H = MacdonaldPolynomialsH(R,q,t) Qp = HallLittlewoodQp(R,t) # or Q or P (Qp = H) ks3 = kSchurFunctions(R,3,t) J = JackPolynomialsP(R, t) # or Q, Qp, J SF = SymmetricFunctions(R) SF.inject_shorthands() /// }}}The $P$-basis of Macdonald polynomials reduce to Schur polynomials when $q=t$.
{{{id=216| def t_to_q(poly): return poly.subs(t=q) /// }}} {{{id=199| for mu in Partitions(5): print mu print ' ',s(P[mu]) print print ' ',s(P[mu]).map_coefficients(t_to_q) print '--------------------------------' /// [5] ((-q^10+q^9*t+q^8*t-q^7*t^2+q^7*t-q^6*t^2+q^6*t-2*q^5*t^2+q^4*t^3-q^4*t^2+q^3*t^3-q^3*t^2+q^2*t^3+q*t^3-t^4)/(-q^10*t^4+q^9*t^3+q^8*t^3+q^7*t^3-q^7*t^2+q^6*t^3-q^6*t^2-2*q^5*t^2-q^4*t^2+q^4*t-q^3*t^2+q^3*t+q^2*t+q*t-1))*s[1, 1, 1, 1, 1] + ((q^9-q^8*t+q^8-2*q^7*t+q^6*t^2+q^7-3*q^6*t+2*q^5*t^2+q^6-3*q^5*t+3*q^4*t^2-q^3*t^3-2*q^4*t+3*q^3*t^2-q^2*t^3-q^3*t+2*q^2*t^2-q*t^3+q*t^2-t^3)/(-q^9*t^3+q^7*t^2+q^6*t^2+q^5*t^2-q^4*t-q^3*t-q^2*t+1))*s[2, 1, 1, 1] + ((-q^8*t+q^7*t^2+q^8-2*q^7*t+2*q^6*t^2-q^5*t^3+q^7-3*q^6*t+3*q^5*t^2-q^4*t^3+q^6-3*q^5*t+3*q^4*t^2-q^3*t^3+q^5-3*q^4*t+3*q^3*t^2-q^2*t^3+q^4-2*q^3*t+2*q^2*t^2-q*t^3-q^2*t+q*t^2)/(-q^9*t^3+q^7*t^2+q^6*t^2+q^5*t^2-q^4*t-q^3*t-q^2*t+1))*s[2, 2, 1] + ((-q^7+q^6*t-q^6+2*q^5*t-q^4*t^2-2*q^5+3*q^4*t-q^3*t^2-q^4+3*q^3*t-2*q^2*t^2-q^3+2*q^2*t-q*t^2+q*t-t^2)/(-q^7*t^2+q^4*t+q^3*t-1))*s[3, 1, 1] + ((q^6*t-q^5*t^2-q^6+2*q^5*t-q^4*t^2-q^5+2*q^4*t-q^3*t^2-q^4+2*q^3*t-q^2*t^2-q^3+2*q^2*t-q*t^2-q^2+q*t)/(-q^7*t^2+q^4*t+q^3*t-1))*s[3, 2] + ((q^4-q^3*t+q^3-q^2*t+q^2-q*t+q-t)/(-q^4*t+1))*s[4, 1] + s[5] s[5] -------------------------------- [4, 1] ((q^6*t-q^5*t^2+q^6-q^5*t-q^4*t^2+q^3*t^3-q^4*t+q^2*t^3-q^3*t+q^2*t^2+q*t^3-t^4+q*t^2-t^3)/(-q^6*t^4+q^5*t^3+q^4*t^3-q^2*t-q*t+1))*s[1, 1, 1, 1, 1] + ((-q^6*t^2+q^5*t^3-q^5*t^2+2*q^4*t^3-q^3*t^4-q^4*t^2+2*q^3*t^3-q^2*t^4+q^5-q^4*t+q^2*t^3-q*t^4+q^4-2*q^3*t+q^2*t^2+q^3-2*q^2*t+q*t^2-q*t+t^2)/(-q^6*t^4+q^5*t^3+q^4*t^3-q^2*t-q*t+1))*s[2, 1, 1, 1] + ((-q^5*t+2*q^4*t^2-q^3*t^3-q^4*t+2*q^3*t^2-q^2*t^3-q^4+q^3*t+q^2*t^2-q*t^3-q^3+2*q^2*t-q*t^2-q^2+2*q*t-t^2)/(-q^5*t^3+q^3*t^2+q^2*t-1))*s[2, 2, 1] + ((q^5*t^2-q^4*t^3+q^4*t^2-q^3*t^3+q^3*t^2-q^2*t^3-q^3+q^2*t-q^2+q*t-q+t)/(-q^5*t^3+q^3*t^2+q^2*t-1))*s[3, 1, 1] + ((q^2-q*t+q-t)/(-q^2*t+1))*s[3, 2] + s[4, 1] s[4, 1] -------------------------------- [3, 2] ((-q^3*t^3+q^2*t^4+q^4*t-2*q^3*t^2+q^2*t^3+q*t^4-t^5+q^4-q^3*t-q^2*t^2+2*q*t^3-t^4-q^2*t+q*t^2)/(-q^4*t^5+q^3*t^4+q^3*t^3+q^2*t^3-q^2*t^2-q*t^2-q*t+1))*s[1, 1, 1, 1, 1] + ((-q^3*t+2*q^2*t^2-q*t^3-q^3+q^2*t+q*t^2-t^3-q^2+2*q*t-t^2)/(-q^3*t^3+q^2*t^2+q*t-1))*s[2, 1, 1, 1] + ((q^3*t^2-q^2*t^3+q^2*t^2-q*t^3-q^2+q*t-q+t)/(-q^3*t^3+q^2*t^2+q*t-1))*s[2, 2, 1] + ((q-t)/(-q*t+1))*s[3, 1, 1] + s[3, 2] s[3, 2] -------------------------------- [3, 1, 1] ((-q^3*t^2+q^2*t^3-q^3*t+q^2*t^2+q*t^3-t^4-q^3+q^2*t+q*t^2-t^3+q*t-t^2)/(-q^3*t^4+q^2*t^3+q*t-1))*s[1, 1, 1, 1, 1] + ((q^3*t^3-q^2*t^4+q^2*t^3-q*t^4-q^2+q*t-q+t)/(-q^3*t^4+q^2*t^3+q*t-1))*s[2, 1, 1, 1] + ((q-t)/(-q*t+1))*s[2, 2, 1] + s[3, 1, 1] s[3, 1, 1] -------------------------------- [2, 2, 1] ((q*t^4-t^5-q^2*t^2+2*q*t^3-t^4-q^2*t+2*q*t^2-t^3-q^2+q*t)/(-q^2*t^5+q*t^3+q*t^2-1))*s[1, 1, 1, 1, 1] + ((q*t-t^2+q-t)/(-q*t^2+1))*s[2, 1, 1, 1] + s[2, 2, 1] s[2, 2, 1] -------------------------------- [2, 1, 1, 1] ((q*t^3-t^4+q*t^2-t^3+q*t-t^2+q-t)/(-q*t^4+1))*s[1, 1, 1, 1, 1] + s[2, 1, 1, 1] s[2, 1, 1, 1] -------------------------------- [1, 1, 1, 1, 1] s[1, 1, 1, 1, 1] s[1, 1, 1, 1, 1] -------------------------------- }}}The $H$ basis for Macdonald polynomials, restricted to partitions whose parts are $\le k$, are in the subring of $k$-Schur functions, and have coefficients which are in $\mathbb{N}[q,t]$:
{{{id=241| for mu in Partitions(5,max_part=3): print ks3(H[mu]) print /// q^4*ks3[1, 1, 1, 1, 1] + (q^3*t+q^3+q^2)*ks3[2, 1, 1, 1] + (q^3*t+q^2*t+q^2+q)*ks3[2, 2, 1] + (q^2*t+q*t+q)*ks3[3, 1, 1] + ks3[3, 2] q^3*ks3[1, 1, 1, 1, 1] + (q^3*t^2+q^2+q)*ks3[2, 1, 1, 1] + (q^2*t^2+q^2*t+q*t+q)*ks3[2, 2, 1] + (q^2*t^2+q*t^2+1)*ks3[3, 1, 1] + t*ks3[3, 2] q^2*ks3[1, 1, 1, 1, 1] + (q^2*t^2+q*t+q)*ks3[2, 1, 1, 1] + (q^2*t^3+q*t^2+q*t+1)*ks3[2, 2, 1] + (q*t^3+q*t^2+t)*ks3[3, 1, 1] + t^2*ks3[3, 2] q*ks3[1, 1, 1, 1, 1] + (q*t^3+q*t^2+1)*ks3[2, 1, 1, 1] + (q*t^4+q*t^3+t^2+t)*ks3[2, 2, 1] + (q*t^5+t^3+t^2)*ks3[3, 1, 1] + t^4*ks3[3, 2] ks3[1, 1, 1, 1, 1] + (t^4+t^3+t^2)*ks3[2, 1, 1, 1] + (t^6+t^5+t^4+t^3)*ks3[2, 2, 1] + (t^7+t^6+t^5)*ks3[3, 1, 1] + t^8*ks3[3, 2] }}}This only works for $k$-bounded partitions:
{{{id=243| ks3(H[5]) /// Traceback (most recent call last): File "", line 1, in File "_sage_input_60.py", line 10, in exec compile(u'open("___code___.py","w").write("# -*- coding: utf-8 -*-\\n" + _support_.preparse_worksheet_cell(base64.b64decode("a3MzKEhbNV0p"),globals())+"\\n"); execfile(os.path.abspath("___code___.py"))' + '\n', '', 'single') File "", line 1, in File "/tmp/tmprVc22M/___code___.py", line 3, in exec compile(u'ks3(H[_sage_const_5 ])' + '\n', '', 'single') File "", line 1, in File "parent.pyx", line 859, in sage.structure.parent.Parent.__call__ (sage/structure/parent.c:6407) File "map.pyx", line 1176, in sage.categories.map.FormalCompositeMap._call_ (sage/categories/map.c:5564) File "morphism.pyx", line 220, in sage.categories.morphism.SetMorphism._call_ (sage/categories/morphism.c:3784) File "/usr/local/src/sage/sage-4.4.4/local/lib/python2.6/site-packages/sage/combinat/sf/kschur.py", line 272, in _s_to_self return self._change_by_triangularity(x, self._self_to_s_cache, True) File "/usr/local/src/sage/sage-4.4.4/local/lib/python2.6/site-packages/sage/combinat/sf/kschur.py", line 152, in _change_by_triangularity raise ValueError,"%s is not in the space spanned by %s."%(orig,self) ValueError: q^10*s[1, 1, 1, 1, 1] + (q^9+q^8+q^7+q^6)*s[2, 1, 1, 1] + (q^8+q^7+q^6+q^5+q^4)*s[2, 2, 1] + (q^7+q^6+2*q^5+q^4+q^3)*s[3, 1, 1] + (q^6+q^5+q^4+q^3+q^2)*s[3, 2] + (q^4+q^3+q^2+q)*s[4, 1] + s[5] is not in the space spanned by k-Schur Functions at level 3 over Fraction Field of Multivariate Polynomial Ring in q, t over Integer Ring. }}} The $Qp$ Hall-Littlewood polynomials can be given as a sum of Schur functions, with coefficients described by the charge statistic.
{{{id=273| out = 0 for la in Partitions(5): for T in SemistandardTableaux(la,[2,2,1]): out += t^T.charge() * s[la] out /// s[2, 2, 1] + t*s[3, 1, 1] + (t^2+t)*s[3, 2] + (t^3+t^2)*s[4, 1] + t^4*s[5] }}} {{{id=274| s(Qp([2,2,1])) /// s[2, 2, 1] + t*s[3, 1, 1] + (t^2+t)*s[3, 2] + (t^3+t^2)*s[4, 1] + t^4*s[5] }}}Exercise: Verify this identity for all partitions of size $\le$ 5.
The Macdonald polynomials in the $H$ basis reduce to the $Qp$ Hall-Littlewood polynomials at $q=0$:
{{{id=272| def kill_q(poly): return poly.subs(q=0) /// }}} {{{id=271| for mu in Partitions(5): print Qp(H[mu]).map_coefficients(kill_q) /// Qp[5] Qp[4, 1] Qp[3, 2] Qp[3, 1, 1] Qp[2, 2, 1] Qp[2, 1, 1, 1] Qp[1, 1, 1, 1, 1] }}}Example 6: Cores and bounded partitions
The bijection between cores and partitions is not quite explicitly implemented, but is easy to add.
{{{id=221| def from_kbounded_to_core(p,k): return p.k_skew(k).outer() def from_core_to_kbounded(c,k): p=[] for i in range(len(c)): p.append(0) for j in range(c[i]): h = c.hook_length(i,j) if (k+1).divides(h): raise ValueError, "%s is not a %s-core"%(c,k+1) if h <= k: p[-1] += 1 return Partition(p) /// }}}We verify this is a bijection on a small example:
{{{id=222| Partition([3,2,2,1]).pp() print from_kbounded_to_core(Partition([3,2,2,1]),3).pp() /// *** ** ** * ****** *** ** * }}} {{{id=223| from_core_to_kbounded(Partition([6,3,2,1]),3).pp() /// *** ** ** * }}} {{{id=294| all( mu == from_core_to_kbounded(from_kbounded_to_core(mu, 3), 3) for mu in Partitions(10, max_part=3) ) /// True }}}The skew of two cores which differ by weight one, where the first is contained in the second, is a sequence of ribbons:
{{{id=230| three_cores_wt_six = [from_kbounded_to_core(mu,3) for mu in Partitions(6, max_part = 3)] three_cores_wt_seven = [from_kbounded_to_core(mu,3) for mu in Partitions(7, max_part = 3)] /// }}} {{{id=233| for mu in three_cores_wt_six: for la in three_cores_wt_seven: if la.contains(mu): print (la/mu).ferrers_diagram() print "-----------------------" /// WARNING: Output truncated! full_output.txt # # # ----------------------- # # ----------------------- ## ## ----------------------- # ----------------------- # # # ----------------------- # # ----------------------- ## ## ----------------------- # # # ----------------------- ## # ----------------------- # # # # ----------------------- # ----------------------- # # ... # ----------------------- ### ----------------------- # # ----------------------- # ----------------------- ## ----------------------- # ----------------------- # # # ----------------------- # # # # ----------------------- ## ----------------------- # # # ----------------------- }}}Example 7: The dual affine $k$-Pieri rule
If we want to multiply $h_1$ by the dual $2$-Schur indexed by the $2$-bounded partition $(2,1,1)$, we first find the corresponding core:
{{{id=250| c = from_kbounded_to_core(Partition([2,1,1]), 2) c.pp() /// *** * * }}}Then we look at all $2$-bounded partitions of 5 whose associated cores contain c:
{{{id=252| core_list = [from_kbounded_to_core(mu,2) for mu in Partitions(5, max_part=2) if from_kbounded_to_core(mu, 2).contains(c)] core_list /// [[5, 3, 1], [4, 2, 1, 1], [3, 2, 2, 1, 1]] }}}Then we look at the associated partition and the number of connected components of the skew-cores:
{{{id=256| for mu in core_list: print from_core_to_kbounded(mu,2) print (mu / c).ferrers_diagram() print "--------------" /// [2, 2, 1] ## ## -------------- [2, 1, 1, 1] # # # -------------- [1, 1, 1, 1, 1] # # # # -------------- }}}So we can see that the result is $2\sigma_{2,2,1} + 3\sigma_{2,1,1,1} + 2\sigma_{1,1,1,1,1}$. In order to automate this, we need to isolate the ribbons.
{{{id=258| def reading_order(c,d): r"""c and d are cells of the form (i,j). Returns -1 if d precedes c in the reading order, 0 if c == d, 1 otherwise.""" if c[0] > d[0]: # higher comes first return int(1) if c[0] == d[0]: if c[1] > d[1]: return int(-1) # to the right comes after if c[1] == d[1]: return int(0) return int(1) return int(-1) def cells(skew): return [c for c in skew.outer().cells() if c not in skew.inner().cells()] def ribbons(cell_list): """Input is a list of cells (i,j) in 1st quadrant which form a set of ribbons. Output is a list of the maximal connected ribbons.""" cl = sorted(cell_list, reading_order) rib = [cl.pop(0)] rib_list = [] while len(cl) > 0: i,j = cl.pop(0) if ((i-1,j) in rib) or ((i,j+1) in rib): # This cell is part of the same ribbon rib.append((i,j)) else: # This cell is part of a new ribbon rib_list.append(rib) rib = [(i,j)] rib_list.append(rib) return rib_list def ribbon_head_content(rib): return rib[-1][1] - rib[-1][0] /// }}}We can now print the number of ribbons together with the partition:
{{{id=253| for mu in core_list: print len(ribbons(cells(mu/c))), from_core_to_kbounded(mu, 2) print "-----------------" /// 2 [2, 2, 1] ----------------- 3 [2, 1, 1, 1] ----------------- 2 [1, 1, 1, 1, 1] ----------------- }}}We can now write a little function to compute the dual_k_pieri rule.
{{{id=262| def dual_k_pieri(la, r, k): r"returns the partitions and multiplicities of the basis functions indexing the product of the dual k-schur indexed by la and h_r" c = from_kbounded_to_core(Partition(la), k) n = sum(la) core_list = [from_kbounded_to_core(mu,k) for mu in Partitions(n+1, max_part=k) if from_kbounded_to_core(mu, k).contains(c)] # old_level will be a list of pairs: (core, content_of_ribbon_head) old_level = [] for mu in core_list: for rib in ribbons(cells(mu/c)): old_level.append( (mu, ribbon_head_content(rib)) ) for i in range(2, r+1): # We will build new_level from old_level if we have containment of cores # and the ascending condition on the content of the ribbon heads new_level = [] core_list = [from_kbounded_to_core(mu,k) for mu in Partitions(n+i, max_part=k)] for (small_core, ct) in old_level: for big_core in core_list: if big_core.contains(small_core): for rib in ribbons(cells(big_core/small_core)): if ribbon_head_content(rib) > ct: new_level.append( (big_core, ribbon_head_content(rib)) ) old_level = new_level # Now we make a dictionary associating the output partitions with their multiplicities out = {} for core, ct in old_level: par = from_core_to_kbounded(core, k) if par in out.keys(): out[par] += 1 else: out[par] = 1 return out /// }}} {{{id=263| dual_k_pieri([2,1,1], 2, 2) /// {[1, 1, 1, 1, 1, 1]: 3, [2, 2, 2]: 3, [2, 2, 1, 1]: 5, [2, 1, 1, 1, 1]: 3} }}}Exercise: Write a function to compute the partitions and coefficients that appear in the dual k-Schur expansion of $h_\mu$. (hint: iterate the dual_k_pieri rule)
Example 8: $k$-atoms
{{{id=287| for T in Partition([3,3,2,1,1]).k_atom(4): T.pp() print '---------------' /// 1 1 1 2 2 2 3 3 4 5 --------------- 1 1 1 3 2 2 2 3 4 5 --------------- 1 1 1 5 2 2 2 3 3 4 --------------- 1 1 1 3 5 2 2 2 3 4 --------------- 1 1 1 3 2 2 2 5 3 4 --------------- 1 1 1 4 2 2 2 5 3 3 --------------- 1 1 1 3 4 2 2 2 5 3 --------------- }}} {{{id=237| mu = Partition([3,3,2]) katom = mu.k_atom(3) for T in katom: T.pp() print '---------------------------' /// 1 1 1 2 2 2 3 3 --------------------------- 1 1 1 2 2 2 3 3 --------------------------- 1 1 1 3 2 2 2 3 --------------------------- 1 1 1 2 3 2 2 3 --------------------------- 1 1 1 3 3 2 2 2 --------------------------- 1 1 1 2 3 3 2 2 --------------------------- 1 1 1 2 2 2 3 3 --------------------------- 1 1 1 2 2 2 3 3 --------------------------- 1 1 1 2 2 3 2 3 --------------------------- 1 1 1 2 2 2 3 3 --------------------------- 1 1 1 2 3 2 2 3 --------------------------- 1 1 1 2 2 3 2 3 --------------------------- 1 1 1 2 2 3 3 2 --------------------------- 1 1 1 2 2 2 3 3 --------------------------- 1 1 1 2 2 2 3 3 --------------------------- 1 1 1 2 2 2 3 3 --------------------------- 1 1 1 2 2 2 3 3 --------------------------- }}} {{{id=283| s(ks3[mu]) /// s[3, 3, 2] + t*s[4, 2, 2] + (t^2+t)*s[4, 3, 1] + t^3*s[4, 4] + (t^3+t^2)*s[5, 2, 1] + (t^4+t^3+t^2)*s[5, 3] + t^4*s[6, 1, 1] + (t^5+t^4+t^3)*s[6, 2] + (t^6+t^5)*s[7, 1] + t^7*s[8] }}} {{{id=284| sum( [t**tab.charge()*s(tab.shape()) for tab in mu.k_atom(3)] ) - s(ks3[mu]) /// 0 }}}If we want to know how this works, we can look at the source code:
{{{id=289| mu.k_atom?? /// }}}We can take the $k$-split of a partition:
{{{id=281| Partition([4,4,3,3,3,3,3,2,2,2,2,2,1,1,1]).k_split(4) /// [[4], [4], [3, 3], [3, 3], [3, 2], [2, 2, 2], [2, 1, 1], [1]] }}} {{{id=227| T = Tableau([[1,1,1,3],[2,2,4,7],[3,6],[4],[5]]) T.pp() print T.katabolism().pp() /// 1 1 1 3 2 2 4 7 3 6 4 5 1 1 1 2 2 4 7 3 3 6 4 5 }}}Katabolism inserts all rows but the first into the first row.
Example 9: Stanley symmetric functions
{{{id=200| W = SymmetricGroup(4) sigma = W([3,4,2,1]) sigma /// (1,3,2,4) }}}Notice that we entered $\sigma$ in one-line notation, but it was displayed in cycle notation. We could also enter the permuation in cycle notation:
{{{id=78| W((1,3,2,4)) == sigma /// True }}}We can get the reduced words of an element, or construct an element from a reduced word:
{{{id=74| sigma.reduced_words() /// [[2, 3, 1, 2, 1], [2, 1, 3, 2, 1], [2, 3, 2, 1, 2], [3, 2, 3, 1, 2], [3, 2, 1, 3, 2]] }}} {{{id=76| w = W.from_reduced_word([2,3,2,1]); w /// (1,2,4) }}} {{{id=77| w.reduced_words() /// [[2, 3, 2, 1], [3, 2, 3, 1], [3, 2, 1, 3]] }}}There is no functionality in sage (yet) for finding factorizations into cyclically decreasing components. Nor is Edelman-Greene insertion in sage. So to compute the Stanley symmetric functions, we implement Edeleman-Green insertion:
{{{id=85| def EG_insert_letter(lst, x): # begin by making a copy since lists are # stored by reference newlst = deepcopy(lst) for i, r in enumerate(newlst): # If x is bigger than the last element in the row # We append x to the end of the row and stop. if x > r[-1]: r.append(x) return newlst # If x and x+1 are both in the row, we preserve all # rows weakly below the current one, and insert # x+1 into the row above if (x in r) and (x+1 in r): return newlst[:i+1] + EG_insert_letter(newlst[i+1:], x+1) # Otherwise, we find the smallest element that is bigger than # x in the current row, replace it with x, and insert it into # the row above for j,y in enumerate(r): if y > x: r[j] = x break return newlst[:i+1] + EG_insert_letter(newlst[i+1:], y) # If the original tableau was empty, we return a single row # containing x return [[x]] /// }}} {{{id=83| def EG_insert(w): P = [] Q = [] for i,x in enumerate(w): # Increase the size of P P = EG_insert_letter(P, x) # Find the row that contains a cell in P but not in Q # This could almost certainly be made faster, if necessary newrow = [c for c in Tableau(P).shape().cells() if c not in Tableau(Q).shape().cells()][0][0] # Place i+1 in Q (since i counts from 0) if newrow >= len(Q): Q.append([i+1]) else: Q[newrow].append(i+1) return Tableau(P), Tableau(Q) /// }}} {{{id=89| P,Q = EG_insert([2,1,2,3,2]) P.pp() print Q.pp() /// 1 2 3 2 3 1 3 4 2 5 }}}We can now compute the Stanley symmetric functions:
{{{id=90| def F(w): s = SymmetricFunctions(QQ).schur() P_list = [] for r in w.inverse().reduced_words(): P,Q = EG_insert(r) P_list.append(P) P_list = uniq(P_list) return s.sum_of_monomials(P.shape() for P in P_list) /// }}} {{{id=95| w = W.from_reduced_word([2,3,2,1]) F(w) /// s[3, 1] }}} {{{id=96| m(F(w)) /// 3*m[1, 1, 1, 1] + 2*m[2, 1, 1] + m[2, 2] + m[3, 1] }}}Mini example 10: The Affine symmetric group
{{{id=97| W = WeylGroup(['A',3,1]) /// }}} {{{id=209| w = W.from_reduced_word([0,1,2,0,1,2,1]) w /// [-1 1 0 1] [-1 1 -1 2] [ 0 1 -1 1] [ 0 0 0 1] }}} {{{id=210| w.reduced_words() /// [[1, 2, 0], [1, 0, 2]] }}} {{{id=212| mu = Partition([2,1,1]) mu.from_kbounded_to_reduced_word(2) /// [2, 1, 2, 0] }}} {{{id=228| from_kbounded_to_core(mu,2).pp() /// *** * * }}}