\[ \begin{align}\begin{aligned} \def\CC{\bf C} \def\QQ{\bf Q} \def\RR{\bf R} \def\ZZ{\bf Z} \def\NN{\bf N}\\# Partitions and Young tableaux tutorial\end{aligned}\end{align} \]

Mélodie Lapointe (lapointe.melodie@courrier.uqam.ca) and Pauline Hubert (hubert.pauline@courrier.uqam.ca)

Partitions

Recall that a partition \(\mu\) of \(n\), one writes \(\mu\vdash n\) or \(n=|\mu|\), is a sequence of integers \((\mu_0,\mu_1,\ldots, \mu_{k-1})\) (the \(m_i\)’s are the parts of \(\mu\)) with \(\mu_0\geq \mu_1\geq\,\cdots\,\geq\mu_{k-1}>0\), and \(n=\mu_0+\mu_1+\ldots+\mu_{k-1}\). The number \(\ell(\mu):=k\) of parts of \(\mu\) is said to be its length. A partition \(\mu\) may also be described as a Ferrers diagram, which is the \(n\)-subset of \(\mathbb{N}\times \mathbb{N}\):

\[\{(a,b)\ |\ 0\leq a<\mu_i\quad{\rm and}\quad b<\ell(\mu)\}.\]

This set is also denoted \(\mu\), and its elements are the cells of \(\mu\). The conjuguate of \(\mu\), is the partition \(\mu'\) such that

\[\mu'=\{(b,a)\ |\ (a,b)\in\mu\}.\]

Parts of \(\mu\) are the lengths of the rows of its diagram, and parts of \(\mu'\) are the lengths of its columns.

For more, see https://en.wikipedia.org/wiki/Partition_(number_theory)

We mostly follow the notation conventions of Macdonald’s book: Symmetric Functions and Hall Polynomials, Second Edition, Oxford Mathematical Monographs, 1998.

Here are a few preliminary declarations just to make outputs nicer, and diagrams to printout with the French convention. Partitions parts are glued together, with parts of size larger that \(9\) ending with a “dot” so that there is no confusion.

[93]:
%display latex
def mystr(i):
    if i<10:
        return str(i)
    else:
        return ''.join([str(i),"."])
def compact(mu):
    return (''.join(mystr(i) for i in mu))

Partition._latex_= compact

Handling partitions in SAGE

Partition can be created/declared in SAGE the following way:

[3]:
mu=Partition([10,10,2,2,1]); mu
[3]:

Nice format versus normal one

with \(\mu\) here printed out in a nicer format than the usual:

[4]:
print(Partition([10,10,2,2,1]))
[10, 10, 2, 2, 1]

Listing partitions of \(n\)

One can also list all partitions of a given integer.

[5]:
Partitions(4).list()
[5]:

Number of partitions

Or simply calculate the number of partitions of \(n\).

(We underline that this function does not actually generate the partitions of \(n\) in order to count them; hence it is amazingly fast.)

[6]:
Partitions(3000).cardinality()
[6]:
[7]:
print(Partitions(100000).cardinality())
27493510569775696512677516320986352688173429315980054758203125984302147328114964173055050741660736621590157844774296248940493063070200461792764493033510116079342457190155718943509725312466108452006369558934464248716828789832182345009262853831404597021307130674510624419227311238999702284408609370935531629697851569569892196108480158600569421098519

Partitions with constraints

One may add constraints on partitions; for instance, to get partitions of 5 of length 2:

[8]:
p = Partitions(5,length=2)
p.list()
[8]:

or get all partitions of 6 with length between 3 and 5.

[14]:
p = Partitions(6,min_length=3,max_length=5)
p.list()
[14]:

Ferrers diagram

By default SAGE uses the English convention, but it has become the tradition in recent years to use the more natural (cartesian coordinates) French notation. Here is how to set this

[33]:
Partitions.options.convention="french"
[32]:
mu = Partition([8,5,5,5,4,3,3,2])
print(mu.ferrers_diagram())
********
*****
*****
*****
****
***
***
**

Cells

The list of cells of \(m\) my be obtained as follows

[34]:
mu.cells()
[34]:

and printed out in nice format as

[36]:
map(compact,mu.cells())
[36]:

If one insists on using the English convention, rather than akwardly reading this notebook “… upside down, in a mirror …” (as Macdonald would say), one could globally switch back as follows

[31]:
Partitions.options(convention='english')
print(mu.ferrers_diagram())
********
*****
*****
*****
****
***
***
**

Partition containment

A partition \(\mu\) is said to be included in a partition \(\lambda\) if \(\mu_i \leq \lambda_i\), for all \(i\). In other words, the diagram of \(\mu\) is a subset of the diagram of \(\lambda\). For example, one can list all partitions \(\lambda\) of \(5\) such that the partition \([2,1]\) is included in \(\lambda\).

[26]:
p = Partitions(5,inner= [2,1])
p.list()
[26]:

Or all partitions of 5 included in the partition \([4,3,2,1]\).

[27]:
p = Partitions(5,outer=[4,3,2,1])
p.list()
[27]:

The default (total) order on partitions is the lexicographic order.

[29]:
mu = Partition([4,3,3])
nu = Partition([4,4,1])
mu < nu
[29]:

Exercise:

*Let \(\lambda\) be the partition \([15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]\). Compute *
\[\sum\limits_{i=0}^{20} \sum\limits_{\mu \vdash i,\ \mu \subseteq \lambda} q^i.\]
[83]:
q=var('q')

mu=[15,14,13,12,11,10,9,8,7,6,5,4,3,2,1]

sum(Partitions(i,outer=mu).cardinality()*q^i for i in range(20))
[83]:

Young Tableaux

An \(A\)-valued Young tableaux of shape \(\mu\) is a “filling” the cells of the Ferrers diagram of \(\mu\) with elements of an ordered set \(A\). Hence it is a function \(\tau:\mu\rightarrow A\). A tableau is said to standard if it is bijective (hence \(A\) has cardinality equal to the number of cells of \(\mu\)), and its entries on each row (and each column) are stricly increasing from left to right (from bottom to top in french convention). A tableau (not necessarily bijective) is said to be semistandard if its entries are weakly increasing from left to right on each row, and strictly increasing on each column. These object may be constructed in the following way.

[38]:
t = SemistandardTableau([[1,2,4],[3,3,6],[5,7],[8]])
t.pp()
print('')
s = StandardTableau([[1,2,4],[3,6],[5,7],[8]])
s.pp()
  8
  5  7
  3  3  6
  1  2  4

  8
  5  7
  3  6
  1  2  4

The function pp(\(\ \)) (“pp” stands for pretty print) gives a nicer display for Young tableaux. Observe that if you set options (like French vs English convention) for partitions, these will also apply to Young tableaux.

It is also possible to list all semistandard and standard Young tableaux of a given partition.

[39]:
x = SemistandardTableaux([4,3,3,2,1])
print(x.cardinality())
y = StandardTableaux([4,3,3,2,1])
print(y.cardinality())
390780390
15015

The functions for partitions, such as display, options, cardinality, and list, may also be used on Young tableaux.

Exercise:

Verify that the number of standard Young tableaux of shape :math:`[n,n]` is equal to the Catalan number for :math:`0 leq n leq 20`. (The function catalan_number(:math:`n`) returns the nth catalan number).
[40]:
all(catalan_number(i)==StandardTableaux([i,i]).cardinality() for i in range(1,10))
[40]:

Exercise:

Compute the sum of all monomials of degree 5 in three variables using partitions and standard tableaux.
[41]:
var('x y z')
young_tableaux = []
monomials = []
for i in Partitions(5).list():
    young_tableaux.extend(SemistandardTableaux(i,max_entry=3).list())
for j in young_tableaux:
    k = reduce(operator.add,j)
    monomials.append(x^k.count(1)*y^k.count(2)*z^k.count(3))
show(sum(monomials))

Hook formula for the number of standard tableaux of shape \(\mu\)

The classical hook formula

\[ \begin{align}\begin{aligned}f^\mu:=\frac{n!}{\prod_{c\in\mu} h(c,\mu)},\\with :math:`h((i,j),\mu):= \mu_i+\mu'_j-i-j-1`, may be encoded as\end{aligned}\end{align} \]
[47]:
def hook_formula(mu):
    mu=Partition(mu)
    return factorial(add(k for k in mu))/prod(mu.hook_length(i,j) for i,j in mu.cells())
[48]:
hook_formula(Partition([4,3,1,1]))
[48]:

Here are some functions on partitions often used in the theory of Macdonald polynomials

(Sometimes one may want a more functional notation for combinatorial calculations on diagrams.)

\begin{eqnarray} && n(\mu):=\sum_{(i,j)\in\mu} i, \qquad T_\mu:=\prod_{(i,j)\in\mu} t^iq^j,\\ && B_\mu:=\sum_{(i,j)\in\mu} t^iq^j, \qquad \Pi_\mu:=\prod_{(i,j)\in\mu,\ (i,j)\not=(0,0)} (1-t^iq^j),\\ &&\varepsilon_\mu:=\prod_{c\in\mu}(q^{a(c)}-t^{l(c)+1})(t^{l(c)}-q^{a(c)+1}), \end{eqnarray}

where \(a(c)\) et \(l(c)\) respectively denote the arm and the leg of a cell \(c\) in \(\mu\). We have

\[(n(\mu),n(\mu'))=\sum_{c \in \mu} c.\]
[114]:
q,t=var('q,t')

def ell(mu):
    mu=Partition(mu)
    return mu.length()

def arm(c,mu):
    mu=Partition(mu)
    return mu.arm_length(c[0],c[1])

def leg(c,mu):
    mu=Partition(mu)
    return mu.leg_length(c[0],c[1])

def corner_cells(mu):
    mu=Partition(mu)
    return mu.corners()

def zee(mu):
    mu=Partition(mu)
    return mu.aut()

def n(mu): return add(i for i,j in mu.cells())

def B(mu):
    return add(t^i*q^j for i,j in mu.cells())

def T(mu):
    return prod(t^i*q^j for i,j in mu.cells())

def Pi(mu):
    return prod(1-t^i*q^j for i,j in mu.cells() if [i,j]<>[0,0])

def epsilon(mu):
    return prod((q^(arm(c,mu))-t^(1+leg(c,mu)))*(t^(leg(c,mu))-q^(1+arm(c,mu))) for c in mu.cells())
[115]:
mu=Partition([4,3,3,1,1])
[116]:
vector([n(mu),n(mu.conjugate())])== add(vector(c) for c in mu.cells())
[116]:
[117]:
B(mu)
[117]:
[118]:
Pi(mu)
[118]:
[119]:
k=var('k')

factor(add(k^ell(mu)/zee(mu) for mu in Partitions(6)))
[119]:
[120]:
factor(add((-k)^ell(mu)/zee(mu) for mu in Partitions(6)))
[120]:
[ ]: