The purpose of this tutorial is to explain how to use the basic commands and data structures of the package WeylGroups. You need a working installation of Macaulay 2 (see here). You also need to install the package WeylGroups and the package Graphics that it uses.
Some familiarity with the Macaulay 2 language is probably useful, and as far as mathematics are concerned, if you don't know what a root system or a Weyl group is, you are probably on the wrong webpage. Anyway, general information about root systems and Weyl groups can be found in Bourbaki, Lie groups and Lie Algebras, chapter VI (especially in the tables at the end) and information about the Bruhat order can be found in the book of Björner and Brenti called Combinatorics of the Bruhat order.
As usual in M2, help about a particular command or class (say RootSystem) is available by typing
help "RootSystem"
or even
viewHelp "RootSystem"
to open the help file in a browser (assuming M2 has been properly configured and knows your browser).
Let us first define a simple root system by giving its type.
i2 : R=rootSystemA(3)
The system outputs
o2 = RootSystem{...8...} o2 : RootSystem
because a root system is implemented as a kind of hash table in which some information is stored when it has been computed. At first, only the Cartan matrix and its inverse are computed. It is possible to see how it looks like internally p>
i3 : peek R o3 = RootSystem{CartanMatrixTr => | 2 -1 0 | } | -1 2 -1 | | 0 -1 2 | CartanMatrixTrInv => | 3/4 1/2 1/4 | | 1/2 1 1/2 | | 1/4 1/2 3/4 | PositiveRoots => set {} ReducedDecompositions => MutableHashTable{} Reflections => HashTable{} RootNorms => {1, 1, 1} RootSystemRank => 3 WeylGroupList => {}
but it should not really matter to the normal user.
We can take the direct sum of root systems.
i4 : S=rootSystemA(3) ++ rootSystemF4
It is possible to obtain basic information about any root system. First of all, its Dynkin type (as a list of simple types, stored as a class called DynkinType
):
i5 : dynkinType S o5 = DynkinType{{A, 3}, {F, 4}} o5 : DynkinType
Note that dynkinType
is not fooled by low rank isomorphisms.
i6 : dynkinType(rootSystemD(3)) o6 = DynkinType{{A,3}} o6 : DynkinType
With the same command dynkinType
, one can construct a DynkinType
by hand, giving the simple types in a list. Note that there is no capital letter in the command dynkinType
as opposed to the class DynkinType
. In general, for every class in the package, there is a constructing command with the same name but with no capital letter (sorry Mr Dynkin).
i7 : D = dynkinType({{"A",2},{"B",3}}) o7 = DynkinType{{A, 2}, {B, 3}} o7 : DynkinType
Starting with a DynkinType
, you can get a root system of that type.
i8 : rootSystem D o8 = RootSystem{...8...} o8 : RootSystem
You can extract the Cartan matrix of the root system and its rank.
i9 : cartanMatrix(S) o9 = | 2 -1 0 0 0 0 0 | | -1 2 -1 0 0 0 0 | | 0 -1 2 0 0 0 0 | | 0 0 0 2 -1 0 0 | | 0 0 0 -1 2 -2 0 | | 0 0 0 0 -1 2 -1 | | 0 0 0 0 0 -1 2 | 7 7 o9 : Matrix ZZ <---ZZ i10 : rank S o10 = 7
Since a root system is mainly a set of roots, we should play a bit with them, and also with weights.
The n-th simple root (say n=3) is given by
i11 : r3=simpleRoot(R,3) o11 = | 0 | | -1 | | 2 | o11 : Root
The class Root
is a subclass of Weight
and both are implemented as vectors with coordinates on the set of fundamental weights. Note that the simple roots or the fundamental weights are ordered as they are in the Cartan matrix of the root system. There is no canonical convention. This package tends to give them in the same order as in the tables of Bourbaki.
The norm of a root is given by
i12 : norm(R,r3) o12 = 1
You can reflect the root r3 using the 2nd simple reflection to get another root
i13 : reflect(R,2,r3) o13 = | -1 | | 1 | | 1 | o13 : Root
and you can even apply several simple reflections at a time by giving them in a list.
i14 : myroot=reflect(R,{2,1,3},r3) o14 = | 1 | | -1 | | -1 | o14 : Root
Note that they are applied from the right as in the usual notation for composition i.e. the 3rd simple reflection is applied first in this example.
Finally, one can compute the reflection corresponding to a root, as an element of the Weyl group of the root system, and then apply it to any root to get another root.
i15 : myreflection=reflection(R,myroot) o15 = WeylGroupElement{RootSystem{...8...}, | 3 |} | -1 | | -1 | o15 : WeylGroupElement i16 : myreflection*r3 o16 = | 1 | | -2 | | 1 | o16 : Root
We will see more about Weyl group elements and their implementation in the next section so don't bother about the way o15
looks.
The coefficients of a root on the simple roots is given by
i17 : rootCoefficients(R,myroot) o17 = | 0 | | -1 | | -1 | 3 o17 : ZZ
One can also construct a weight by hand.
i18 : l=weight(R,{1,2,1}) o18 = | 1 | | 2 | | 1 | o18 : Weight
and check if it is a root.
i19 : isRoot(R,l) o19 = false
Note that to do this checking, all positive roots are computed and stored in the root system (try peek
again), so the first check can be longer than subsequent ones.
Weights can be summed, subtracted or multiplied by integers like vectors.
i20 : (l+l, 2*l, l-l) o20 = (| 2 |, | 2 |, 0) | 4 | | 4 | | 2 | | 4 | o20 : Sequence
Given a root r and a weight l, one can get the integer <l,rˇ> i.e. evaluate the dual of r at l.
i21 : eval(R,l,r3) o21 = 1 i22 : eval(R,r3,r3) o22 = 2
The Weyl group of a root system is not implemented as a structure that is computed once and for all.
Instead, there is a class called WeylGroupElement
representing an element of the Weyl group of a root system.
Internally, it contains a reference to the root system and a weight.
This weight is w(ρ) where ρ is the sum of the fundamental weights and w is the Weyl group element represented. Since the cone on the fundamental weights is a fundamental domain for the action of the Weyl group on the vector space of the root system and ρ is in the interior of this cone, we have w(ρ)=w'(ρ) if and only if w=w'. So we use w(ρ) to describe w internally.
There are two particular elements that are often useful: the neutral element of the Weyl group and the longest element (with respect to the chosen set of simple reflections).
i23 : wn = neutralWeylGroupElement R o23 = WeylGroupElement{RootSystem{...8...}, | 1 |} | 1 | | 1 | o23 : WeylGroupElement i24 : w0 = longestWeylGroupElement R o24 = WeylGroupElement{RootSystem{...8...}, | -1 |} | -1 | | -1 | o24 : WeylGroupElement
As you can see, the weight w(ρ) when w is the neutral element is just ρ itself whereas when w is the longest element w0 the weight is -ρ.
Other Weyl group elements can be defined by using reflection
to obtain the reflection corresponding to a root as above, or by giving them as products of simple reflections as follows.
i25 : w1=reduce(R,{1,2,3,2,1,3}) o25 = WeylGroupElement{RootSystem{...8...}, | -1 |} | 2 | | -3 | o25 : WeylGroupElement
Conversely, given the element w1, you can get its length and a reduced decomposition of it.
i26 : coxeterLength(w1) o26 = 4 i27 : reducedDecomposition(w1) o27 = {1,3,2,1} o27 : List
Note that the reduced decomposition returned is the smallest one for the lexicographic order, so it also identifies uniquely the Weyl group element.
Given a decomposition in simple roots (as a list of numbers), you can check if it is reduced.
i28 : isReduced(R,{3,2,1}) o28 = true
Naturally, you can multiply elements of a Weyl group.
i29 : w1=reduce(R,{1,2,3}); w2=reduce(R,{2,3,2}); i31 : w1*w2 o31 = WeylGroupElement{RootSystem{...8...}, | -1 |} | 3 | | -1 | o31 : WeylGroupElement i32 : reducedDecomposition(w1*w2) o32 = {1, 3} o32 : List
The package provides a way to deal with left and right cosets of a Weyl group, when these cosets are defined using standard parabolic subgroups, i.e. subgroups generated by a subset of the simple reflections. Such a subgroup is implemented as an object of the class Parabolic
.
i33 : R=rootSystemF4; P=parabolic(R,set{1,2}) o34 = set {1, 2} o34 : Parabolic
Let WP denote the subgroup of the Weyl group W generated by the chosen reflections (here 1 and 2). In fact, it is naturally the Weyl group of a root system (corresponding to a Levy subgroup of the associated parabolic subgroup in the algebraic group giving rise to the original root system). You can obtain this root system.
i35 : rootSystem(R,P) o35 = RootSystem{...8...} o35 : RootSystem
Left cosets, i.e. elements of the form w+WP are represented as objects of the class WeylGroupLeftCoset
.
i36 : lc= reduce(R,{1,3,4})%P o36 = WeylGroupLeftCoset{set {1, 2}, WeylGroupElement{RootSystem{...8...}, | 1 |}} | 3 | | -2 | | 1 | o36 : WeylGroupLeftCoset
In fact, any such coset has a representative in W that is minimal for the Bruhat order, and internally, we store this minimal element. You can see it by asking:
i37 : minimalRepresentative(lc) o37 = WeylGroupElement{RootSystem{...8...}, | 1 |} | 3 | | -2 | | 1 | o37 : WeylGroupElement i38 : reducedDecomposition(minimalRepresentative(lc)) o38 = {3, 4} o38 : List
The Weyl group acts on the left on left cosets.
i39 : w0=longestWeylGroupElement R; i40 : w0*lc o40 = WeylGroupLeftCoset{set {1, 2}, WeylGroupElement{RootSystem{...8...}, | 1 |}} | -5 | | 6 | | -5 | o40 : WeylGroupLeftCoset
The same kind of objects and functions exist for right cosets and double cosets as in
i41 : P%reduce(R,{1,3,4}) o41 = WeylGroupRightCoset{set {1, 2}, WeylGroupElement{RootSystem{...8...}, | 1 |}} | 3 | | -2 | | 1 | o41 : WeylGroupRightCoset i42 : P % reduce(R,{1,3,4}) % P o42 = WeylGroupDoubleCoset{set {1, 2}, set {1, 2}, WeylGroupElement{RootSystem{...8...}, | 1 |}} | 3 | | -2 | | 1 | o42 : WeylGroupDoubleCoset
and you can ask for minimal representatives, but note that there is no action remaining on double cosets.
There are several functions in the package to deal with the (strong) Bruhat order on a Weyl group or on a quotient of it by a parabolic subgroup. Some pictures of Hasse diagrams can be produced and this is why the package "WeylGroups" needs to load the package "Graphics". To display these pictures (in SVG format), you will need an SVG compatible browser (Firefox, for example) or any other application that can display SVG pictures.
Given an element w of a WeylGroup, you can compute the elements that are just below (resp. above) it in the Bruhat order, i.e. there is no element in between, i.e. the ones that have length one less (resp. one more).
i43 : R=rootSystemF4; w=reduce(R,{1,2,3,4,1,2}); L=underBruhat(w) o45 = {{WeylGroupElement{RootSystem{...8...}, | -2 |}, | 0 |}, | -2 | | 0 | | 5 | | -1 | | 4 | | 2 | ---------------------------------------------------------- {WeylGroupElement{RootSystem{...8...}, | -3 |}, | -1 |}, | -1 | | 2 | | 6 | | -2 | | 1 | | 0 | ---------------------------------------------------------- {WeylGroupElement{RootSystem{...8...}, | -5 |}, | 1 |}, | 2 | | 1 | | 2 | | -2 | | 3 | | 0 | ---------------------------------------------------------- {WeylGroupElement{RootSystem{...8...}, | 3 |}, | -1 |}} | -5 | | 0 | | 6 | | 0 | | 3 | | 2 | o45 : List
Notice that M2 returns a list of pairs {w',r} made of a Weyl group element w' and a root r. The root tells you that you have to take the reflection s with respect to r so that ws=w'. You can see how all these elements w' decompose in simple reflections.
i46 : apply(L,x->reducedDecomposition(x#0)) o46 = {{1, 2, 1, 3, 2}, {1, 2, 1, 3, 4}, {1, 2, 3, 2, 4}, {2, 1, 3, 2, 4}} o46 : List
Given two elements w1 and w2, you might also want to compute the interval [w2,w1] for the Bruhat order, i.e. the set of w' less or equal to w1 and greater or equal to w2.
i47 : w1=reduce(R,{1,2,3,4}); w2=reduce(R,{1,2,3,4,1,2}); ib=intervalBruhat(w1,w2) o47 = ... a big mess that I don't want to display here... o47 : HasseDiagram
Note the order in which the arguments w1 and w2 are written.
The interval is returned as an object of class HasseDiagram
. It contains all information on the Bruhat order for the given interval and for that reason, it does not display very well. Basically, it describes the Hasse Diagram of the interval, layer by layer. Let us extract part of it and see what there is to see.
i48 : ib#0 o48 = {{WeylGroupElement{RootSystem{...8...}, | -3 |}, {{0, | -1 |}, {1, | 1 |}}}} | -2 | | 2 | | 1 | | 6 | | -2 | | -2 | | 3 | | 0 | | 0 | o48 : List
In M2, the first element of a list has index 0. So here, we have extracted this first element. It represents the top row of the Hasse diagram. Since this Hasse diagram is an interval, the top row just has one vertex w1 together with edges to the second row. This top row is of the form {{w1,someEdges}} where someEdges is a list {{0,r1},{1,r2}} describing edges frow w1 to the second row. The {0,r1} and {1,r2} are these edges. The 0 tells us that the first edge goes to the element of index 0 in the next row (with the M2 convention for lists) and the r1 is the root corresponding to the reflection to get there. So, all in all, this layer has one vertex connected to two vertices in the next row. Let us see this next row.
i49 : ib#1 o49 = {{WeylGroupElement{RootSystem{...8...}, | -3 |}, {{0, | 2 |}}}, | -1 | | -1 | | 6 | | 0 | | 1 | | 0 | ---------------------------------------------------------------- {WeylGroupElement{RootSystem{...8...}, | -5 |}, {{0, | -1 |}}}} | 2 | | 2 | | 2 | | -2 | | 3 | | 0 | o49 : List
This time, we have two vertices, each of them having one edge to the next row. Finally, the last row
i50 : ib#2 o50 = {{WeylGroupElement{RootSystem{...8...}, | -4 |}, {}}} | 1 | | 4 | | 1 | o50 : List
has one vertex w2 and of course no edge to the next row. We therefore recognize the structure of a non-empty interval with l(w2)=l(w1)-2.
We can also produce pictures of Hasse diagrams in SVG format. First, we transform the Hasse diagram into an object of class HasseGraph, which is nearly the same thing, except that it contains strings instead of Weyl group elements and roots. In other words, it is really ready to be displayed as a graph with labels (the strings).
i51 : mygraph=hasseDiagramToGraph(ib,"labels"=>"reducedDecomposition") o51 = HasseGraph{{{121324, {{2, 0}, {121, 1}}}}, {{12134, {{1, 0}}}, {12324, {{2, 0}}}}, {{1234, {}}}} o51 : HasseGraph
The option says that we want the labels on the vertices and edges to be reduced decompositions (of the elements and of the reflections).
Then we can produce the description of a picture as a list of graphic primitives in the specifications of the package Graphics
and finally turn this list of graphic primitives into an SVG picture that we store in a file called "myinterval.svg".
i52 : svgPicture(hasseGraphToPicture(mygraph),"myinterval") o52 = myinterval.svg o52 : File
Your browser can be directly called from M2 to view the SVG picture:
i53 : viewPicture "myinterval.svg"
If your browser does not support SVG, it will not display anything. Instead, you can manually open the picture in anything that opens SVG images. The SVG picture that you should see is here so if your browser displays it, you should be fine.
It is also possible to compute and display intervals for the Bruhat order (induced) on quotients of the Weyl group by a parabolic subgroup. For the fun of it, let us compute the whole Hasse diagram of the Weyl group of E6 modulo the parabolic subgroup generated by the reflections 1,2,3,4 and 5. This parabolic subgroup is a Weyl group of type D5.
i54 : R=rootSystemE(6); i55 : P=parabolic(R,set {1,2,3,4,5}); i56 : myinter=intervalBruhat((neutralWeylGroupElement R) % P, (longestWeylGroupElement R) % P); i57 : mygraph=hasseDiagramToGraph(myinter,"labels"=>"reduced decomposition"); i58 : mypicture=hasseGraphToPicture(mygraph); i59 : svgPicture(mypicture,"hasseDiagE6modD5"); i60 : viewPicture("hasseDiagE6modD5.svg")
See the result here.
The picture can be made nicer by passing svg options to svgPicture.
i61 : myfancypicture=hasseGraphToPicture(mygraph, "point radius"=>4, "edge options"=>new HashTable from {"stroke-width"=>"4"}, "point options"=>new HashTable from {"fill-opacity"=>"1","fill"=>"red"}); i62 : svgPicture(myfancypicture,"fancyhasseDiagE6modD5"); i63 : viewPicture("fancyhasseDiagE6modD5.svg")
See the result here.
Note that it is also possible to generate a picture in the pgf/tikz language to include in a tex or latex document, by using the command pgfPicture of the package Graphics.
Maybe a word of caution is needed. Recall that the number of elements in a Weyl group grows quickly with the rank of the group: for example, the Weyl group of E6 already has 27.34.5=51840 elements, so if you wanted to compute the whole Hasse diagram, your computer would probably do it, but it would take several hours and occupy some memory.
Since the hasse diagram contains a lot of information on a Weyl group and can take some time to be computed, it can be convenient to store it in a file. Strictly speaking, this is not possible for the moment, but it is possible to store an object of class HasseGraph in a file, and then from it, it is quite quick to recover the original Hasse diagram.
i64 : storeHasseGraph(mygraph,"mygraph.txt") o64 = mygraph.txt o64 : File i65 : mygraph2=loadHasseGraph("mygraph.txt"); i66 : mygraph===mygraph2 o66 = true