Infinity Crystals - Thematic Tutorials

Skip to content
Back to top
View this page
Edit this page

# Infinity Crystals

Infinity crystals, denoted \(\mathcal{B}(\infty)\) and depend only on the Cartan type, are the crystal analogue of Verma modules with highest weight \(0\) associated to a symmetrizable Kac-Moody algebra (equivalently, the lower half of the corresponding quantum group). As such, they are infinite-dimensional and any irreducible highest weight crystal may be obtained from an infinity crystal via some cutting procedure. On the other hand, the crystal \(B(\infty)\) is the direct limit of all irreducible highest weight crystals \(B(\lambda)\), so there are natural embeddings of each \(B(\lambda)\) in \(B(\infty)\). Below, we outline the various implementations of the crystal \(B(\infty)\) in Sage and give examples of how \(B(\lambda)\) interacts with \(B(\infty)\).

All infinity crystals that are currently implemented in Sage can be accessed by typing `crystals.infinity.<tab>`.

## Marginally large tableaux

Marginally large tableaux were introduced by J. Hong and H. Lee as a realization of the crystal \(B(\infty)\) in types \(A_n\), \(B_n\), \(C_n\), \(D_{n+1}\), and \(G_2\). The marginally large condition guarantees that all tableau have exactly \(n\) rows and that the number of \(i\)-boxes in the \(i\)-th row from the top (in the English convention) is exactly one more than the total number of boxes in the \((i+1)\)-st row. Other specific conditions on the tableaux vary by type. See [HongLee2008] for more information.

Here is an example in type \(A_2\):

Sage

    sage: B = crystals.infinity.Tableaux(['A',2])
    sage: b = B.highest_weight_vector()
    sage: b.pp()
      1  1
      2
    sage: b.f_string([1,2,2,1,2,1,2,2,2,2,2]).pp()
      1  1  1  1  1  1  1  1  1  2  2  3
      2  3  3  3  3  3  3  3

Python

    >>> from sage.all import *
    >>> B = crystals.infinity.Tableaux(['A',Integer(2)])
    >>> b = B.highest_weight_vector()
    >>> b.pp()
      1  1
      2
    >>> b.f_string([Integer(1),Integer(2),Integer(2),Integer(1),Integer(2),Integer(1),Integer(2),Integer(2),Integer(2),Integer(2),Integer(2)]).pp()
      1  1  1  1  1  1  1  1  1  2  2  3
      2  3  3  3  3  3  3  3

Since the crystal is infinite, we must specify the subcrystal we would like to view:

Sage

    sage: B = crystals.infinity.Tableaux(['A',2])
    sage: S = B.subcrystal(max_depth=4)
    sage: G = B.digraph(subset=S)
    sage: view(G, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

[Python]

    >>> from sage.all import *
    >>> B = crystals.infinity.Tableaux(['A',Integer(2)])
    >>> S = B.subcrystal(max_depth=Integer(4))
    >>> G = B.digraph(subset=S)
    >>> view(G, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

Using elementary crystals, we can cut irreducible highest weight crystals from \(B(\infty)\) as the connected component of \(C \otimes T_\lambda \otimes B(\infty)\) containing \(c\otimes t_\lambda \otimes u_\infty\), where \(u_\infty\) is the highest weight vector in \(B(\infty)\). In this example, we cut out \(B(\rho)\) from \(B(\infty)\) in type \(A_2\):

Sage

    sage: B = crystals.infinity.Tableaux(['A',2])
    sage: b = B.highest_weight_vector()
    sage: T = crystals.elementary.T(['A',2], B.Lambda()[1] + B.Lambda()[2])
    sage: t = T[0]
    sage: C = crystals.elementary.Component(['A',2])
    sage: c = C[0]
    sage: TP = crystals.TensorProduct(C,T,B)
    sage: t0 = TP(c,t,b)
    sage: STP = TP.subcrystal(generators=[t0])
    sage: GTP = TP.digraph(subset=STP)
    sage: view(GTP, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

Python

    >>> from sage.all import *
    >>> B = crystals.infinity.Tableaux(['A',Integer(2)])
    >>> b = B.highest_weight_vector()
    >>> T = crystals.elementary.T(['A',Integer(2)], B.Lambda()[Integer(1)] + B.Lambda()[Integer(2)])
    >>> t = T[Integer(0)]
    >>> C = crystals.elementary.Component(['A',Integer(2)])
    >>> c = C[Integer(0)]
    >>> TP = crystals.TensorProduct(C,T,B)
    >>> t0 = TP(c,t,b)
    >>> STP = TP.subcrystal(generators=[t0])
    >>> GTP = TP.digraph(subset=STP)
    >>> view(GTP, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

Note that the above code can be simplified using the R-crystal:

Sage

    sage: B = crystals.infinity.Tableaux(['A',2])
    sage: b = B.highest_weight_vector()
    sage: R = crystals.elementary.R(['A',2], B.Lambda()[1] + B.Lambda()[2])
    sage: r = R[0]
    sage: TP2 = crystals.TensorProduct(R,B)
    sage: t2 = TP2(r,b)
    sage: STP2 = TP2.subcrystal(generators=[t2])
    sage: GTP2 = TP2.digraph(subset=STP2)
    sage: view(GTP2, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

[Python]

    >>> from sage.all import *
    >>> B = crystals.infinity.Tableaux(['A',Integer(2)])
    >>> b = B.highest_weight_vector()
    >>> R = crystals.elementary.R(['A',Integer(2)], B.Lambda()[Integer(1)] + B.Lambda()[Integer(2)])
    >>> r = R[Integer(0)]
    >>> TP2 = crystals.TensorProduct(R,B)
    >>> t2 = TP2(r,b)
    >>> STP2 = TP2.subcrystal(generators=[t2])
    >>> GTP2 = TP2.digraph(subset=STP2)
    >>> view(GTP2, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

On the other hand, we can embed the irreducible highest weight crystal \(B(\rho)\) into \(B(\infty)\):

Sage

    sage: Brho = crystals.Tableaux(['A',2],shape=[2,1])
    sage: brho = Brho.highest_weight_vector()
    sage: B = crystals.infinity.Tableaux(['A',2])
    sage: binf = B.highest_weight_vector()
    sage: wt = brho.weight()
    sage: T = crystals.elementary.T(['A',2],wt)
    sage: TlambdaBinf = crystals.TensorProduct(T,B)
    sage: hw = TlambdaBinf(T[0],binf)
    sage: Psi = Brho.crystal_morphism({brho : hw})
    sage: BG = B.digraph(subset=[Psi(x) for x in Brho])
    sage: view(BG, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

Python

    >>> from sage.all import *
    >>> Brho = crystals.Tableaux(['A',Integer(2)],shape=[Integer(2),Integer(1)])
    >>> brho = Brho.highest_weight_vector()
    >>> B = crystals.infinity.Tableaux(['A',Integer(2)])
    >>> binf = B.highest_weight_vector()
    >>> wt = brho.weight()
    >>> T = crystals.elementary.T(['A',Integer(2)],wt)
    >>> TlambdaBinf = crystals.TensorProduct(T,B)
    >>> hw = TlambdaBinf(T[Integer(0)],binf)
    >>> Psi = Brho.crystal_morphism({brho : hw})
    >>> BG = B.digraph(subset=[Psi(x) for x in Brho])
    >>> view(BG, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

Note that in the last example, we had to inject \(B(\rho)\) into the tensor product \(T_\lambda \otimes B(\infty)\), since otherwise, the map `Psi` would not be a crystal morphism (as `b.weight() != brho.weight()`).

## Generalized Young walls

Generalized Young walls were introduced by J.-A. Kim and D.-U. Shin as a model for \(B(\infty)\) and each \(B(\lambda)\) solely in affine type \(A_n^{(1)}\). See [KimShin2010] for more information on the construction of generalized Young walls.

Since this model is only valid for one Cartan type, the input to initialize the crystal is simply the rank of the underlying type:

Sage

    sage: Y = crystals.infinity.GeneralizedYoungWalls(2)
    sage: y = Y.highest_weight_vector()
    sage: y.f_string([0,1,2,2,2,1,0,0,1,2]).pp()
             2|
              |
              |
           1|2|
           0|1|
     2|0|1|2|0|

Python

    >>> from sage.all import *
    >>> Y = crystals.infinity.GeneralizedYoungWalls(Integer(2))
    >>> y = Y.highest_weight_vector()
    >>> y.f_string([Integer(0),Integer(1),Integer(2),Integer(2),Integer(2),Integer(1),Integer(0),Integer(0),Integer(1),Integer(2)]).pp()
             2|
              |
              |
           1|2|
           0|1|
     2|0|1|2|0|

In the `weight` method for this model, we can choose whether to view weights in the extended weight lattice (by default) or in the root lattice:

Sage

    sage: Y = crystals.infinity.GeneralizedYoungWalls(2)
    sage: y = Y.highest_weight_vector()
    sage: y.f_string([0,1,2,2,2,1,0,0,1,2]).weight()
    Lambda[0] + Lambda[1] - 2*Lambda[2] - 3*delta
    sage: y.f_string([0,1,2,2,2,1,0,0,1,2]).weight(root_lattice=True)
    -3*alpha[0] - 3*alpha[1] - 4*alpha[2]

[Python]

    >>> from sage.all import *
    >>> Y = crystals.infinity.GeneralizedYoungWalls(Integer(2))
    >>> y = Y.highest_weight_vector()
    >>> y.f_string([Integer(0),Integer(1),Integer(2),Integer(2),Integer(2),Integer(1),Integer(0),Integer(0),Integer(1),Integer(2)]).weight()
    Lambda[0] + Lambda[1] - 2*Lambda[2] - 3*delta
    >>> y.f_string([Integer(0),Integer(1),Integer(2),Integer(2),Integer(2),Integer(1),Integer(0),Integer(0),Integer(1),Integer(2)]).weight(root_lattice=True)
    -3*alpha[0] - 3*alpha[1] - 4*alpha[2]

As before, we need to indicate a specific subcrystal when attempting to view the crystal graph:

Sage

    sage: Y = crystals.infinity.GeneralizedYoungWalls(2)
    sage: SY = Y.subcrystal(max_depth=3)
    sage: GY = Y.digraph(subset=SY)
    sage: view(GY, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

Python

    >>> from sage.all import *
    >>> Y = crystals.infinity.GeneralizedYoungWalls(Integer(2))
    >>> SY = Y.subcrystal(max_depth=Integer(3))
    >>> GY = Y.digraph(subset=SY)
    >>> view(GY, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

One can also make irreducible highest weight crystals using generalized Young walls:

Sage

    sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights()
    sage: YLa = crystals.GeneralizedYoungWalls(2,La[0])
    sage: SYLa = YLa.subcrystal(max_depth=3)
    sage: GYLa = YLa.digraph(subset=SYLa)
    sage: view(GYLa, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

[Python]

    >>> from sage.all import *
    >>> La = RootSystem(['A',Integer(2),Integer(1)]).weight_lattice(extended=True).fundamental_weights()
    >>> YLa = crystals.GeneralizedYoungWalls(Integer(2),La[Integer(0)])
    >>> SYLa = YLa.subcrystal(max_depth=Integer(3))
    >>> GYLa = YLa.digraph(subset=SYLa)
    >>> view(GYLa, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

In the highest weight crystals, however, weights are always elements of the extended affine weight lattice:

Sage

    sage: La = RootSystem(['A',2,1]).weight_lattice(extended=True).fundamental_weights()
    sage: YLa = crystals.GeneralizedYoungWalls(2,La[0])
    sage: YLa.highest_weight_vector().f_string([0,1,2,0]).weight()
    -Lambda[0] + Lambda[1] + Lambda[2] - 2*delta

Python

    >>> from sage.all import *
    >>> La = RootSystem(['A',Integer(2),Integer(1)]).weight_lattice(extended=True).fundamental_weights()
    >>> YLa = crystals.GeneralizedYoungWalls(Integer(2),La[Integer(0)])
    >>> YLa.highest_weight_vector().f_string([Integer(0),Integer(1),Integer(2),Integer(0)]).weight()
    -Lambda[0] + Lambda[1] + Lambda[2] - 2*delta

## Modified Nakajima monomials

Let \(Y_{i,k}\), for \(i \in I\) and \(k \in \ZZ\), be a commuting set of variables, and let \(\boldsymbol{1}\) be a new variable which commutes with each \(Y_{i,k}\). (Here, \(I\) represents the index set of a Cartan datum.) One may endow the structure of a crystal on the set \(\widehat{\mathcal{M}}\) of monomials of the form

\[M = \prod_{(i,k) \in I\times \ZZ_{\ge0}} Y_{i,k}^{y_i(k)}\boldsymbol{1}.\]

Elements of \(\widehat{\mathcal{M}}\) are called modified Nakajima monomials. We will omit the \(\boldsymbol{1}\) from the end of a monomial if there exists at least one \(y_i(k) \neq 0\). The crystal structure on this set is defined by

\[\begin{split}\begin{aligned} \mathrm{wt}(M) &= \sum_{i\in I} \Bigl( \sum_{k\ge 0} y_i(k) \Bigr) \Lambda_i, \\ \varphi_i(M) &= \max\Bigl\{ \sum_{0\le j \le k} y_i(j) : k\ge 0 \Bigr\}, \\ \varepsilon_i(M) &= \varphi_i(M) - \langle h_i, \mathrm{wt}(M) \rangle, \\ k_f = k_f(M) &= \min\Bigl\{ k\ge 0 : \varphi_i(M) = \sum_{0\le j\le k} y_i(j) \Bigr\}, \\ k_e = k_e(M) &= \max\Bigl\{ k\ge 0 : \varphi_i(M) = \sum_{0\le j\le k} y_i(j) \Bigr\}, \end{aligned}\end{split}\]

where \(\{h_i : i \in I\}\) and \(\{\Lambda_i : i \in I \}\) are the simple coroots and fundamental weights, respectively. With a chosen set of integers \(C = (c_{ij})_{i\neq j}\) such that \(c_{ij}+c_{ji} =1\), one defines

\[A_{i,k} = Y_{i,k} Y_{i,k+1} \prod_{j\neq i} Y_{j,k+c_{ji}}^{a_{ji}},\]

where \((a_{ij})\) is a Cartan matrix. Then

\[\begin{split}\begin{aligned} e_iM &= \begin{cases} 0 & \text{if } \varepsilon_i(M) = 0, \\ A_{i,k_e}M & \text{if } \varepsilon_i(M) > 0, \end{cases} \\ f_iM &= A_{i,k_f}^{-1} M. \end{aligned}\end{split}\]

Note

Monomial crystals depend on the choice of positive integers \(C = (c_{ij})_{i\neq j}\) satisfying the condition \(c_{ij}+c_{ji}=1\). This choice has been made in Sage such that \(c_{ij} = 1\) if \(i < j\) and \(c_{ij} = 0\) if \(i>j\), but other choices may be used if deliberately stated at the initialization of the crystal:

Sage

    sage: c = Matrix([[0,0,1],[1,0,0],[0,1,0]])
    sage: La = RootSystem(['C',3]).weight_lattice().fundamental_weights()
    sage: M = crystals.NakajimaMonomials(2*La[1], c=c)
    sage: M.c()
    [0 0 1]
    [1 0 0]
    [0 1 0]

Python

    >>> from sage.all import *
    >>> c = Matrix([[Integer(0),Integer(0),Integer(1)],[Integer(1),Integer(0),Integer(0)],[Integer(0),Integer(1),Integer(0)]])
    >>> La = RootSystem(['C',Integer(3)]).weight_lattice().fundamental_weights()
    >>> M = crystals.NakajimaMonomials(Integer(2)*La[Integer(1)], c=c)
    >>> M.c()
    [0 0 1]
    [1 0 0]
    [0 1 0]

It is shown in [KKS2007] that the connected component of \(\widehat{\mathcal{M}}\) containing the element \(\boldsymbol{1}\), which we denote by \(\mathcal{M}(\infty)\), is crystal isomorphic to the crystal \(B(\infty)\):

Sage

    sage: Minf = crystals.infinity.NakajimaMonomials(['C',3,1])
    sage: minf = Minf.highest_weight_vector()
    sage: m = minf.f_string([0,1,2,3,2,1,0]); m
    Y(0,0)^-1 Y(0,4)^-1 Y(1,0) Y(1,3)
    sage: m.weight()
    -2*Lambda[0] + 2*Lambda[1] - 2*delta
    sage: m.weight_in_root_lattice()
    -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3]

Python

    >>> from sage.all import *
    >>> Minf = crystals.infinity.NakajimaMonomials(['C',Integer(3),Integer(1)])
    >>> minf = Minf.highest_weight_vector()
    >>> m = minf.f_string([Integer(0),Integer(1),Integer(2),Integer(3),Integer(2),Integer(1),Integer(0)]); m
    Y(0,0)^-1 Y(0,4)^-1 Y(1,0) Y(1,3)
    >>> m.weight()
    -2*Lambda[0] + 2*Lambda[1] - 2*delta
    >>> m.weight_in_root_lattice()
    -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3]

We can also model \(B(\infty)\) using the variables \(A_{i,k}\) instead:

Sage

    sage: Minf = crystals.infinity.NakajimaMonomials(['C',3,1])
    sage: minf = Minf.highest_weight_vector()
    sage: Minf.set_variables('A')
    sage: m = minf.f_string([0,1,2,3,2,1,0]); m
    A(0,0)^-1 A(0,3)^-1 A(1,0)^-1 A(1,2)^-1 A(2,0)^-1 A(2,1)^-1 A(3,0)^-1
    sage: m.weight()
    -2*Lambda[0] + 2*Lambda[1] - 2*delta
    sage: m.weight_in_root_lattice()
    -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3]
    sage: Minf.set_variables('Y')

[Python]

    >>> from sage.all import *
    >>> Minf = crystals.infinity.NakajimaMonomials(['C',Integer(3),Integer(1)])
    >>> minf = Minf.highest_weight_vector()
    >>> Minf.set_variables('A')
    >>> m = minf.f_string([Integer(0),Integer(1),Integer(2),Integer(3),Integer(2),Integer(1),Integer(0)]); m
    A(0,0)^-1 A(0,3)^-1 A(1,0)^-1 A(1,2)^-1 A(2,0)^-1 A(2,1)^-1 A(3,0)^-1
    >>> m.weight()
    -2*Lambda[0] + 2*Lambda[1] - 2*delta
    >>> m.weight_in_root_lattice()
    -2*alpha[0] - 2*alpha[1] - 2*alpha[2] - alpha[3]
    >>> Minf.set_variables('Y')

Building the crystal graph output for these monomial crystals is the same as the constructions above:

Sage

    sage: Minf = crystals.infinity.NakajimaMonomials(['C',3,1])
    sage: Sinf = Minf.subcrystal(max_depth=2)
    sage: Ginf = Minf.digraph(subset=Sinf)
    sage: view(Ginf, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

Python

    >>> from sage.all import *
    >>> Minf = crystals.infinity.NakajimaMonomials(['C',Integer(3),Integer(1)])
    >>> Sinf = Minf.subcrystal(max_depth=Integer(2))
    >>> Ginf = Minf.digraph(subset=Sinf)
    >>> view(Ginf, tightpage=True) # optional - dot2tex graphviz, not tested (opens external window)

Note that this model will also work for any symmetrizable Cartan matrix:

Sage

    sage: A = CartanMatrix([[2,-4],[-5,2]])
    sage: Linf = crystals.infinity.NakajimaMonomials(A); Linf
    Infinity Crystal of modified Nakajima monomials of type [ 2 -4]
    [-5  2]
    sage: Linf.highest_weight_vector().f_string([0,1,1,1,0,0,1,1,0])
    Y(0,0)^-1 Y(0,1)^9 Y(0,2)^5 Y(0,3)^-1 Y(1,0)^2 Y(1,1)^5 Y(1,2)^3

[Python]

    >>> from sage.all import *
    >>> A = CartanMatrix([[Integer(2),-Integer(4)],[-Integer(5),Integer(2)]])
    >>> Linf = crystals.infinity.NakajimaMonomials(A); Linf
    Infinity Crystal of modified Nakajima monomials of type [ 2 -4]
    [-5  2]
    >>> Linf.highest_weight_vector().f_string([Integer(0),Integer(1),Integer(1),Integer(1),Integer(0),Integer(0),Integer(1),Integer(1),Integer(0)])
    Y(0,0)^-1 Y(0,1)^9 Y(0,2)^5 Y(0,3)^-1 Y(1,0)^2 Y(1,1)^5 Y(1,2)^3

## Rigged configurations

Rigged configurations are sequences of partitions, one partition for each node in the underlying Dynkin diagram, such that each part of each partition has a label (or rigging). A crystal structure was defined on these objects in [Schilling2006], then later extended to work as a model for \(B(\infty)\). See [SalisburyScrimshaw2015] for more information:

Sage

    sage: RiggedConfigurations.options(display="horizontal")
    sage: RC = crystals.infinity.RiggedConfigurations(['C',3,1])
    sage: nu = RC.highest_weight_vector().f_string([0,1,2,3,2,1,0]); nu
    -2[ ]-1   2[ ]1   0[ ]0   0[ ]0
    -2[ ]-1   2[ ]1   0[ ]0
    sage: nu.weight()
    -2*Lambda[0] + 2*Lambda[1] - 2*delta
    sage: RiggedConfigurations.options._reset()

Python

    >>> from sage.all import *
    >>> RiggedConfigurations.options(display="horizontal")
    >>> RC = crystals.infinity.RiggedConfigurations(['C',Integer(3),Integer(1)])
    >>> nu = RC.highest_weight_vector().f_string([Integer(0),Integer(1),Integer(2),Integer(3),Integer(2),Integer(1),Integer(0)]); nu
    -2[ ]-1   2[ ]1   0[ ]0   0[ ]0
    -2[ ]-1   2[ ]1   0[ ]0
    >>> nu.weight()
    -2*Lambda[0] + 2*Lambda[1] - 2*delta
    >>> RiggedConfigurations.options._reset()

We can check this crystal is isomorphic to the crystal above using Nakajima monomials:

Sage

    sage: Minf = crystals.infinity.NakajimaMonomials(['C',3,1])
    sage: Sinf = Minf.subcrystal(max_depth=2)
    sage: Ginf = Minf.digraph(subset=Sinf)
    sage: RC = crystals.infinity.RiggedConfigurations(['C',3,1])
    sage: RCS = RC.subcrystal(max_depth=2)
    sage: RCG = RC.digraph(subset=RCS)
    sage: RCG.is_isomorphic(Ginf, edge_labels=True)
    True

[Python]

    >>> from sage.all import *
    >>> Minf = crystals.infinity.NakajimaMonomials(['C',Integer(3),Integer(1)])
    >>> Sinf = Minf.subcrystal(max_depth=Integer(2))
    >>> Ginf = Minf.digraph(subset=Sinf)
    >>> RC = crystals.infinity.RiggedConfigurations(['C',Integer(3),Integer(1)])
    >>> RCS = RC.subcrystal(max_depth=Integer(2))
    >>> RCG = RC.digraph(subset=RCS)
    >>> RCG.is_isomorphic(Ginf, edge_labels=True)
    True

This model works in Sage for all finite and affine types, as well as any simply laced Cartan matrix.