Maximal Subgroups and Branching Rules¶
Branching rules¶
If
References for this topic:
Sage can compute how a character of
It is sufficient to consider the case where
Sage has a class BranchingRule
for branching rules. The function
branching_rule
returns elements of this class. For example,
the natural embedding of
sage: b = branching_rule("A3","C2",rule="symmetric"); b
symmetric branching rule A3 => C2
>>> from sage.all import *
>>> b = branching_rule("A3","C2",rule="symmetric"); b
symmetric branching rule A3 => C2
b = branching_rule("A3","C2",rule="symmetric"); b
The name “symmetric” of this branching rule will be
explained further later, but it means that A3
and C2
are the Cartan types of the groups
Now we may see how representations of
sage: A3 = WeylCharacterRing("A3",style="coroots")
sage: chi = A3(1,0,1); chi.degree()
15
sage: C2 = WeylCharacterRing("C2",style="coroots")
sage: chi.branch(C2,rule=b)
C2(0,1) + C2(2,0)
>>> from sage.all import *
>>> A3 = WeylCharacterRing("A3",style="coroots")
>>> chi = A3(Integer(1),Integer(0),Integer(1)); chi.degree()
15
>>> C2 = WeylCharacterRing("C2",style="coroots")
>>> chi.branch(C2,rule=b)
C2(0,1) + C2(2,0)
A3 = WeylCharacterRing("A3",style="coroots") chi = A3(1,0,1); chi.degree() C2 = WeylCharacterRing("C2",style="coroots") chi.branch(C2,rule=b)
Alternatively, we may pass chi
to b
as an
argument of its branch method, which gives the same
result:
sage: b.branch(chi)
C2(0,1) + C2(2,0)
>>> from sage.all import *
>>> b.branch(chi)
C2(0,1) + C2(2,0)
b.branch(chi)
It is believed that the built-in branching rules of Sage are sufficient to handle all maximal subgroups and this is certainly the case when the rank if less than or equal to 8.
However, if you want to branch to a subgroup that
is not maximal you may not find a built-in
branching rule. We may compose branching rules to build
up embeddings. For example, here are two different
embeddings of C2
in
C4
. One embedding
factors through Weyl character rings
.
sage: C4 = WeylCharacterRing("C4",style="coroots")
sage: b1 = branching_rule("C4","A3","levi")*branching_rule("A3","C2","symmetric"); b1
composite branching rule C4 => (levi) A3 => (symmetric) C2
sage: b2 = branching_rule("C4","C2xC2","orthogonal_sum")*branching_rule("C2xC2","C2","proj1"); b2
composite branching rule C4 => (orthogonal_sum) C2xC2 => (proj1) C2
sage: C2 = WeylCharacterRing("C2",style="coroots")
sage: C4 = WeylCharacterRing("C4",style="coroots")
sage: [C4(2,0,0,1).branch(C2, rule=br) for br in [b1,b2]]
[4*C2(0,0) + 7*C2(0,1) + 15*C2(2,0) + 7*C2(0,2) + 11*C2(2,1) + C2(0,3) + 6*C2(4,0) + 3*C2(2,2),
10*C2(0,0) + 40*C2(1,0) + 50*C2(0,1) + 16*C2(2,0) + 20*C2(1,1) + 4*C2(3,0) + 5*C2(2,1)]
>>> from sage.all import *
>>> C4 = WeylCharacterRing("C4",style="coroots")
>>> b1 = branching_rule("C4","A3","levi")*branching_rule("A3","C2","symmetric"); b1
composite branching rule C4 => (levi) A3 => (symmetric) C2
>>> b2 = branching_rule("C4","C2xC2","orthogonal_sum")*branching_rule("C2xC2","C2","proj1"); b2
composite branching rule C4 => (orthogonal_sum) C2xC2 => (proj1) C2
>>> C2 = WeylCharacterRing("C2",style="coroots")
>>> C4 = WeylCharacterRing("C4",style="coroots")
>>> [C4(Integer(2),Integer(0),Integer(0),Integer(1)).branch(C2, rule=br) for br in [b1,b2]]
[4*C2(0,0) + 7*C2(0,1) + 15*C2(2,0) + 7*C2(0,2) + 11*C2(2,1) + C2(0,3) + 6*C2(4,0) + 3*C2(2,2),
10*C2(0,0) + 40*C2(1,0) + 50*C2(0,1) + 16*C2(2,0) + 20*C2(1,1) + 4*C2(3,0) + 5*C2(2,1)]
C4 = WeylCharacterRing("C4",style="coroots") b1 = branching_rule("C4","A3","levi")*branching_rule("A3","C2","symmetric"); b1 b2 = branching_rule("C4","C2xC2","orthogonal_sum")*branching_rule("C2xC2","C2","proj1"); b2 C2 = WeylCharacterRing("C2",style="coroots") C4 = WeylCharacterRing("C4",style="coroots") [C4(2,0,0,1).branch(C2, rule=br) for br in [b1,b2]]
What’s in a branching rule?¶
The essence of the branching rule is a function from the
weight lattice of
sage: b = branching_rule("A3","C2","symmetric")
sage: for r in RootSystem("A3").ambient_space().simple_roots():
....: print("{} {}".format(r, b(r)))
(1, -1, 0, 0) [1, -1]
(0, 1, -1, 0) [0, 2]
(0, 0, 1, -1) [1, -1]
>>> from sage.all import *
>>> b = branching_rule("A3","C2","symmetric")
>>> for r in RootSystem("A3").ambient_space().simple_roots():
... print("{} {}".format(r, b(r)))
(1, -1, 0, 0) [1, -1]
(0, 1, -1, 0) [0, 2]
(0, 0, 1, -1) [1, -1]
b = branching_rule("A3","C2","symmetric") for r in RootSystem("A3").ambient_space().simple_roots(): print("{} {}".format(r, b(r)))
We could conjugate this map by an element of the Weyl
group of
The branching rule has a describe()
method that shows how
the roots (including the affine root) restrict. This is a
useful way of understanding the embedding. You might
want to try it with various branching rules of different
kinds, "extended"
, "symmetric"
, "levi"
etc.
sage: b.describe()
0
O-------+
| |
| |
O---O---O
1 2 3
A3~
root restrictions A3 => C2:
O=<=O
1 2
C2
1 => 1
2 => 2
3 => 1
For more detailed information use verbose=True
>>> from sage.all import *
>>> b.describe()
<BLANKLINE>
0
O-------+
| |
| |
O---O---O
1 2 3
A3~
<BLANKLINE>
root restrictions A3 => C2:
<BLANKLINE>
O=<=O
1 2
C2
<BLANKLINE>
1 => 1
2 => 2
3 => 1
<BLANKLINE>
For more detailed information use verbose=True
b.describe()
The extended Dynkin diagram of 3 => 1
means that the third simple root verbose=true
you will
be shown the restriction of all simple roots and the
affine root, and also the restrictions of the fundamental weights
(in coroot notation).
Maximal subgroups¶
Sage has a database of maximal subgroups for every simple Cartan
type of rank maximal_subgroups
method of the Weyl character ring:
sage: E7 = WeylCharacterRing("E7",style="coroots")
sage: E7.maximal_subgroups()
A7:branching_rule("E7","A7","extended")
E6:branching_rule("E7","E6","levi")
A2:branching_rule("E7","A2","miscellaneous")
A1:branching_rule("E7","A1","iii")
A1:branching_rule("E7","A1","iv")
A1xF4:branching_rule("E7","A1xF4","miscellaneous")
G2xC3:branching_rule("E7","G2xC3","miscellaneous")
A1xG2:branching_rule("E7","A1xG2","miscellaneous")
A1xA1:branching_rule("E7","A1xA1","miscellaneous")
A1xD6:branching_rule("E7","A1xD6","extended")
A5xA2:branching_rule("E7","A5xA2","extended")
>>> from sage.all import *
>>> E7 = WeylCharacterRing("E7",style="coroots")
>>> E7.maximal_subgroups()
A7:branching_rule("E7","A7","extended")
E6:branching_rule("E7","E6","levi")
A2:branching_rule("E7","A2","miscellaneous")
A1:branching_rule("E7","A1","iii")
A1:branching_rule("E7","A1","iv")
A1xF4:branching_rule("E7","A1xF4","miscellaneous")
G2xC3:branching_rule("E7","G2xC3","miscellaneous")
A1xG2:branching_rule("E7","A1xG2","miscellaneous")
A1xA1:branching_rule("E7","A1xA1","miscellaneous")
A1xD6:branching_rule("E7","A1xD6","extended")
A5xA2:branching_rule("E7","A5xA2","extended")
E7 = WeylCharacterRing("E7",style="coroots") E7.maximal_subgroups()
It should be understood that there are other ways of
embedding
sage: b = E7.maximal_subgroup("A2"); b
miscellaneous branching rule E7 => A2
sage: E7, A2 = [WeylCharacterRing(x,style="coroots") for x in ["E7","A2"]]
sage: E7(1,0,0,0,0,0,0).branch(A2,rule=b)
A2(1,1) + A2(4,4)
>>> from sage.all import *
>>> b = E7.maximal_subgroup("A2"); b
miscellaneous branching rule E7 => A2
>>> E7, A2 = [WeylCharacterRing(x,style="coroots") for x in ["E7","A2"]]
>>> E7(Integer(1),Integer(0),Integer(0),Integer(0),Integer(0),Integer(0),Integer(0)).branch(A2,rule=b)
A2(1,1) + A2(4,4)
b = E7.maximal_subgroup("A2"); b E7, A2 = [WeylCharacterRing(x,style="coroots") for x in ["E7","A2"]] E7(1,0,0,0,0,0,0).branch(A2,rule=b)
This gives the same branching rule as just pasting line beginning to the right of the colon onto the command line:
sage:branching_rule("E7","A2","miscellaneous")
miscellaneous branching rule E7 => A2
>>> from sage.all import *
sage:branching_rule("E7","A2","miscellaneous")
miscellaneous branching rule E7 => A2
There are two distinct embeddings of maximal_subgroup
method will return a list of rules:
sage: WeylCharacterRing("E7").maximal_subgroup("A1")
[iii branching rule E7 => A1, iv branching rule E7 => A1]
>>> from sage.all import *
>>> WeylCharacterRing("E7").maximal_subgroup("A1")
[iii branching rule E7 => A1, iv branching rule E7 => A1]
WeylCharacterRing("E7").maximal_subgroup("A1")
The list of maximal subgroups returned by the maximal_subgroups
method for irreducible Cartan types of rank up to 8 is believed to
be complete up to outer automorphisms. You may want a list that is
complete up to inner automorphisms. For example,
sage: E6, A2xG2 = [WeylCharacterRing(x,style="coroots") for x in ["E6","A2xG2"]]
sage: b = E6.maximal_subgroup("A2xG2"); b
miscellaneous branching rule E6 => A2xG2
sage: E6(1,0,0,0,0,0).branch(A2xG2,rule=b)
A2xG2(0,1,1,0) + A2xG2(2,0,0,0)
sage: E6(0,0,0,0,0,1).branch(A2xG2,rule=b)
A2xG2(1,0,1,0) + A2xG2(0,2,0,0)
>>> from sage.all import *
>>> E6, A2xG2 = [WeylCharacterRing(x,style="coroots") for x in ["E6","A2xG2"]]
>>> b = E6.maximal_subgroup("A2xG2"); b
miscellaneous branching rule E6 => A2xG2
>>> E6(Integer(1),Integer(0),Integer(0),Integer(0),Integer(0),Integer(0)).branch(A2xG2,rule=b)
A2xG2(0,1,1,0) + A2xG2(2,0,0,0)
>>> E6(Integer(0),Integer(0),Integer(0),Integer(0),Integer(0),Integer(1)).branch(A2xG2,rule=b)
A2xG2(1,0,1,0) + A2xG2(0,2,0,0)
E6, A2xG2 = [WeylCharacterRing(x,style="coroots") for x in ["E6","A2xG2"]] b = E6.maximal_subgroup("A2xG2"); b E6(1,0,0,0,0,0).branch(A2xG2,rule=b) E6(0,0,0,0,0,1).branch(A2xG2,rule=b)
Since as we see the two 27 dimensional irreducibles (which are
interchanged by the outer automorphism) have different branching,
the
sage: b1 = branching_rule("E6","E6","automorphic")*b; b1
composite branching rule E6 => (automorphic) E6 => (miscellaneous) A2xG2
>>> from sage.all import *
>>> b1 = branching_rule("E6","E6","automorphic")*b; b1
composite branching rule E6 => (automorphic) E6 => (miscellaneous) A2xG2
b1 = branching_rule("E6","E6","automorphic")*b; b1
Levi subgroups¶
A Levi subgroup may or may not be maximal. They are easily
classified. If one starts with a Dynkin diagram for
For example, here is the A3 Dynkin diagram:
sage: A3 = WeylCharacterRing("A3")
sage: A3.dynkin_diagram()
O---O---O
1 2 3
A3
>>> from sage.all import *
>>> A3 = WeylCharacterRing("A3")
>>> A3.dynkin_diagram()
O---O---O
1 2 3
A3
A3 = WeylCharacterRing("A3") A3.dynkin_diagram()
We see that we may remove the node 3 and obtain
Let us construct the irreducible
representations of
sage: reps = [A3(v) for v in A3.fundamental_weights()]; reps
[A3(1,0,0,0), A3(1,1,0,0), A3(1,1,1,0)]
sage: A2 = WeylCharacterRing("A2")
sage: A1xA1 = WeylCharacterRing("A1xA1")
sage: [pi.branch(A2, rule="levi") for pi in reps]
[A2(0,0,0) + A2(1,0,0), A2(1,0,0) + A2(1,1,0), A2(1,1,0) + A2(1,1,1)]
sage: [pi.branch(A1xA1, rule="levi") for pi in reps]
[A1xA1(1,0,0,0) + A1xA1(0,0,1,0),
A1xA1(1,1,0,0) + A1xA1(1,0,1,0) + A1xA1(0,0,1,1),
A1xA1(1,1,1,0) + A1xA1(1,0,1,1)]
>>> from sage.all import *
>>> reps = [A3(v) for v in A3.fundamental_weights()]; reps
[A3(1,0,0,0), A3(1,1,0,0), A3(1,1,1,0)]
>>> A2 = WeylCharacterRing("A2")
>>> A1xA1 = WeylCharacterRing("A1xA1")
>>> [pi.branch(A2, rule="levi") for pi in reps]
[A2(0,0,0) + A2(1,0,0), A2(1,0,0) + A2(1,1,0), A2(1,1,0) + A2(1,1,1)]
>>> [pi.branch(A1xA1, rule="levi") for pi in reps]
[A1xA1(1,0,0,0) + A1xA1(0,0,1,0),
A1xA1(1,1,0,0) + A1xA1(1,0,1,0) + A1xA1(0,0,1,1),
A1xA1(1,1,1,0) + A1xA1(1,0,1,1)]
reps = [A3(v) for v in A3.fundamental_weights()]; reps A2 = WeylCharacterRing("A2") A1xA1 = WeylCharacterRing("A1xA1") [pi.branch(A2, rule="levi") for pi in reps] [pi.branch(A1xA1, rule="levi") for pi in reps]
Let us redo this calculation in coroot notation. As we have explained,
coroot notation does not distinguish between representations of
sage: A3 = WeylCharacterRing("A3", style="coroots")
sage: reps = [A3(v) for v in A3.fundamental_weights()]; reps
[A3(1,0,0), A3(0,1,0), A3(0,0,1)]
sage: A2 = WeylCharacterRing("A2", style="coroots")
sage: A1xA1 = WeylCharacterRing("A1xA1", style="coroots")
sage: [pi.branch(A2, rule="levi") for pi in reps]
[A2(0,0) + A2(1,0), A2(0,1) + A2(1,0), A2(0,0) + A2(0,1)]
sage: [pi.branch(A1xA1, rule="levi") for pi in reps]
[A1xA1(1,0) + A1xA1(0,1), 2*A1xA1(0,0) + A1xA1(1,1), A1xA1(1,0) + A1xA1(0,1)]
>>> from sage.all import *
>>> A3 = WeylCharacterRing("A3", style="coroots")
>>> reps = [A3(v) for v in A3.fundamental_weights()]; reps
[A3(1,0,0), A3(0,1,0), A3(0,0,1)]
>>> A2 = WeylCharacterRing("A2", style="coroots")
>>> A1xA1 = WeylCharacterRing("A1xA1", style="coroots")
>>> [pi.branch(A2, rule="levi") for pi in reps]
[A2(0,0) + A2(1,0), A2(0,1) + A2(1,0), A2(0,0) + A2(0,1)]
>>> [pi.branch(A1xA1, rule="levi") for pi in reps]
[A1xA1(1,0) + A1xA1(0,1), 2*A1xA1(0,0) + A1xA1(1,1), A1xA1(1,0) + A1xA1(0,1)]
A3 = WeylCharacterRing("A3", style="coroots") reps = [A3(v) for v in A3.fundamental_weights()]; reps A2 = WeylCharacterRing("A2", style="coroots") A1xA1 = WeylCharacterRing("A1xA1", style="coroots") [pi.branch(A2, rule="levi") for pi in reps] [pi.branch(A1xA1, rule="levi") for pi in reps]
Now we may observe a distinction difference in branching from
versus
Consider the representation A3(0,1,0)
, which is the six dimensional
exterior square. In the coroot notation, the restriction contained two
copies of the trivial representation, 2*A1xA1(0,0)
. The other way,
we had instead three distinct representations in the restriction, namely
A1xA1(1,1,0,0)
and A1xA1(0,0,1,1)
, that is,
The Levi subgroup A1xA1
is actually not maximal. Indeed, we may
factor the embedding:
Therefore there are branching rules A3 -> C2
and C2 -> A2
, and
we could accomplish the branching in two steps, thus:
sage: A3 = WeylCharacterRing("A3", style="coroots")
sage: C2 = WeylCharacterRing("C2", style="coroots")
sage: B2 = WeylCharacterRing("B2", style="coroots")
sage: D2 = WeylCharacterRing("D2", style="coroots")
sage: A1xA1 = WeylCharacterRing("A1xA1", style="coroots")
sage: reps = [A3(fw) for fw in A3.fundamental_weights()]
sage: [pi.branch(C2, rule="symmetric").branch(B2, rule="isomorphic"). \
....: branch(D2, rule="extended").branch(A1xA1, rule="isomorphic") for pi in reps]
[A1xA1(1,0) + A1xA1(0,1), 2*A1xA1(0,0) + A1xA1(1,1), A1xA1(1,0) + A1xA1(0,1)]
>>> from sage.all import *
>>> A3 = WeylCharacterRing("A3", style="coroots")
>>> C2 = WeylCharacterRing("C2", style="coroots")
>>> B2 = WeylCharacterRing("B2", style="coroots")
>>> D2 = WeylCharacterRing("D2", style="coroots")
>>> A1xA1 = WeylCharacterRing("A1xA1", style="coroots")
>>> reps = [A3(fw) for fw in A3.fundamental_weights()]
>>> [pi.branch(C2, rule="symmetric").branch(B2, rule="isomorphic"). branch(D2, rule="extended").branch(A1xA1, rule="isomorphic") for pi in reps]
[A1xA1(1,0) + A1xA1(0,1), 2*A1xA1(0,0) + A1xA1(1,1), A1xA1(1,0) + A1xA1(0,1)]
A3 = WeylCharacterRing("A3", style="coroots") C2 = WeylCharacterRing("C2", style="coroots") B2 = WeylCharacterRing("B2", style="coroots") D2 = WeylCharacterRing("D2", style="coroots") A1xA1 = WeylCharacterRing("A1xA1", style="coroots") reps = [A3(fw) for fw in A3.fundamental_weights()] [pi.branch(C2, rule="symmetric").branch(B2, rule="isomorphic"). \ branch(D2, rule="extended").branch(A1xA1, rule="isomorphic") for pi in reps]
As you can see, we’ve redone the branching rather circuitously this
way, making use of the branching rules A3 -> C2
and B2 -> D2
, and
two accidental isomorphisms C2 = B2
and D2 = A1xA1
. It is much
easier to go in one step using rule="levi"
, but reassuring that we
get the same answer!
Subgroups classified by the extended Dynkin diagram¶
It is also true that if we remove one node from the extended Dynkin diagram that we obtain the Dynkin diagram of a subgroup. For example:
sage: G2 = WeylCharacterRing("G2", style="coroots")
sage: G2.extended_dynkin_diagram()
3
O=<=O---O
1 2 0
G2~
>>> from sage.all import *
>>> G2 = WeylCharacterRing("G2", style="coroots")
>>> G2.extended_dynkin_diagram()
3
O=<=O---O
1 2 0
G2~
G2 = WeylCharacterRing("G2", style="coroots") G2.extended_dynkin_diagram()
Observe that by removing the 1 node that we obtain an
sage: G2 = WeylCharacterRing("G2", style="coroots")
sage: A2 = WeylCharacterRing("A2", style="coroots")
sage: [G2(f).degree() for f in G2.fundamental_weights()]
[7, 14]
sage: [G2(f).branch(A2, rule="extended") for f in G2.fundamental_weights()]
[A2(0,0) + A2(0,1) + A2(1,0), A2(0,1) + A2(1,0) + A2(1,1)]
>>> from sage.all import *
>>> G2 = WeylCharacterRing("G2", style="coroots")
>>> A2 = WeylCharacterRing("A2", style="coroots")
>>> [G2(f).degree() for f in G2.fundamental_weights()]
[7, 14]
>>> [G2(f).branch(A2, rule="extended") for f in G2.fundamental_weights()]
[A2(0,0) + A2(0,1) + A2(1,0), A2(0,1) + A2(1,0) + A2(1,1)]
G2 = WeylCharacterRing("G2", style="coroots") A2 = WeylCharacterRing("A2", style="coroots") [G2(f).degree() for f in G2.fundamental_weights()] [G2(f).branch(A2, rule="extended") for f in G2.fundamental_weights()]
The two representations of
For embeddings of this type, the rank of the subgroup
Levi subgroups of ¶
The exceptional group rule="levi"
. To obtain the other,
use the rule:
sage: branching_rule("G2","A2","extended")*branching_rule("A2","A1","levi")
composite branching rule G2 => (extended) A2 => (levi) A1
>>> from sage.all import *
>>> branching_rule("G2","A2","extended")*branching_rule("A2","A1","levi")
composite branching rule G2 => (extended) A2 => (levi) A1
branching_rule("G2","A2","extended")*branching_rule("A2","A1","levi")
which branches to the
Orthogonal and symplectic subgroups of orthogonal and symplectic groups¶
If ['D',r]
to ['B',r-1]
or ['B',r]
to ['D',r]
. These are
handled as follows:
sage: branching_rule("B4","D4",rule="extended")
extended branching rule B4 => D4
sage: branching_rule("D4","B3",rule="symmetric")
symmetric branching rule D4 => B3
>>> from sage.all import *
>>> branching_rule("B4","D4",rule="extended")
extended branching rule B4 => D4
>>> branching_rule("D4","B3",rule="symmetric")
symmetric branching rule D4 => B3
branching_rule("B4","D4",rule="extended") branching_rule("D4","B3",rule="symmetric")
If
Sometimes this embedding is of extended type, and sometimes it is
not. It is of extended type unless rule="extended"
. In any case you
may use rule="orthogonal_sum"
. The name refer to the origin of the
embedding
There are four cases depending on the parity of
['D',k] x ['D',l] --> ['D',k+l]
This is of extended type. Thus consider the embedding
D4xD3 -> D7
. Here is the extended Dynkin diagram:
0 O O 7
| |
| |
O---O---O---O---O---O
1 2 3 4 5 6
Removing the 4 vertex results in a disconnected Dynkin diagram:
0 O O 7
| |
| |
O---O---O O---O
1 2 3 5 6
This is D4xD3
. Therefore use the “extended” branching rule:
sage: D7 = WeylCharacterRing("D7", style="coroots")
sage: D4xD3 = WeylCharacterRing("D4xD3", style="coroots")
sage: spin = D7(D7.fundamental_weights()[7]); spin
D7(0,0,0,0,0,0,1)
sage: spin.branch(D4xD3, rule="extended")
D4xD3(0,0,1,0,0,1,0) + D4xD3(0,0,0,1,0,0,1)
>>> from sage.all import *
>>> D7 = WeylCharacterRing("D7", style="coroots")
>>> D4xD3 = WeylCharacterRing("D4xD3", style="coroots")
>>> spin = D7(D7.fundamental_weights()[Integer(7)]); spin
D7(0,0,0,0,0,0,1)
>>> spin.branch(D4xD3, rule="extended")
D4xD3(0,0,1,0,0,1,0) + D4xD3(0,0,0,1,0,0,1)
D7 = WeylCharacterRing("D7", style="coroots") D4xD3 = WeylCharacterRing("D4xD3", style="coroots") spin = D7(D7.fundamental_weights()[7]); spin spin.branch(D4xD3, rule="extended")
But we could equally well use the “orthogonal_sum” rule:
sage: spin.branch(D4xD3, rule="orthogonal_sum")
D4xD3(0,0,1,0,0,1,0) + D4xD3(0,0,0,1,0,0,1)
>>> from sage.all import *
>>> spin.branch(D4xD3, rule="orthogonal_sum")
D4xD3(0,0,1,0,0,1,0) + D4xD3(0,0,0,1,0,0,1)
spin.branch(D4xD3, rule="orthogonal_sum")
Similarly we have embeddings:
['D',k] x ['B',l] --> ['B',k+l]
These are also of extended type. For example consider the embedding of
D3xB2 -> B5
. Here is the B5
extended Dynkin diagram:
O 0
|
|
O---O---O---O=>=O
1 2 3 4 5
Removing the 3 node gives:
O 0
|
O---O O=>=O
1 2 4 5
and this is the Dynkin diagram or D3xB2
. For such branchings we
again use either rule="extended"
or rule="orthogonal_sum"
.
Finally, there is an embedding
['B',k] x ['B',l] --> ['D',k+l+1]
This is not of extended type, so you may not use rule="extended"
.
You must use rule="orthogonal_sum"
:
sage: D5 = WeylCharacterRing("D5",style="coroots")
sage: B2xB2 = WeylCharacterRing("B2xB2",style="coroots")
sage: [D5(v).branch(B2xB2,rule="orthogonal_sum") for v in D5.fundamental_weights()]
[B2xB2(1,0,0,0) + B2xB2(0,0,1,0),
B2xB2(0,2,0,0) + B2xB2(1,0,1,0) + B2xB2(0,0,0,2),
B2xB2(0,2,0,0) + B2xB2(0,2,1,0) + B2xB2(1,0,0,2) + B2xB2(0,0,0,2),
B2xB2(0,1,0,1), B2xB2(0,1,0,1)]
>>> from sage.all import *
>>> D5 = WeylCharacterRing("D5",style="coroots")
>>> B2xB2 = WeylCharacterRing("B2xB2",style="coroots")
>>> [D5(v).branch(B2xB2,rule="orthogonal_sum") for v in D5.fundamental_weights()]
[B2xB2(1,0,0,0) + B2xB2(0,0,1,0),
B2xB2(0,2,0,0) + B2xB2(1,0,1,0) + B2xB2(0,0,0,2),
B2xB2(0,2,0,0) + B2xB2(0,2,1,0) + B2xB2(1,0,0,2) + B2xB2(0,0,0,2),
B2xB2(0,1,0,1), B2xB2(0,1,0,1)]
D5 = WeylCharacterRing("D5",style="coroots") B2xB2 = WeylCharacterRing("B2xB2",style="coroots") [D5(v).branch(B2xB2,rule="orthogonal_sum") for v in D5.fundamental_weights()]
Non-maximal Levi subgroups and Projection from Reducible Types¶
Not all Levi subgroups are maximal. Recall that the Dynkin-diagram
of a Levi subgroup
If the Levi subgroup is not maximal, the branching rule may or may not be implemented in Sage. However if it is not implemented, it may be constructed as a composition of two branching rules.
For example, prior to Sage-6.1 branching_rule("E6","A5","levi")
returned a not-implemented error and the advice to branch to
A5xA1
. And we can see from the extended Dynkin diagram of A5xA1
. To
construct the branching rule to
sage: b = branching_rule("E6","A5xA1","extended")*branching_rule("A5xA1","A5","proj1"); b
composite branching rule E6 => (extended) A5xA1 => (proj1) A5
sage: E6 = WeylCharacterRing("E6",style="coroots")
sage: A5 = WeylCharacterRing("A5",style="coroots")
sage: E6(0,1,0,0,0,0).branch(A5,rule=b)
3*A5(0,0,0,0,0) + 2*A5(0,0,1,0,0) + A5(1,0,0,0,1)
sage: b.describe()
O 0
|
|
O 2
|
|
O---O---O---O---O
1 3 4 5 6
E6~
root restrictions E6 => A5:
O---O---O---O---O
1 2 3 4 5
A5
0 => (zero)
1 => 1
3 => 2
4 => 3
5 => 4
6 => 5
For more detailed information use verbose=True
>>> from sage.all import *
>>> b = branching_rule("E6","A5xA1","extended")*branching_rule("A5xA1","A5","proj1"); b
composite branching rule E6 => (extended) A5xA1 => (proj1) A5
>>> E6 = WeylCharacterRing("E6",style="coroots")
>>> A5 = WeylCharacterRing("A5",style="coroots")
>>> E6(Integer(0),Integer(1),Integer(0),Integer(0),Integer(0),Integer(0)).branch(A5,rule=b)
3*A5(0,0,0,0,0) + 2*A5(0,0,1,0,0) + A5(1,0,0,0,1)
>>> b.describe()
<BLANKLINE>
O 0
|
|
O 2
|
|
O---O---O---O---O
1 3 4 5 6
E6~
root restrictions E6 => A5:
<BLANKLINE>
O---O---O---O---O
1 2 3 4 5
A5
<BLANKLINE>
0 => (zero)
1 => 1
3 => 2
4 => 3
5 => 4
6 => 5
<BLANKLINE>
For more detailed information use verbose=True
b = branching_rule("E6","A5xA1","extended")*branching_rule("A5xA1","A5","proj1"); b E6 = WeylCharacterRing("E6",style="coroots") A5 = WeylCharacterRing("A5",style="coroots") E6(0,1,0,0,0,0).branch(A5,rule=b) b.describe()
Note that it is not necessary to construct the Weyl character ring
for the intermediate group A5xA1
.
This last example illustrates another common problem:
how to extract one component from a reducible root system.
We used the rule "proj1"
to extract the first component.
We could similarly use "proj2"
to get the second, or
more generally any combination of components:
sage: branching_rule("A2xB2xG2","A2xG2","proj13")
proj13 branching rule A2xB2xG2 => A2xG2
>>> from sage.all import *
>>> branching_rule("A2xB2xG2","A2xG2","proj13")
proj13 branching rule A2xB2xG2 => A2xG2
branching_rule("A2xB2xG2","A2xG2","proj13")
Symmetric subgroups¶
If
Suppose that the Dynkin diagram of
Here are the branching rules that can be obtained using
rule="symmetric"
.
Cartan Types |
||
---|---|---|
|
||
|
||
|
||
|
||
|
Tensor products¶
If
Group |
Subgroup |
Cartan Types |
---|---|---|
|
||
|
||
|
||
|
||
|
||
|
||
|
These branching rules are obtained using rule="tensor"
.
Symmetric powers¶
The
['B',r] => A1
['C',r] => A1
and these may be obtained using rule="symmetric_power"
.
Plethysms¶
The above branching rules are sufficient for most cases, but a few fall between the cracks. Mostly these involve maximal subgroups of fairly small rank.
The rule rule="plethysm"
is a powerful rule that includes any
branching rule from types
We consider a homomorphism branching_rule_from_plethysm
produces the corresponding branching
rule. The main ingredient is the character
Let us consider the symmetric fifth power representation of
rule="symmetric_power"
, but
suppose we want to use rule="plethysm"
. First we construct the
homomorphism by invoking its character, to be called chi
:
sage: A1 = WeylCharacterRing("A1", style="coroots")
sage: chi = A1([5])
sage: chi.degree()
6
sage: chi.frobenius_schur_indicator()
-1
>>> from sage.all import *
>>> A1 = WeylCharacterRing("A1", style="coroots")
>>> chi = A1([Integer(5)])
>>> chi.degree()
6
>>> chi.frobenius_schur_indicator()
-1
A1 = WeylCharacterRing("A1", style="coroots") chi = A1([5]) chi.degree() chi.frobenius_schur_indicator()
This confirms that the character has degree 6 and is symplectic, so it
corresponds to a homomorphism C3 -> A1
:
sage: A1 = WeylCharacterRing("A1", style="coroots")
sage: C3 = WeylCharacterRing("C3", style="coroots")
sage: chi = A1([5])
sage: sym5rule = branching_rule_from_plethysm(chi, "C3")
sage: [C3(hwv).branch(A1, rule=sym5rule) for hwv in C3.fundamental_weights()]
[A1(5), A1(4) + A1(8), A1(3) + A1(9)]
>>> from sage.all import *
>>> A1 = WeylCharacterRing("A1", style="coroots")
>>> C3 = WeylCharacterRing("C3", style="coroots")
>>> chi = A1([Integer(5)])
>>> sym5rule = branching_rule_from_plethysm(chi, "C3")
>>> [C3(hwv).branch(A1, rule=sym5rule) for hwv in C3.fundamental_weights()]
[A1(5), A1(4) + A1(8), A1(3) + A1(9)]
A1 = WeylCharacterRing("A1", style="coroots") C3 = WeylCharacterRing("C3", style="coroots") chi = A1([5]) sym5rule = branching_rule_from_plethysm(chi, "C3") [C3(hwv).branch(A1, rule=sym5rule) for hwv in C3.fundamental_weights()]
This is identical to the results we would obtain using
rule="symmetric_power"
:
sage: A1 = WeylCharacterRing("A1", style="coroots")
sage: C3 = WeylCharacterRing("C3", style="coroots")
sage: [C3(v).branch(A1, rule="symmetric_power") for v in C3.fundamental_weights()]
[A1(5), A1(4) + A1(8), A1(3) + A1(9)]
>>> from sage.all import *
>>> A1 = WeylCharacterRing("A1", style="coroots")
>>> C3 = WeylCharacterRing("C3", style="coroots")
>>> [C3(v).branch(A1, rule="symmetric_power") for v in C3.fundamental_weights()]
[A1(5), A1(4) + A1(8), A1(3) + A1(9)]
A1 = WeylCharacterRing("A1", style="coroots") C3 = WeylCharacterRing("C3", style="coroots") [C3(v).branch(A1, rule="symmetric_power") for v in C3.fundamental_weights()]
But the next example of plethysm gives a branching rule not available by other methods:
sage: G2 = WeylCharacterRing("G2", style="coroots")
sage: D7 = WeylCharacterRing("D7", style="coroots")
sage: ad = G2.adjoint_representation(); ad.degree()
14
sage: ad.frobenius_schur_indicator()
1
sage: for r in D7.fundamental_weights(): # long time (1.29s)
....: print(D7(r).branch(G2, rule=branching_rule_from_plethysm(ad, "D7")))
G2(0,1)
G2(0,1) + G2(3,0)
G2(0,0) + G2(2,0) + G2(3,0) + G2(0,2) + G2(4,0)
G2(0,1) + G2(2,0) + G2(1,1) + G2(0,2) + G2(2,1) + G2(4,0) + G2(3,1)
G2(1,0) + G2(0,1) + G2(1,1) + 2*G2(3,0) + 2*G2(2,1) + G2(1,2) + G2(3,1) + G2(5,0) + G2(0,3)
G2(1,1)
G2(1,1)
>>> from sage.all import *
>>> G2 = WeylCharacterRing("G2", style="coroots")
>>> D7 = WeylCharacterRing("D7", style="coroots")
>>> ad = G2.adjoint_representation(); ad.degree()
14
>>> ad.frobenius_schur_indicator()
1
>>> for r in D7.fundamental_weights(): # long time (1.29s)
... print(D7(r).branch(G2, rule=branching_rule_from_plethysm(ad, "D7")))
G2(0,1)
G2(0,1) + G2(3,0)
G2(0,0) + G2(2,0) + G2(3,0) + G2(0,2) + G2(4,0)
G2(0,1) + G2(2,0) + G2(1,1) + G2(0,2) + G2(2,1) + G2(4,0) + G2(3,1)
G2(1,0) + G2(0,1) + G2(1,1) + 2*G2(3,0) + 2*G2(2,1) + G2(1,2) + G2(3,1) + G2(5,0) + G2(0,3)
G2(1,1)
G2(1,1)
G2 = WeylCharacterRing("G2", style="coroots") D7 = WeylCharacterRing("D7", style="coroots") ad = G2.adjoint_representation(); ad.degree() ad.frobenius_schur_indicator() for r in D7.fundamental_weights(): # long time (1.29s) print(D7(r).branch(G2, rule=branching_rule_from_plethysm(ad, "D7")))
In this example,
We do not actually have to create the character (or for that matter its ambient WeylCharacterRing) in order to create the branching rule:
sage: branching_rule("D4","A2.adjoint_representation()","plethysm")
plethysm (along A2(1,1)) branching rule D4 => A2
>>> from sage.all import *
>>> branching_rule("D4","A2.adjoint_representation()","plethysm")
plethysm (along A2(1,1)) branching rule D4 => A2
branching_rule("D4","A2.adjoint_representation()","plethysm")
The adjoint representation of any semisimple Lie group is orthogonal, so we do not need to compute the Frobenius-Schur indicator.
Miscellaneous other subgroups¶
Use rule="miscellaneous"
for the following rules. Every maximal
subgroup rule="extended"
.
The first rule corresponds to the embedding of
The remaining rules come about as follows. Let
Then the centralizer of
The embedding of
The embedding if
The embedding if
The embedding
Maximal A1 subgroups of Exceptional Groups¶
There are seven embeddings of
sage: branching_rule("G2","A1","i")
i branching rule G2 => A1
sage: branching_rule("F4","A1","ii")
ii branching rule F4 => A1
sage: branching_rule("E7","A1","iii")
iii branching rule E7 => A1
sage: branching_rule("E7","A1","iv")
iv branching rule E7 => A1
sage: branching_rule("E8","A1","v")
v branching rule E8 => A1
sage: branching_rule("E8","A1","vi")
vi branching rule E8 => A1
sage: branching_rule("E8","A1","vii")
vii branching rule E8 => A1
>>> from sage.all import *
>>> branching_rule("G2","A1","i")
i branching rule G2 => A1
>>> branching_rule("F4","A1","ii")
ii branching rule F4 => A1
>>> branching_rule("E7","A1","iii")
iii branching rule E7 => A1
>>> branching_rule("E7","A1","iv")
iv branching rule E7 => A1
>>> branching_rule("E8","A1","v")
v branching rule E8 => A1
>>> branching_rule("E8","A1","vi")
vi branching rule E8 => A1
>>> branching_rule("E8","A1","vii")
vii branching rule E8 => A1
branching_rule("G2","A1","i") branching_rule("F4","A1","ii") branching_rule("E7","A1","iii") branching_rule("E7","A1","iv") branching_rule("E8","A1","v") branching_rule("E8","A1","vi") branching_rule("E8","A1","vii")
The embeddings are characterized by the root
restrictions in their branching rules: usually
a simple root of the ambient group describe()
method of the branching rule.
Thus:
sage: branching_rule("E8","A1","vii").describe()
O 2
|
|
O---O---O---O---O---O---O---O
1 3 4 5 6 7 8 0
E8~
root restrictions E8 => A1:
O
1
A1
1 => 1
2 => 1
3 => 1
4 => (zero)
5 => 1
6 => (zero)
7 => 1
8 => 1
For more detailed information use verbose=True
>>> from sage.all import *
>>> branching_rule("E8","A1","vii").describe()
<BLANKLINE>
O 2
|
|
O---O---O---O---O---O---O---O
1 3 4 5 6 7 8 0
E8~
root restrictions E8 => A1:
<BLANKLINE>
O
1
A1
<BLANKLINE>
1 => 1
2 => 1
3 => 1
4 => (zero)
5 => 1
6 => (zero)
7 => 1
8 => 1
<BLANKLINE>
For more detailed information use verbose=True
branching_rule("E8","A1","vii").describe()
Writing your own branching rules¶
Sage has many built-in branching rules. Indeed, at least
up to rank eight (including all the exceptional groups)
branching rules to all maximal subgroups are implemented
as built in rules, except for a few obtainable using
branching_rule_from_plethysm
. This means that
all the rules in [McKayPatera1981] are available in Sage.
Still in this section we are including instructions for coding a rule by
hand. As we have already explained, the branching rule is a function from the
weight lattice of G
to the weight lattice of H
, and if you supply this
you can write your own branching rules.
As an example, let us consider how to implement the branching rule
A3 -> C2
. Here H = C2 = Sp(4)
embedded as a subgroup in
A3 = GL(4)
. The Cartan subalgebra u1, u2, -u2, -u1
. Then
C2.space()
is the two dimensional vector spaces consisting of the
linear functionals u1
and u2
on U
. On the other hand
[u1,u2] -> [u1,u2,-u2,-u1]
,
that is, [x0,x1,x2,x3] -> [x0-x3,x1-x2]
. Hence we may encode the
rule:
def brule(x):
return [x[0]-x[3], x[1]-x[2]]
or simply:
brule = lambda x: [x[0]-x[3], x[1]-x[2]]
Let us check that this agrees with the built-in rule:
sage: A3 = WeylCharacterRing(['A', 3])
sage: C2 = WeylCharacterRing(['C', 2])
sage: brule = lambda x: [x[0]-x[3], x[1]-x[2]]
sage: A3(1,1,0,0).branch(C2, rule=brule)
C2(0,0) + C2(1,1)
sage: A3(1,1,0,0).branch(C2, rule="symmetric")
C2(0,0) + C2(1,1)
>>> from sage.all import *
>>> A3 = WeylCharacterRing(['A', Integer(3)])
>>> C2 = WeylCharacterRing(['C', Integer(2)])
>>> brule = lambda x: [x[Integer(0)]-x[Integer(3)], x[Integer(1)]-x[Integer(2)]]
>>> A3(Integer(1),Integer(1),Integer(0),Integer(0)).branch(C2, rule=brule)
C2(0,0) + C2(1,1)
>>> A3(Integer(1),Integer(1),Integer(0),Integer(0)).branch(C2, rule="symmetric")
C2(0,0) + C2(1,1)
A3 = WeylCharacterRing(['A', 3]) C2 = WeylCharacterRing(['C', 2]) brule = lambda x: [x[0]-x[3], x[1]-x[2]] A3(1,1,0,0).branch(C2, rule=brule) A3(1,1,0,0).branch(C2, rule="symmetric")
Although this works, it is better to make the rule
into an element of the BranchingRule
class, as follows.
sage: brule = BranchingRule("A3","C2",lambda x : [x[0]-x[3], x[1]-x[2]],"custom")
sage: A3(1,1,0,0).branch(C2, rule=brule)
C2(0,0) + C2(1,1)
>>> from sage.all import *
>>> brule = BranchingRule("A3","C2",lambda x : [x[Integer(0)]-x[Integer(3)], x[Integer(1)]-x[Integer(2)]],"custom")
>>> A3(Integer(1),Integer(1),Integer(0),Integer(0)).branch(C2, rule=brule)
C2(0,0) + C2(1,1)
brule = BranchingRule("A3","C2",lambda x : [x[0]-x[3], x[1]-x[2]],"custom") A3(1,1,0,0).branch(C2, rule=brule)
Automorphisms and triality¶
The case where
So the automorphism acts on the representations of
sage: A4 = WeylCharacterRing("A4",style="coroots")
sage: A4(1,0,1,0).degree()
45
sage: A4(0,1,0,1).degree()
45
sage: A4(1,0,1,0).branch(A4,rule="automorphic")
A4(0,1,0,1)
>>> from sage.all import *
>>> A4 = WeylCharacterRing("A4",style="coroots")
>>> A4(Integer(1),Integer(0),Integer(1),Integer(0)).degree()
45
>>> A4(Integer(0),Integer(1),Integer(0),Integer(1)).degree()
45
>>> A4(Integer(1),Integer(0),Integer(1),Integer(0)).branch(A4,rule="automorphic")
A4(0,1,0,1)
A4 = WeylCharacterRing("A4",style="coroots") A4(1,0,1,0).degree() A4(0,1,0,1).degree() A4(1,0,1,0).branch(A4,rule="automorphic")
In the special case where G=D4
, the Dynkin diagram has
extra symmetries, and these correspond to outer automorphisms
of the group. These are implemented as the "triality"
branching rule:
sage: branching_rule("D4","D4","triality").describe()
O 4
|
|
O---O---O
1 |2 3
|
O 0
D4~
root restrictions D4 => D4:
O 4
|
|
O---O---O
1 2 3
D4
1 => 3
2 => 2
3 => 4
4 => 1
For more detailed information use verbose=True
>>> from sage.all import *
>>> branching_rule("D4","D4","triality").describe()
<BLANKLINE>
O 4
|
|
O---O---O
1 |2 3
|
O 0
D4~
root restrictions D4 => D4:
<BLANKLINE>
O 4
|
|
O---O---O
1 2 3
D4
<BLANKLINE>
1 => 3
2 => 2
3 => 4
4 => 1
<BLANKLINE>
For more detailed information use verbose=True
branching_rule("D4","D4","triality").describe()
Triality his is not an automorphisms of
sage: D4 = WeylCharacterRing("D4",style="coroots")
sage: D4(0,0,0,1).branch(D4,rule="triality")
D4(1,0,0,0)
sage: D4(0,0,0,1).branch(D4,rule="triality").branch(D4,rule="triality")
D4(0,0,1,0)
sage: D4(0,0,0,1).branch(D4,rule="triality").branch(D4,rule="triality").branch(D4,rule="triality")
D4(0,0,0,1)
>>> from sage.all import *
>>> D4 = WeylCharacterRing("D4",style="coroots")
>>> D4(Integer(0),Integer(0),Integer(0),Integer(1)).branch(D4,rule="triality")
D4(1,0,0,0)
>>> D4(Integer(0),Integer(0),Integer(0),Integer(1)).branch(D4,rule="triality").branch(D4,rule="triality")
D4(0,0,1,0)
>>> D4(Integer(0),Integer(0),Integer(0),Integer(1)).branch(D4,rule="triality").branch(D4,rule="triality").branch(D4,rule="triality")
D4(0,0,0,1)
D4 = WeylCharacterRing("D4",style="coroots") D4(0,0,0,1).branch(D4,rule="triality") D4(0,0,0,1).branch(D4,rule="triality").branch(D4,rule="triality") D4(0,0,0,1).branch(D4,rule="triality").branch(D4,rule="triality").branch(D4,rule="triality")
By contrast, rule="automorphic"
simply interchanges the two
spin representations, as it always does in type
sage: D4(0,0,0,1).branch(D4,rule="automorphic")
D4(0,0,1,0)
sage: D4(0,0,1,0).branch(D4,rule="automorphic")
D4(0,0,0,1)
>>> from sage.all import *
>>> D4(Integer(0),Integer(0),Integer(0),Integer(1)).branch(D4,rule="automorphic")
D4(0,0,1,0)
>>> D4(Integer(0),Integer(0),Integer(1),Integer(0)).branch(D4,rule="automorphic")
D4(0,0,0,1)
D4(0,0,0,1).branch(D4,rule="automorphic") D4(0,0,1,0).branch(D4,rule="automorphic")