6. Invariants of paths

We saw in section 4 how to compute the Thrall coefficients, and in section 5 how to compute higher Lie idempotents.

[1]:
s=SymmetricFunctions(QQ).schur()
def thrall_coefficient(la,mu):
    return s.gessel_reutenauer(la).coefficient(mu)
def higher_lie_idempotent(la):
    return DescentAlgebra(QQ, la.size()).I().idempotent(la).to_symmetric_group_algebra()

The function \(\texttt{invariant_multiplicity}\) computes, for a given \(d\) and \(\lambda \vdash \ell d\), the dimension of the space of invariants inside \(W_\lambda(\mathbb{C}^d)\). This is just the Thrall coefficient \(a^{\lambda}_{(\ell^d)}\).

[2]:
def invariant_multiplicity(la,d):
    k=sum(la)
    if k%d!=0:
        return False
    else:
        l=k//d
        invPartition=Partition([l for _ in range(d)])
        return thrall_coefficient(la,invPartition)

The function \(\texttt{invariant_graded_parts}\) prints the dimensions of the components \(U_{\lambda}(\mathbb{C}^d)\) of the invariant ring \(\mathcal{I}_{\leq m}(\mathbb{C}^d)\). Here \(\lambda\) runs over all parititions with \(\lambda_1 \leq m\) and \(|\lambda| \leq \texttt{max_degree}\).

[3]:
def invariant_graded_parts(d,max_degree=9,m=False):
    l=1
    while l*d<=max_degree:
        print("==============")
        if order:
            partitions=Partitions(l*d,max_part=m)
        else:
            partitions=Partitions(l*d)
        for la in partitions:
            mult=invariant_multiplicity(la,d)
            if mult>0:
                print(str(la)+": "+str(mult))
        l+=1

The ring \(\mathcal{I}_{\leq 2}(\mathbb{C}^3)\), see Proposition 6.5:

[4]:
invariant_graded_parts(3,m=2)
==============
[2, 1]: 1
==============
[2, 2, 1, 1]: 1
==============
[2, 2, 2, 1, 1, 1]: 1

The ring \(\mathcal{I}_{\leq 3}(\mathbb{C}^2)\), see Proposition 6.6:

[5]:
invariant_graded_parts(2,m=3)
==============
[2]: 1
==============
[3, 1]: 1
[2, 2]: 1
==============
[3, 2, 1]: 1
[2, 2, 2]: 1
==============
[3, 3, 1, 1]: 1
[3, 2, 2, 1]: 1
[2, 2, 2, 2]: 1

A basis for the space of invariants

We need two auxiliary functions:

\(\texttt{isotypic_basis}\) computes a basis of the isotypic component \(\mathbb{S}^{\lambda}(\mathbb{C}^d)^{\oplus {m_{\lambda}}} \subset (\mathbb{C}^d)^{\otimes k}\) in the Schur-Weyl decomposition.

[6]:
#la is a partition of size k
#The output is a list of elements of SchurTensorModule(QQ, d, k)
from sage.combinat.symmetric_group_algebra import e_hat
def isotypic_basis(la,d):
    b=[]
    k=la.size()
    A=SymmetricGroupAlgebra(QQ,k)
    symmetrizer=A.central_orthogonal_idempotent(la)
    T=SchurTensorModule(QQ, d, k)
    B=T.basis()
    SST=SemistandardTableaux(la,max_entry=d)
    ST=StandardTableaux(la)
    est=e_hat(list(ST)[-1])
    for st in ST:
        p = Permutation(flatten(st))
        E=A(p.inverse())*est
        for sst in SST:
            b.append(B[tuple(flatten(sst))]*E)
    return b

\(\texttt{module_projection}\) projects a submodule of \((\mathbb{C}^d)^{\otimes k}\) along an idempotent:

[7]:
# b is a basis of the submodule
# E is the idempotent
# Optional: dim_bound specifies an upper bound on the dimension of the projection. This will speed up the computation.
def module_projection(d,k,b,E,dim_bound=oo):
    if dim_bound==0:
        return False
    W=False
    for v in b:
        vE=v*(E.antipode())
        if vE!=0:
            V=span([vector(vE.dense_coefficient_list())])
            if not W:
                W=V
            else:
                W+=V
            if W.dimension()==dim_bound:
                break
    L=list(IntegerVectors(k=k,min_part=1,max_part=d))
    L.sort()
    T=FreeModule(QQ,L)
    B=list(T.basis())
    return [sum([v[i]*B[i] for i in range(d^k)]) for v in W.basis()]

The function \(\texttt{path_invariants}\) computes for each partition \(\lambda\) of \(d\ell\) the space of invariants in \(W_{\lambda}(\mathbb{C}^d)\).

[8]:
#The optional argument m restricts to partitions whose entries are at most m.
#Output: a dict, listing for each partition $\lambda$ of $dl$ the space of invariants in $W_{\lambda}(\CC^d)$
def path_invariants(d,l,m=False):
    D=dict()
    k=d*l
    invPartition=Partition([l for _ in range(d)])
    b=isotypic_basis(invPartition,d)
    for la in Partitions(k):
        if m and la[0] > m:
            continue
        invMult=s.gessel_reutenauer(la).coefficient(invPartition)
        if invMult==0:
            continue
        E=higher_lie_idempotent(la)
        D[la]=module_projection(d,k,b,E,dim_bound=invMult)
    return D

Here are the invariants from Example 6.7:

[9]:
path_invariants(2,2)
[9]:
{[3, 1]: [B[[1, 1, 2, 2]] - 1/2*B[[1, 2, 1, 2]] - 1/2*B[[1, 2, 2, 1]] - 1/2*B[[2, 1, 1, 2]] - 1/2*B[[2, 1, 2, 1]] + B[[2, 2, 1, 1]]],
 [2, 2]: [B[[1, 1, 2, 2]] - B[[1, 2, 2, 1]] - B[[2, 1, 1, 2]] + B[[2, 2, 1, 1]]]}