Example #1
0
def coxeter_matrix(t):
    """
    Returns the Coxeter matrix of type t.
    
    EXAMPLES::
    
        sage: coxeter_matrix(['A', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 3]
        [2 2 3 1]
        sage: coxeter_matrix(['B', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 4]
        [2 2 4 1]
        sage: coxeter_matrix(['C', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 4]
        [2 2 4 1]
        sage: coxeter_matrix(['D', 4])
        [1 3 2 2]
        [3 1 3 3]
        [2 3 1 2]
        [2 3 2 1]
    
    ::
    
        sage: coxeter_matrix(['E', 6])
        [1 2 3 2 2 2]
        [2 1 2 3 2 2]
        [3 2 1 3 2 2]
        [2 3 3 1 3 2]
        [2 2 2 3 1 3]
        [2 2 2 2 3 1]
    
    ::
    
        sage: coxeter_matrix(['F', 4])
        [1 3 2 2]
        [3 1 4 2]
        [2 4 1 3]
        [2 2 3 1]
    
    ::
    
        sage: coxeter_matrix(['G', 2])
        [1 6]
        [6 1]
    """
    ct = CartanType(t)
    cf = coxeter_matrix_as_function(ct)
    index_set = ct.index_set()
    MS = MatrixSpace(ZZ, len(index_set))
    m = MS(0)
    for i in range(len(index_set)):
        for j in range(len(index_set)):
            m[i, j] = cf(index_set[i], index_set[j])
    return m
Example #2
0
    def __init__(self, cartan_type, as_dual_of=None):
        """
        TESTS::
        
            sage: R = RootSystem(['A',3])
            sage: R
            Root system of type ['A', 3]
        """
        self._cartan_type = CartanType(cartan_type)

        # Duality
        # The root system can be defined as dual of another root system. This will
        # only affects the pretty printing
        if as_dual_of is None:
            self.dual_side = False
            self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self)
            # still fails for CartanType G2xA1
            try:
                self.dual = RootSystem(self._cartan_type.dual(),
                                       as_dual_of=self)
            except StandardError:
                pass
        else:
            self.dual_side = True
            self.dual = as_dual_of
Example #3
0
def coxeter_matrix(t):
    """
    Returns the Coxeter matrix of type t.
    
    EXAMPLES::
    
        sage: coxeter_matrix(['A', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 3]
        [2 2 3 1]
        sage: coxeter_matrix(['B', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 4]
        [2 2 4 1]
        sage: coxeter_matrix(['C', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 4]
        [2 2 4 1]
        sage: coxeter_matrix(['D', 4])
        [1 3 2 2]
        [3 1 3 3]
        [2 3 1 2]
        [2 3 2 1]
    
    ::
    
        sage: coxeter_matrix(['E', 6])
        [1 2 3 2 2 2]
        [2 1 2 3 2 2]
        [3 2 1 3 2 2]
        [2 3 3 1 3 2]
        [2 2 2 3 1 3]
        [2 2 2 2 3 1]
    
    ::
    
        sage: coxeter_matrix(['F', 4])
        [1 3 2 2]
        [3 1 4 2]
        [2 4 1 3]
        [2 2 3 1]
    
    ::
    
        sage: coxeter_matrix(['G', 2])
        [1 6]
        [6 1]
    """
    ct = CartanType(t)
    cf = coxeter_matrix_as_function(ct)
    index_set = ct.index_set()
    MS = MatrixSpace(ZZ, len(index_set))
    m = MS(0)
    for i in range(len(index_set)):
        for j in range(len(index_set)):
            m[i, j] = cf(index_set[i], index_set[j])
    return m
Example #4
0
def DynkinDiagram(*args):
    """
    INPUT:

     -  ``ct`` - a Cartan Type

    Returns a Dynkin diagram for type ct.
    
    
    The edge multiplicities are encoded as edge labels. This uses the
    convention in Kac / Fulton Harris, Representation theory / Wikipedia
    (http://en.wikipedia.org/wiki/Dynkin_diagram). That is for i != j::

       j --k--> i <==> a_ij = -k 
                  <==> -scalar(coroot[i], root[j]) = k
                  <==> multiple arrows point from the longer root 
                       to the shorter one
    
    EXAMPLES::
    
        sage: DynkinDiagram(['A', 4])
        O---O---O---O
        1   2   3   4
        A4

        sage: DynkinDiagram(['A',1],['A',1])
        O
        1
        O
        2
        A1xA1

        sage: R = RootSystem("A2xB2xF4")
        sage: DynkinDiagram(R)
        O---O
        1   2
        O=>=O
        3   4
        O---O=>=O---O
        5   6   7   8
        A2xB2xF4

    SEE ALSO: :func:`CartanType` for a general discussion on Cartan
    types and in particular node labeling conventions.
    """
    if len(args) == 0:
       return DynkinDiagram_class()
    ct = CartanType(*args)
    if hasattr(ct, "dynkin_diagram"):
        return ct.dynkin_diagram()
    else:
        raise ValueError, "Dynkin diagram data not yet hardcoded for type %s"%ct
Example #5
0
def DynkinDiagram(*args):
    """
    INPUT:

     -  ``ct`` - a Cartan Type

    Returns a Dynkin diagram for type ct.


    The edge multiplicities are encoded as edge labels. This uses the
    convention in Kac / Fulton Harris, Representation theory / Wikipedia
    (http://en.wikipedia.org/wiki/Dynkin_diagram). That is for i != j::

       j --k--> i <==> a_ij = -k
                  <==> -scalar(coroot[i], root[j]) = k
                  <==> multiple arrows point from the longer root
                       to the shorter one

    EXAMPLES::

        sage: DynkinDiagram(['A', 4])
        O---O---O---O
        1   2   3   4
        A4

        sage: DynkinDiagram(['A',1],['A',1])
        O
        1
        O
        2
        A1xA1

        sage: R = RootSystem("A2xB2xF4")
        sage: DynkinDiagram(R)
        O---O
        1   2
        O=>=O
        3   4
        O---O=>=O---O
        5   6   7   8
        A2xB2xF4

    SEE ALSO: :func:`CartanType` for a general discussion on Cartan
    types and in particular node labeling conventions.
    """
    if len(args) == 0:
       return DynkinDiagram_class()
    ct = CartanType(*args)
    if hasattr(ct, "dynkin_diagram"):
        return ct.dynkin_diagram()
    else:
        raise ValueError, "Dynkin diagram data not yet hardcoded for type %s"%ct
Example #6
0
    def ascii_art(self, label=lambda i: i, node=None):
        """
        Return a ascii art representation of the extended Dynkin diagram.

        EXAMPLES::

            sage: print CartanType(['C',5,1]).ascii_art(label = lambda x: x+2)
            O=>=O---O---O---O=<=O
            2   3   4   5   6   7

            sage: print CartanType(['C',3,1]).ascii_art()
            O=>=O---O=<=O
            0   1   2   3

            sage: print CartanType(['C',2,1]).ascii_art()
            O=>=O=<=O
            0   1   2

            sage: print CartanType(['C',1,1]).ascii_art()
            O<=>O
            0   1
        """
        if node is None:
            node = self._ascii_art_node
        n = self.n
        from cartan_type import CartanType
        if n == 1:
            return CartanType(["A", 1, 1]).ascii_art(label, node)
        ret = node(label(0)) + "=>=" + "---".join(
            node(label(i)) for i in range(1, n))
        ret += "=<=" + node(label(n)) + '\n'
        ret += "".join("{!s:4}".format(label(i)) for i in range(n + 1))
        return ret
Example #7
0
    def ascii_art(self, label = lambda x: x):
        """
        Returns a ascii art representation of the extended Dynkin diagram

        EXAMPLES::

            sage: print CartanType(['C',5,1]).ascii_art(label = lambda x: x+2)
            O=>=O---O---O---O=<=O
            2   3   4   5   6   7

            sage: print CartanType(['C',3,1]).ascii_art()
            O=>=O---O=<=O
            0   1   2   3

            sage: print CartanType(['C',2,1]).ascii_art()
            O=>=O=<=O
            0   1   2

            sage: print CartanType(['C',1,1]).ascii_art()
            O<=>O
            0   1
        """
        n = self.n
        from cartan_type import CartanType
        if n == 1:
            return CartanType(["A",1,1]).ascii_art(label)
        if self.global_options('mark_special_node') in ['printing', 'both']:
            special_str = self.global_options('special_node_str')
        else:
            special_str = 'O'
        ret = "%s=>=O"%special_str + (n-2)*"---O"+"=<=O\n%s   "%label(0)
        ret += "   ".join("%s"%label(i) for i in range(1,n+1))
        return ret
Example #8
0
def coxeter_matrix_as_function(t):
    """
    Returns the coxeter matrix associated to the Cartan type t.
    
    EXAMPLES::
    
        sage: from sage.combinat.root_system.coxeter_matrix import coxeter_matrix_as_function
        sage: f = coxeter_matrix_as_function(['A',4])
        sage: matrix([[f(i,j) for j in range(1,5)] for i in range(1,5)])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 3]
        [2 2 3 1]
    """
    a = CartanType(t).dynkin_diagram()
    scalarproducts_to_order = {
        0: 2,
        1: 3,
        2: 4,
        3: 6
        # 4 should be infinity
    }

    return lambda i, j: 1 if i == j else scalarproducts_to_order[a[i, j] * a[
        j, i]]
Example #9
0
    def ascii_art(self, label=lambda x: x):
        """
        Returns a ascii art representation of the extended Dynkin diagram

        EXAMPLES::

            sage: print CartanType(['C',5,1]).ascii_art(label = lambda x: x+2)
            O=>=O---O---O---O=<=O
            2   3   4   5   6   7

            sage: print CartanType(['C',3,1]).ascii_art()
            O=>=O---O=<=O
            0   1   2   3

            sage: print CartanType(['C',2,1]).ascii_art()
            O=>=O=<=O
            0   1   2

            sage: print CartanType(['C',1,1]).ascii_art()
            O<=>O
            0   1
        """
        n = self.n
        from cartan_type import CartanType
        if n == 1:
            return CartanType(["A", 1, 1]).ascii_art(label)
        ret = "O=>=O" + (n - 2) * "---O" + "=<=O\n%s   " % label(0)
        ret += "   ".join("%s" % label(i) for i in range(1, n + 1))
        return ret
Example #10
0
    def ascii_art(self, label=lambda x: x):
        """
        Returns a ascii art representation of the extended Dynkin diagram

        EXAMPLES::

            sage: print CartanType(['B',3,1]).ascii_art()
                O 0
                |
                |
            O---O=>=O
            1   2   3

            sage: print CartanType(['B',5,1]).ascii_art(label = lambda x: x+2)
                O 2
                |
                |
            O---O---O---O=>=O
            3   4   5   6   7

            sage: print CartanType(['B',2,1]).ascii_art(label = lambda x: x+2)
            O=>=O=<=O
            2   4   3
            sage: print CartanType(['B',1,1]).ascii_art(label = lambda x: x+2)
            O<=>O
            2   3
        """
        n = self.n
        from cartan_type import CartanType
        if n == 1:
            return CartanType(["A", 1, 1]).ascii_art(label)
        if n == 2:
            return CartanType(["C", 2, 1]).relabel({
                0: 0,
                1: 2,
                2: 1
            }).ascii_art(label)
        if self.global_options('mark_special_node') in ['printing', 'both']:
            special_str = self.global_options('special_node_str')
        else:
            special_str = 'O'
        ret = "    %s %s\n    |\n    |\n" % (special_str, label(0))
        ret += (n - 2) * "O---" + "O=>=O\n"
        ret += "   ".join("%s" % label(i) for i in range(1, n + 1))
        return ret
Example #11
0
def WeylDim(ct, coeffs):
    """
    The Weyl Dimension Formula.
    
    INPUT:
    
    
    -  ``type`` - a Cartan type
    
    -  ``coeffs`` - a list of nonnegative integers
    
    
    The length of the list must equal the rank type[1]. A dominant
    weight hwv is constructed by summing the fundamental weights with
    coefficients from this list. The dimension of the irreducible
    representation of the semisimple complex Lie algebra with highest
    weight vector hwv is returned.
    
    EXAMPLES:

    For `SO(7)`, the Cartan type is `B_3`, so::
    
        sage: WeylDim(['B',3],[1,0,0]) # standard representation of SO(7)
        7
        sage: WeylDim(['B',3],[0,1,0]) # exterior square
        21
        sage: WeylDim(['B',3],[0,0,1]) # spin representation of spin(7)
        8
        sage: WeylDim(['B',3],[1,0,1]) # sum of the first and third fundamental weights
        48
        sage: [WeylDim(['F',4],x) for x in [1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
        [52, 1274, 273, 26]
        sage: [WeylDim(['E', 6], x) for x in [0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 2], [0, 0, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1], [2, 0, 0, 0, 0, 0]]
        [1, 78, 27, 351, 351, 351, 27, 650, 351]
    """
    ct = CartanType(ct)
    lattice = RootSystem(ct).ambient_space()
    rank = ct.rank()
    fw = lattice.fundamental_weights()
    hwv = lattice.sum(coeffs[i] * fw[i + 1]
                      for i in range(min(rank, len(coeffs))))
    return lattice.weyl_dimension(hwv)
Example #12
0
def WeylDim(ct, coeffs):
    """
    The Weyl Dimension Formula.
    
    INPUT:
    
    
    -  ``type`` - a Cartan type
    
    -  ``coeffs`` - a list of nonnegative integers
    
    
    The length of the list must equal the rank type[1]. A dominant
    weight hwv is constructed by summing the fundamental weights with
    coefficients from this list. The dimension of the irreducible
    representation of the semisimple complex Lie algebra with highest
    weight vector hwv is returned.
    
    EXAMPLES:

    For `SO(7)`, the Cartan type is `B_3`, so::
    
        sage: WeylDim(['B',3],[1,0,0]) # standard representation of SO(7)
        7
        sage: WeylDim(['B',3],[0,1,0]) # exterior square
        21
        sage: WeylDim(['B',3],[0,0,1]) # spin representation of spin(7)
        8
        sage: WeylDim(['B',3],[1,0,1]) # sum of the first and third fundamental weights
        48
        sage: [WeylDim(['F',4],x) for x in [1,0,0,0],[0,1,0,0],[0,0,1,0],[0,0,0,1]]
        [52, 1274, 273, 26]
        sage: [WeylDim(['E', 6], x) for x in [0, 0, 0, 0, 0, 0], [0, 1, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 2], [0, 0, 0, 0, 1, 0], [0, 0, 1, 0, 0, 0], [1, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 1], [2, 0, 0, 0, 0, 0]]
        [1, 78, 27, 351, 351, 351, 27, 650, 351]
    """
    ct = CartanType(ct)
    lattice = RootSystem(ct).ambient_space()
    rank = ct.rank()
    fw = lattice.fundamental_weights()
    hwv = lattice.sum(coeffs[i]*fw[i+1] for i in range(min(rank, len(coeffs))))
    return lattice.weyl_dimension(hwv)
Example #13
0
    def ascii_art(self, label=lambda x: x):
        """
        Returns a ascii art representation of the extended Dynkin diagram

        EXAMPLES::

            sage: print CartanType(['B',3,1]).ascii_art()
                O 0
                |
                |
            O---O=>=O
            1   2   3

            sage: print CartanType(['B',5,1]).ascii_art(label = lambda x: x+2)
                O 2
                |
                |
            O---O---O---O=>=O
            3   4   5   6   7

            sage: print CartanType(['B',2,1]).ascii_art(label = lambda x: x+2)
            O=>=O=<=O
            2   4   3
            sage: print CartanType(['B',1,1]).ascii_art(label = lambda x: x+2)
            O<=>O
            2   3
        """
        n = self.n
        from cartan_type import CartanType
        if n == 1:
            return CartanType(["A", 1, 1]).ascii_art(label)
        if n == 2:
            return CartanType(["C", 2, 1]).relabel({
                0: 0,
                1: 2,
                2: 1
            }).ascii_art(label)
        ret = "    O %s\n    |\n    |\n" % label(0)
        ret += (n - 2) * "O---" + "O=>=O\n"
        ret += "   ".join("%s" % label(i) for i in range(1, n + 1))
        return ret
Example #14
0
def coxeter_matrix(t):
    """
    Returns the Coxeter matrix of type t.
    
    EXAMPLES::
    
        sage: coxeter_matrix(['A', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 3]
        [2 2 3 1]
        sage: coxeter_matrix(['B', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 4]
        [2 2 4 1]
        sage: coxeter_matrix(['C', 4])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 4]
        [2 2 4 1]
        sage: coxeter_matrix(['D', 4])
        [1 3 2 2]
        [3 1 3 3]
        [2 3 1 2]
        [2 3 2 1]
    
    ::
    
        sage: coxeter_matrix(['E', 6])
        [1 2 3 2 2 2]
        [2 1 2 3 2 2]
        [3 2 1 3 2 2]
        [2 3 3 1 3 2]
        [2 2 2 3 1 3]
        [2 2 2 2 3 1]
    
    ::
    
        sage: coxeter_matrix(['F', 4])
        [1 3 2 2]
        [3 1 4 2]
        [2 4 1 3]
        [2 2 3 1]
    
    ::
    
        sage: coxeter_matrix(['G', 2])
        [1 6]
        [6 1]
    """
    return CartanType(t).coxeter_matrix()
Example #15
0
    def __classcall__(cls, cartan_type, as_dual_of=None):
        """
        Straighten arguments to enable unique representation

        .. seealso:: :class:`UniqueRepresentation`

        TESTS::

            sage: RootSystem(["A",3]) is RootSystem(CartanType(["A",3]))
            True
            sage: RootSystem(["B",3], as_dual_of=None) is RootSystem("B3")
            True
        """
        return super(RootSystem, cls).__classcall__(cls, CartanType(cartan_type), as_dual_of)
Example #16
0
def coxeter_matrix_as_function(t):
    """
    Returns the coxeter matrix, as a function

    INPUT:

    - ``t`` -- a Cartan type

    EXAMPLES::

        sage: from sage.combinat.root_system.coxeter_matrix import coxeter_matrix_as_function
        sage: f = coxeter_matrix_as_function(['A',4])
        sage: matrix([[f(i,j) for j in range(1,5)] for i in range(1,5)])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 3]
        [2 2 3 1]
    """
    t = CartanType(t)
    m = t.coxeter_matrix()
    index_set = t.index_set()
    reverse = dict((index_set[i], i) for i in range(len(index_set)))
    return lambda i,j: m[reverse[i], reverse[j]]
Example #17
0
def coxeter_matrix_as_function(t):
    """
    Returns the Coxeter matrix, as a function

    INPUT:

    - ``t`` -- a Cartan type

    EXAMPLES::

        sage: from sage.combinat.root_system.coxeter_matrix import coxeter_matrix_as_function
        sage: f = coxeter_matrix_as_function(['A',4])
        sage: matrix([[f(i,j) for j in range(1,5)] for i in range(1,5)])
        [1 3 2 2]
        [3 1 3 2]
        [2 3 1 3]
        [2 2 3 1]
    """
    t = CartanType(t)
    m = t.coxeter_matrix()
    index_set = t.index_set()
    reverse = dict((index_set[i], i) for i in range(len(index_set)))
    return lambda i,j: m[reverse[i], reverse[j]]
Example #18
0
    def __init__(self, cartan_type, as_dual_of=None):
        """
        TESTS::

            sage: R = RootSystem(['A',3])
            sage: R
            Root system of type ['A', 3]
        """
        self._cartan_type = CartanType(cartan_type)

        # Duality
        # The root system can be defined as dual of another root system. This will
        # only affects the pretty printing
        if as_dual_of is None:
            self.dual_side = False
            # still fails for CartanType G2xA1
            try:
                self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            except Exception:
                pass
        else:
            self.dual_side = True
            self.dual = as_dual_of
Example #19
0
class RootSystem(UniqueRepresentation, SageObject):
    r"""
    A class for root systems.
    
    EXAMPLES:

    We construct the root system for type `B_3`::
    
        sage: R=RootSystem(['B',3]); R
        Root system of type ['B', 3]
    
    ``R`` models the root system abstractly. It comes equipped with various
    realizations of the root and weight lattices, where all computations
    take place. Let us play first with the root lattice::
    
        sage: space = R.root_lattice()
        sage: space
        Root lattice of the Root system of type ['B', 3]
    
    It is the free `\ZZ`-module
    `\bigoplus_i \ZZ.\alpha_i` spanned by the simple
    roots::
    
        sage: space.base_ring()
        Integer Ring
        sage: list(space.basis()) 
        [alpha[1], alpha[2], alpha[3]]
    
    Let us do some computations with the simple roots::
    
        sage: alpha = space.simple_roots()
        sage: alpha[1] + alpha[2]
        alpha[1] + alpha[2]
    
    There is a canonical pairing between the root lattice and the
    coroot lattice::
    
        sage: R.coroot_lattice()
        Coroot lattice of the Root system of type ['B', 3]
    
    We construct the simple coroots, and do some computations (see
    comments about duality below for some caveat)::
    
        sage: alphacheck = space.simple_coroots()
        sage: list(alphacheck)
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    We can carry over the same computations in any of the other
    realizations of the root lattice, like the root space
    `\bigoplus_i \QQ.\alpha_i`, the weight lattice
    `\bigoplus_i \ZZ.\Lambda_i`, the weight
    space `\bigoplus_i \QQ.\Lambda_i`. For example::
    
        sage: space = R.weight_space()
        sage: space
        Weight space over the Rational Field of the Root system of type ['B', 3]
    
    ::
    
        sage: space.base_ring()
        Rational Field
        sage: list(space.basis())
        [Lambda[1], Lambda[2], Lambda[3]]
    
    ::
    
        sage: alpha = space.simple_roots()
        sage: alpha[1] + alpha[2]
        Lambda[1] + Lambda[2] - 2*Lambda[3]
    
    The fundamental weights are the dual basis of the coroots::
    
        sage: Lambda = space.fundamental_weights()
        sage: Lambda[1]
        Lambda[1]
    
    ::
    
        sage: alphacheck = space.simple_coroots()
        sage: list(alphacheck)
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    ::
    
        sage: [Lambda[i].scalar(alphacheck[1]) for i in space.index_set()]
        [1, 0, 0]
        sage: [Lambda[i].scalar(alphacheck[2]) for i in space.index_set()]
        [0, 1, 0]
        sage: [Lambda[i].scalar(alphacheck[3]) for i in space.index_set()]
        [0, 0, 1]
    
    Let us use the simple reflections. In the weight space, they
    work as in the *number game*: firing the node `i` on an
    element `x` adds `c` times the simple root
    `\alpha_i`, where `c` is the coefficient of
    `i` in `x`::
    
        sage: s = space.simple_reflections()
        sage: Lambda[1].simple_reflection(1)
        -Lambda[1] + Lambda[2]
        sage: Lambda[2].simple_reflection(1)
        Lambda[2]
        sage: Lambda[3].simple_reflection(1)
        Lambda[3]
        sage: (-2*Lambda[1] + Lambda[2] + Lambda[3]).simple_reflection(1)
        2*Lambda[1] - Lambda[2] + Lambda[3]
    
    It can be convenient to manipulate the simple reflections
    themselves::
    
        sage: s = space.simple_reflections()
        sage: s[1](Lambda[1])
        -Lambda[1] + Lambda[2]
        sage: s[1](Lambda[2])
        Lambda[2]
        sage: s[1](Lambda[3])
        Lambda[3]
    
    The root system may also come equipped with an ambient space, that
    is a simultaneous realization of the weight lattice and the coroot
    lattice in a Euclidean vector space. This is implemented on a type
    by type basis, and is not always available. When the coefficients
    permit it, this is also available as an ambient lattice.
    
    TODO: Demo: signed permutations realization of type B
    
    The root system is aware of its dual root system::
    
        sage: R.dual
        Dual of root system of type ['B', 3]
    
    R.dual is really the root system of type `C_3`::
    
        sage: R.dual.cartan_type()
        ['C', 3]
    
    And the coroot lattice that we have been manipulating before is
    really implemented as the root lattice of the dual root system::
    
        sage: R.dual.root_lattice()
        Coroot lattice of the Root system of type ['B', 3]
    
    In particular, the coroots for the root lattice are in fact the
    roots of the coroot lattice::
    
        sage: list(R.root_lattice().simple_coroots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
        sage: list(R.coroot_lattice().simple_roots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
        sage: list(R.dual.root_lattice().simple_roots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    The coweight lattice and space are defined similarly. Note that, to
    limit confusion, all the output have been tweaked appropriately.

    .. seealso::

        - :mod:`sage.combinat.root_system`
        - :class:`RootSpace`
        - :class:`WeightSpace`
        - :class:`AmbientSpace`
        - :class:`~sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations`
        - :class:`~sage.combinat.root_system.weight_lattice_realizations.WeightLatticeRealizations`

    TESTS::

        sage: R = RootSystem(['C',3])
        sage: TestSuite(R).run()
        sage: L = R.ambient_space()
        sage: s = L.simple_reflections() # this used to break the testsuite below due to caching an unpicklable method
        sage: s = L.simple_projections() # todo: not implemented
        sage: TestSuite(L).run()
        sage: L = R.root_space()
        sage: s = L.simple_reflections()
        sage: TestSuite(L).run()

    ::

        sage: for T in CartanType.samples(crystalographic=True):  # long time (13s on sage.math, 2012)
        ...       TestSuite(RootSystem(T)).run()
    """

    @staticmethod
    def __classcall__(cls, cartan_type, as_dual_of=None):
        """
        Straighten arguments to enable unique representation

        .. seealso:: :class:`UniqueRepresentation`

        TESTS::

            sage: RootSystem(["A",3]) is RootSystem(CartanType(["A",3]))
            True
            sage: RootSystem(["B",3], as_dual_of=None) is RootSystem("B3")
            True
        """
        return super(RootSystem, cls).__classcall__(cls, CartanType(cartan_type), as_dual_of)

    def __init__(self, cartan_type, as_dual_of=None):
        """
        TESTS::
        
            sage: R = RootSystem(['A',3])
            sage: R
            Root system of type ['A', 3]
        """
        self._cartan_type = CartanType(cartan_type)

        # Duality
        # The root system can be defined as dual of another root system. This will
        # only affects the pretty printing
        if as_dual_of is None:
            self.dual_side = False
            self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            # still fails for CartanType G2xA1
            try:
                self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            except:
                pass
        else:
            self.dual_side = True
            self.dual = as_dual_of


    def _test_root_lattice_realizations(self, **options):
        """
        Runs tests on all the root lattice realizations of this root
        system.
        
        EXAMPLES::
        
            sage: RootSystem(["A",3])._test_root_lattice_realizations()

        See also :class:`TestSuite`.
        """
        tester = self._tester(**options)
        options.pop('tester', None)
        from sage.misc.sage_unittest import TestSuite
        TestSuite(self.root_lattice()).run(**options)
        TestSuite(self.root_space()).run(**options)
        TestSuite(self.weight_lattice()).run(**options)
        TestSuite(self.weight_space()).run(**options)
        if self.cartan_type().is_affine():
            TestSuite(self.weight_lattice(extended=True)).run(**options)
            TestSuite(self.weight_space(extended=True)).run(**options)
        if self.ambient_lattice() is not None:
            TestSuite(self.ambient_lattice()).run(**options)
        if self.ambient_space() is not None:
            TestSuite(self.ambient_space()).run(**options)

    def _repr_(self):
        """
        EXAMPLES::

            sage: RootSystem(['A',3])    # indirect doctest
            Root system of type ['A', 3]
            sage: RootSystem(['B',3]).dual    # indirect doctest
            Dual of root system of type ['B', 3]
        """
        if self.dual_side:
            return "Dual of root system of type %s"%self.dual.cartan_type()
        else:
            return "Root system of type %s"%self.cartan_type()

    def cartan_type(self):
        """
        Returns the Cartan type of the root system.
        
        EXAMPLES::
        
            sage: R = RootSystem(['A',3])
            sage: R.cartan_type()
            ['A', 3]
        """
        return self._cartan_type

    @cached_method
    def dynkin_diagram(self):
        """
        Returns the Dynkin diagram of the root system.
        
        EXAMPLES::
        
            sage: R = RootSystem(['A',3])
            sage: R.dynkin_diagram()
            O---O---O
            1   2   3
            A3
        """
        return self.cartan_type().dynkin_diagram()

    @cached_method
    def cartan_matrix(self):
        """
        EXAMPLES::
        
            sage: RootSystem(['A',3]).cartan_matrix()
            [ 2 -1  0]
            [-1  2 -1]
            [ 0 -1  2]
        """
        return self.cartan_type().cartan_matrix()

    def index_set(self):
        """
        EXAMPLES::
        
            sage: RootSystem(['A',3]).index_set()
            [1, 2, 3]
        """
        return self.cartan_type().index_set()

    @cached_method
    def is_finite(self):
        """
        Returns True if self is a finite root system.
        
        EXAMPLES::
        
            sage: RootSystem(["A",3]).is_finite()
            True
            sage: RootSystem(["A",3,1]).is_finite()
            False
        """
        return self.cartan_type().is_finite()

    @cached_method
    def is_irreducible(self):
        """
        Returns True if self is an irreducible root system.
        
        EXAMPLES::
        
            sage: RootSystem(['A', 3]).is_irreducible()
            True
            sage: RootSystem("A2xB2").is_irreducible()
            False
        """
        return self.cartan_type().is_irreducible()

    def __cmp__(self, other):
        """
        EXAMPLES::
        
            sage: r1 = RootSystem(['A',3])
            sage: r2 = RootSystem(['B',3])
            sage: r1 == r1
            True
            sage: r1 == r2
            False
        """
        if self.__class__ != other.__class__:
            return cmp(self.__class__, other.__class__)
        if self._cartan_type != other._cartan_type:
            return cmp(self._cartan_type, other._cartan_type)
        return 0

    def root_lattice(self):
        """
        Returns the root lattice associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).root_lattice()
            Root lattice of the Root system of type ['A', 3]
        """
        return self.root_space(ZZ)

    @cached_method
    def root_space(self, base_ring=QQ):
        """
        Returns the root space associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).root_space()
            Root space over the Rational Field of the Root system of type ['A', 3]
        """
        return RootSpace(self, base_ring)

    def root_poset(self, restricted=False, facade=False):
        r"""
        Returns the (restricted) root poset associated to ``self``.

        The elements are given by the positive roots (resp. non-simple, positive roots), and
        `\alpha \leq \beta` iff `\beta - \alpha` is a non-negative linear combination of simple roots.

        INPUT:

        - ``restricted`` -- (default:False) if True, only non-simple roots are considered.
        - ``facade`` -- (default:False) passes facade option to the poset generator.

        EXAMPLES::

            sage: Phi = RootSystem(['A',2]).root_poset(); Phi
            Finite poset containing 3 elements
            sage: Phi.cover_relations()
            [[alpha[1], alpha[1] + alpha[2]], [alpha[2], alpha[1] + alpha[2]]]

            sage: Phi = RootSystem(['A',3]).root_poset(restricted=True); Phi
            Finite poset containing 3 elements
            sage: Phi.cover_relations()
            [[alpha[1] + alpha[2], alpha[1] + alpha[2] + alpha[3]], [alpha[2] + alpha[3], alpha[1] + alpha[2] + alpha[3]]]

            sage: Phi = RootSystem(['B',2]).root_poset(); Phi
            Finite poset containing 4 elements
            sage: Phi.cover_relations()
            [[alpha[1], alpha[1] + alpha[2]], [alpha[2], alpha[1] + alpha[2]], [alpha[1] + alpha[2], alpha[1] + 2*alpha[2]]]
        """
        return self.root_lattice().root_poset(restricted=restricted,facade=facade)

    def coroot_lattice(self):
        """
        Returns the coroot lattice associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).coroot_lattice()
            Coroot lattice of the Root system of type ['A', 3]
        """
        return self.dual.root_lattice()
    
    def coroot_space(self, base_ring=QQ):
        """
        Returns the coroot space associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).coroot_space()
            Coroot space over the Rational Field of the Root system of type ['A', 3]
        """
        return self.dual.root_space(base_ring)

    @cached_method
    def weight_lattice(self, extended = False):
        """
        Returns the weight lattice associated to self.

        .. see also::

            - :meth:`weight_space`
            - :meth:`coweight_space`, :meth:`coweight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).weight_lattice()
            Weight lattice of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).weight_space(extended = True)
            Extended weight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return WeightSpace(self, ZZ, extended = extended)

    @cached_method
    def weight_space(self, base_ring=QQ, extended = False):
        """
        Returns the weight space associated to self.

        .. see also::

            - :meth:`weight_lattice`
            - :meth:`coweight_space`, :meth:`coweight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).weight_space()
            Weight space over the Rational Field of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).weight_space(extended = True)
            Extended weight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return WeightSpace(self, base_ring, extended = extended)

    def coweight_lattice(self, extended = False):
        """
        Returns the coweight lattice associated to self.

        This is the weight lattice of the dual root system.

        .. see also::

            - :meth:`coweight_space`
            - :meth:`weight_space`, :meth:`weight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).coweight_lattice()
            Coweight lattice of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).coweight_lattice(extended = True)
            Extended coweight lattice of the Root system of type ['A', 3, 1]
        """
        return self.dual.weight_lattice(extended = extended)

    def coweight_space(self, base_ring=QQ, extended = False):
        """
        Returns the coweight space associated to self.

        This is the weight space of the dual root system.

        .. see also::

            - :meth:`coweight_lattice`
            - :meth:`weight_space`, :meth:`weight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).coweight_space()
            Coweight space over the Rational Field of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).coweight_space(extended=True)
            Extended coweight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return self.dual.weight_space(base_ring, extended = extended)


    def ambient_lattice(self):
        r"""
        Returns the usual ambient lattice for this root_system, if it
        exists and is implemented, and None otherwise. This is a
        `\ZZ`-module, endowed with its canonical euclidean
        scalar product, which embeds simultaneously the root lattice
        and the coroot lattice (what about the weight lattice?)
        
        EXAMPLES::
        
            sage: RootSystem(['A',4]).ambient_lattice()
            Ambient lattice of the Root system of type ['A', 4]
        
        ::
        
            sage: RootSystem(['B',4]).ambient_lattice()
            sage: RootSystem(['C',4]).ambient_lattice()
            sage: RootSystem(['D',4]).ambient_lattice()
            sage: RootSystem(['E',6]).ambient_lattice()
            sage: RootSystem(['F',4]).ambient_lattice()
            sage: RootSystem(['G',2]).ambient_lattice()
        """
        return self.ambient_space(ZZ)

    @cached_method
    def ambient_space(self, base_ring=QQ):
        r"""
        Returns the usual ambient space for this root_system, if it is
        implemented, and None otherwise. This is a `\QQ`-module, endowed with
        its canonical euclidean scalar product, which embeds simultaneously
        the root lattice and the coroot lattice (what about the weight
        lattice?). An alternative base ring can be provided as an option;
        it must contain the smallest ring over which the ambient space can
        be defined (`\ZZ` or `\QQ`, depending on the type).
        
        EXAMPLES::
        
            sage: RootSystem(['A',4]).ambient_space()
            Ambient space of the Root system of type ['A', 4]
        
        ::
        
            sage: RootSystem(['B',4]).ambient_space()
            Ambient space of the Root system of type ['B', 4]
        
        ::
        
            sage: RootSystem(['C',4]).ambient_space()
            Ambient space of the Root system of type ['C', 4]
        
        ::
        
            sage: RootSystem(['D',4]).ambient_space()
            Ambient space of the Root system of type ['D', 4]
        
        ::
        
            sage: RootSystem(['E',6]).ambient_space()
            Ambient space of the Root system of type ['E', 6]
        
        ::
        
            sage: RootSystem(['F',4]).ambient_space()
            Ambient space of the Root system of type ['F', 4]
        
        ::
        
            sage: RootSystem(['G',2]).ambient_space()
            Ambient space of the Root system of type ['G', 2]
        """
        # Intention: check that the ambient_space is implemented and that
        # base_ring contains the smallest base ring for this ambient space
        if not hasattr(self.cartan_type(),"AmbientSpace"):
            return None
        AmbientSpace = self.cartan_type().AmbientSpace
        if base_ring == ZZ and AmbientSpace.smallest_base_ring() == QQ:
            return None
        return AmbientSpace(self, base_ring)
Example #20
0
class RootSystem(UniqueRepresentation, SageObject):
    r"""
    A class for root systems.
    
    EXAMPLES:

    We construct the root system for type `B_3`::
    
        sage: R=RootSystem(['B',3]); R
        Root system of type ['B', 3]
    
    ``R`` models the root system abstractly. It comes equipped with various
    realizations of the root and weight lattices, where all computations
    take place. Let us play first with the root lattice::
    
        sage: space = R.root_lattice()
        sage: space
        Root lattice of the Root system of type ['B', 3]
    
    This is the free `\ZZ`-module `\bigoplus_i \ZZ.\alpha_i` spanned
    by the simple roots::
    
        sage: space.base_ring()
        Integer Ring
        sage: list(space.basis()) 
        [alpha[1], alpha[2], alpha[3]]
    
    Let us do some computations with the simple roots::
    
        sage: alpha = space.simple_roots()
        sage: alpha[1] + alpha[2]
        alpha[1] + alpha[2]
    
    There is a canonical pairing between the root lattice and the
    coroot lattice::
    
        sage: R.coroot_lattice()
        Coroot lattice of the Root system of type ['B', 3]
    
    We construct the simple coroots, and do some computations (see
    comments about duality below for some caveat)::
    
        sage: alphacheck = space.simple_coroots()
        sage: list(alphacheck)
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    We can carry over the same computations in any of the other
    realizations of the root lattice, like the root space
    `\bigoplus_i \QQ.\alpha_i`, the weight lattice
    `\bigoplus_i \ZZ.\Lambda_i`, the weight
    space `\bigoplus_i \QQ.\Lambda_i`. For example::
    
        sage: space = R.weight_space()
        sage: space
        Weight space over the Rational Field of the Root system of type ['B', 3]
    
    ::
    
        sage: space.base_ring()
        Rational Field
        sage: list(space.basis())
        [Lambda[1], Lambda[2], Lambda[3]]
    
    ::
    
        sage: alpha = space.simple_roots()
        sage: alpha[1] + alpha[2]
        Lambda[1] + Lambda[2] - 2*Lambda[3]
    
    The fundamental weights are the dual basis of the coroots::
    
        sage: Lambda = space.fundamental_weights()
        sage: Lambda[1]
        Lambda[1]
    
    ::
    
        sage: alphacheck = space.simple_coroots()
        sage: list(alphacheck)
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    ::
    
        sage: [Lambda[i].scalar(alphacheck[1]) for i in space.index_set()]
        [1, 0, 0]
        sage: [Lambda[i].scalar(alphacheck[2]) for i in space.index_set()]
        [0, 1, 0]
        sage: [Lambda[i].scalar(alphacheck[3]) for i in space.index_set()]
        [0, 0, 1]
    
    Let us use the simple reflections. In the weight space, they
    work as in the *number game*: firing the node `i` on an
    element `x` adds `c` times the simple root
    `\alpha_i`, where `c` is the coefficient of
    `i` in `x`::
    
        sage: s = space.simple_reflections()
        sage: Lambda[1].simple_reflection(1)
        -Lambda[1] + Lambda[2]
        sage: Lambda[2].simple_reflection(1)
        Lambda[2]
        sage: Lambda[3].simple_reflection(1)
        Lambda[3]
        sage: (-2*Lambda[1] + Lambda[2] + Lambda[3]).simple_reflection(1)
        2*Lambda[1] - Lambda[2] + Lambda[3]
    
    It can be convenient to manipulate the simple reflections
    themselves::
    
        sage: s = space.simple_reflections()
        sage: s[1](Lambda[1])
        -Lambda[1] + Lambda[2]
        sage: s[1](Lambda[2])
        Lambda[2]
        sage: s[1](Lambda[3])
        Lambda[3]

    .. RUBRIC:: Ambient spaces

    The root system may also come equipped with an ambient space.
    This is a `\QQ`-module, endowed with its canonical Euclidean
    scalar product, which admits simultaneous embeddings of the
    (extended) weight and the (extended) coweight lattice, and
    therefore the root and the coroot lattice. This is implemented on
    a type by type basis for the finite crystalographic root systems
    following Bourbaki's conventions and is extended to the affine
    cases. Coefficients permitting, this is also available as an
    ambient lattice.

    .. SEEALSO:: :meth:`ambient_space` and :meth:`ambient_lattice` for details

    In finite type `A`, we recover the natural representation of the
    symmetric group as group of permutation matrices::

        sage: RootSystem(["A",2]).ambient_space().weyl_group().simple_reflections()
        Finite family {1: [0 1 0]
                          [1 0 0]
                          [0 0 1],
                       2: [1 0 0]
                          [0 0 1]
                          [0 1 0]}

    In type `B`, `C`, and `D`, we recover the natural representation
    of the Weyl group as groups of signed permutation matrices::

        sage: RootSystem(["B",3]).ambient_space().weyl_group().simple_reflections()
        Finite family {1: [0 1 0]
                          [1 0 0]
                          [0 0 1],
                       2: [1 0 0]
                          [0 0 1]
                          [0 1 0],
                       3: [ 1  0  0]
                          [ 0  1  0]
                          [ 0  0 -1]}

    In (untwisted) affine types `A`, ..., `D`, one can recover from
    the ambient space the affine permutation representation, in window
    notation. Let us consider the ambient space for affine type `A`::

        sage: L = RootSystem(["A",2,1]).ambient_space(); L
        Ambient space of the Root system of type ['A', 2, 1]

    Define the "identity" by an appropriate vector at level -3::

        sage: e = L.basis(); Lambda = L.fundamental_weights()
        sage: id = e[0] + 2*e[1] + 3*e[2]  - 3*Lambda[0]

    The corresponding permutation is obtained by projecting it onto
    the classical ambient space::

        sage: L.classical()
        Ambient space of the Root system of type ['A', 2]
        sage: L.classical()(id)
        (1, 2, 3)

    Here is the orbit of the identity under the action of the finite
    group::

        sage: W = L.weyl_group()
        sage: S3 = [ w.action(id) for w in W.classical() ]
        sage: [L.classical()(x) for x in S3]
        [(1, 2, 3), (2, 1, 3), (3, 1, 2), (3, 2, 1), (1, 3, 2), (2, 3, 1)]

    And the action of `s_0` on these yields::

        sage: s = W.simple_reflections()
        sage: [L.classical()(s[0].action(x)) for x in S3]
        [(0, 2, 4), (0, 1, 5), (-1, 1, 6), (-2, 2, 6), (-1, 3, 4), (-2, 3, 5)]

    .. RUBRIC:: Dual root systems

    The root system is aware of its dual root system::
    
        sage: R.dual
        Dual of root system of type ['B', 3]
    
    R.dual is really the root system of type `C_3`::
    
        sage: R.dual.cartan_type()
        ['C', 3]
    
    And the coroot lattice that we have been manipulating before is
    really implemented as the root lattice of the dual root system::
    
        sage: R.dual.root_lattice()
        Coroot lattice of the Root system of type ['B', 3]
    
    In particular, the coroots for the root lattice are in fact the
    roots of the coroot lattice::
    
        sage: list(R.root_lattice().simple_coroots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
        sage: list(R.coroot_lattice().simple_roots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
        sage: list(R.dual.root_lattice().simple_roots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    The coweight lattice and space are defined similarly. Note that, to
    limit confusion, all the output have been tweaked appropriately.

    .. seealso::

        - :mod:`sage.combinat.root_system`
        - :class:`RootSpace`
        - :class:`WeightSpace`
        - :class:`AmbientSpace`
        - :class:`~sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations`
        - :class:`~sage.combinat.root_system.weight_lattice_realizations.WeightLatticeRealizations`

    TESTS::

        sage: R = RootSystem(['C',3])
        sage: TestSuite(R).run()
        sage: L = R.ambient_space()
        sage: s = L.simple_reflections() # this used to break the testsuite below due to caching an unpicklable method
        sage: s = L.simple_projections() # todo: not implemented
        sage: TestSuite(L).run()
        sage: L = R.root_space()
        sage: s = L.simple_reflections()
        sage: TestSuite(L).run()

    ::

        sage: for T in CartanType.samples(crystalographic=True):  # long time (13s on sage.math, 2012)
        ...       TestSuite(RootSystem(T)).run()
    """

    @staticmethod
    def __classcall__(cls, cartan_type, as_dual_of=None):
        """
        Straighten arguments to enable unique representation

        .. seealso:: :class:`UniqueRepresentation`

        TESTS::

            sage: RootSystem(["A",3]) is RootSystem(CartanType(["A",3]))
            True
            sage: RootSystem(["B",3], as_dual_of=None) is RootSystem("B3")
            True
        """
        return super(RootSystem, cls).__classcall__(cls, CartanType(cartan_type), as_dual_of)

    def __init__(self, cartan_type, as_dual_of=None):
        """
        TESTS::
        
            sage: R = RootSystem(['A',3])
            sage: R
            Root system of type ['A', 3]
        """
        self._cartan_type = CartanType(cartan_type)

        # Duality
        # The root system can be defined as dual of another root system. This will
        # only affects the pretty printing
        if as_dual_of is None:
            self.dual_side = False
            self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            # still fails for CartanType G2xA1
            try:
                self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            except StandardError:
                pass
        else:
            self.dual_side = True
            self.dual = as_dual_of


    def _test_root_lattice_realizations(self, **options):
        """
        Runs tests on all the root lattice realizations of this root
        system.
        
        EXAMPLES::
        
            sage: RootSystem(["A",3])._test_root_lattice_realizations()

        See also :class:`TestSuite`.
        """
        tester = self._tester(**options)
        options.pop('tester', None)
        from sage.misc.sage_unittest import TestSuite
        TestSuite(self.root_lattice()).run(**options)
        TestSuite(self.root_space()).run(**options)
        TestSuite(self.weight_lattice()).run(**options)
        TestSuite(self.weight_space()).run(**options)
        if self.cartan_type().is_affine():
            TestSuite(self.weight_lattice(extended=True)).run(**options)
            TestSuite(self.weight_space(extended=True)).run(**options)
        if self.ambient_lattice() is not None:
            TestSuite(self.ambient_lattice()).run(**options)
        if self.ambient_space() is not None:
            TestSuite(self.ambient_space()).run(**options)

    def _repr_(self):
        """
        EXAMPLES::

            sage: RootSystem(['A',3])    # indirect doctest
            Root system of type ['A', 3]
            sage: RootSystem(['B',3]).dual    # indirect doctest
            Dual of root system of type ['B', 3]
        """
        if self.dual_side:
            return "Dual of root system of type %s"%self.dual.cartan_type()
        else:
            return "Root system of type %s"%self.cartan_type()

    def cartan_type(self):
        """
        Returns the Cartan type of the root system.
        
        EXAMPLES::
        
            sage: R = RootSystem(['A',3])
            sage: R.cartan_type()
            ['A', 3]
        """
        return self._cartan_type

    @cached_method
    def dynkin_diagram(self):
        """
        Returns the Dynkin diagram of the root system.
        
        EXAMPLES::
        
            sage: R = RootSystem(['A',3])
            sage: R.dynkin_diagram()
            O---O---O
            1   2   3
            A3
        """
        return self.cartan_type().dynkin_diagram()

    @cached_method
    def cartan_matrix(self):
        """
        EXAMPLES::
        
            sage: RootSystem(['A',3]).cartan_matrix()
            [ 2 -1  0]
            [-1  2 -1]
            [ 0 -1  2]
        """
        return self.cartan_type().cartan_matrix()

    def index_set(self):
        """
        EXAMPLES::
        
            sage: RootSystem(['A',3]).index_set()
            [1, 2, 3]
        """
        return self.cartan_type().index_set()

    @cached_method
    def is_finite(self):
        """
        Returns True if self is a finite root system.
        
        EXAMPLES::
        
            sage: RootSystem(["A",3]).is_finite()
            True
            sage: RootSystem(["A",3,1]).is_finite()
            False
        """
        return self.cartan_type().is_finite()

    @cached_method
    def is_irreducible(self):
        """
        Returns True if self is an irreducible root system.
        
        EXAMPLES::
        
            sage: RootSystem(['A', 3]).is_irreducible()
            True
            sage: RootSystem("A2xB2").is_irreducible()
            False
        """
        return self.cartan_type().is_irreducible()

    def __cmp__(self, other):
        """
        EXAMPLES::
        
            sage: r1 = RootSystem(['A',3])
            sage: r2 = RootSystem(['B',3])
            sage: r1 == r1
            True
            sage: r1 == r2
            False
        """
        if self.__class__ != other.__class__:
            return cmp(self.__class__, other.__class__)
        if self._cartan_type != other._cartan_type:
            return cmp(self._cartan_type, other._cartan_type)
        return 0

    def root_lattice(self):
        """
        Returns the root lattice associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).root_lattice()
            Root lattice of the Root system of type ['A', 3]
        """
        return self.root_space(ZZ)

    @cached_method
    def root_space(self, base_ring=QQ):
        """
        Returns the root space associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).root_space()
            Root space over the Rational Field of the Root system of type ['A', 3]
        """
        return RootSpace(self, base_ring)

    def root_poset(self, restricted=False, facade=False):
        r"""
        Returns the (restricted) root poset associated to ``self``.

        The elements are given by the positive roots (resp. non-simple, positive roots), and
        `\alpha \leq \beta` iff `\beta - \alpha` is a non-negative linear combination of simple roots.

        INPUT:

        - ``restricted`` -- (default:False) if True, only non-simple roots are considered.
        - ``facade`` -- (default:False) passes facade option to the poset generator.

        EXAMPLES::

            sage: Phi = RootSystem(['A',2]).root_poset(); Phi
            Finite poset containing 3 elements
            sage: Phi.cover_relations()
            [[alpha[1], alpha[1] + alpha[2]], [alpha[2], alpha[1] + alpha[2]]]

            sage: Phi = RootSystem(['A',3]).root_poset(restricted=True); Phi
            Finite poset containing 3 elements
            sage: Phi.cover_relations()
            [[alpha[1] + alpha[2], alpha[1] + alpha[2] + alpha[3]], [alpha[2] + alpha[3], alpha[1] + alpha[2] + alpha[3]]]

            sage: Phi = RootSystem(['B',2]).root_poset(); Phi
            Finite poset containing 4 elements
            sage: Phi.cover_relations()
            [[alpha[1], alpha[1] + alpha[2]], [alpha[2], alpha[1] + alpha[2]], [alpha[1] + alpha[2], alpha[1] + 2*alpha[2]]]
        """
        return self.root_lattice().root_poset(restricted=restricted,facade=facade)

    def coroot_lattice(self):
        """
        Returns the coroot lattice associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).coroot_lattice()
            Coroot lattice of the Root system of type ['A', 3]
        """
        return self.dual.root_lattice()
    
    def coroot_space(self, base_ring=QQ):
        """
        Returns the coroot space associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).coroot_space()
            Coroot space over the Rational Field of the Root system of type ['A', 3]
        """
        return self.dual.root_space(base_ring)

    @cached_method
    def weight_lattice(self, extended = False):
        """
        Returns the weight lattice associated to self.

        .. see also::

            - :meth:`weight_space`
            - :meth:`coweight_space`, :meth:`coweight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).weight_lattice()
            Weight lattice of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).weight_space(extended = True)
            Extended weight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return WeightSpace(self, ZZ, extended = extended)

    @cached_method
    def weight_space(self, base_ring=QQ, extended = False):
        """
        Returns the weight space associated to self.

        .. see also::

            - :meth:`weight_lattice`
            - :meth:`coweight_space`, :meth:`coweight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).weight_space()
            Weight space over the Rational Field of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).weight_space(extended = True)
            Extended weight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return WeightSpace(self, base_ring, extended = extended)

    def coweight_lattice(self, extended = False):
        """
        Returns the coweight lattice associated to self.

        This is the weight lattice of the dual root system.

        .. see also::

            - :meth:`coweight_space`
            - :meth:`weight_space`, :meth:`weight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).coweight_lattice()
            Coweight lattice of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).coweight_lattice(extended = True)
            Extended coweight lattice of the Root system of type ['A', 3, 1]
        """
        return self.dual.weight_lattice(extended = extended)

    def coweight_space(self, base_ring=QQ, extended = False):
        """
        Returns the coweight space associated to self.

        This is the weight space of the dual root system.

        .. see also::

            - :meth:`coweight_lattice`
            - :meth:`weight_space`, :meth:`weight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).coweight_space()
            Coweight space over the Rational Field of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).coweight_space(extended=True)
            Extended coweight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return self.dual.weight_space(base_ring, extended = extended)


    def ambient_lattice(self):
        r"""
        Return the ambient lattice for this root_system.

        This is the ambient space, over `\ZZ`.

        .. SEEALSO::

            - :meth:`ambient_space`
            - :meth:`root_lattice`
            - :meth:`weight_lattice`

        EXAMPLES::

            sage: RootSystem(['A',4]).ambient_lattice()
            Ambient lattice of the Root system of type ['A', 4]
            sage: RootSystem(['A',4,1]).ambient_lattice()
            Ambient lattice of the Root system of type ['A', 4, 1]

        Except in type A, only an ambient space can be realized::

            sage: RootSystem(['B',4]).ambient_lattice()
            sage: RootSystem(['C',4]).ambient_lattice()
            sage: RootSystem(['D',4]).ambient_lattice()
            sage: RootSystem(['E',6]).ambient_lattice()
            sage: RootSystem(['F',4]).ambient_lattice()
            sage: RootSystem(['G',2]).ambient_lattice()
        """
        return self.ambient_space(ZZ)

    @cached_method
    def ambient_space(self, base_ring=QQ):
        r"""
        Return the usual ambient space for this root_system.

        INPUT:

        - ``base_ring`` -- a base ring (default: `\QQ`)

        This is a ``base_ring``-module, endowed with its canonical
        Euclidean scalar product, which admits simultaneous embeddings
        into the weight and the coweight lattice, and therefore the
        root and the coroot lattice, and preserves scalar products
        between elements of the coroot lattice and elements of the
        root or weight lattice (and dually).

        There is no mechanical way to define the ambient space just
        from the Cartan matrix. Instead is is constructed from hard
        coded type by type data, according to the usual Bourbaki
        conventions. Such data is provided for all the finite
        (crystalographic) types. From this data, ambient spaces can be
        built as well for dual types, reducible types and affine
        types. When no data is available, or if the base ring is not
        large enough, None is returned.

        .. WARNING:: for affine types

        .. SEEALSO::

            - The section on ambient spaces in :class:`RootSystem`
            - :meth:`ambient_lattice`
            - :class:`~sage.combinat.root_system.ambient_space.AmbientSpace`
            - :class:`~sage.combinat.root_system.ambient_space.type_affine.AmbientSpace`
            - :meth:`root_space`
            - :meth:`weight:space`

        EXAMPLES::
        
            sage: RootSystem(['A',4]).ambient_space()
            Ambient space of the Root system of type ['A', 4]
        
        ::
        
            sage: RootSystem(['B',4]).ambient_space()
            Ambient space of the Root system of type ['B', 4]
        
        ::
        
            sage: RootSystem(['C',4]).ambient_space()
            Ambient space of the Root system of type ['C', 4]
        
        ::
        
            sage: RootSystem(['D',4]).ambient_space()
            Ambient space of the Root system of type ['D', 4]
        
        ::
        
            sage: RootSystem(['E',6]).ambient_space()
            Ambient space of the Root system of type ['E', 6]
        
        ::
        
            sage: RootSystem(['F',4]).ambient_space()
            Ambient space of the Root system of type ['F', 4]
        
        ::
        
            sage: RootSystem(['G',2]).ambient_space()
            Ambient space of the Root system of type ['G', 2]

        An alternative base ring can be provided as an option::

            sage: e = RootSystem(['B',3]).ambient_space(RR)
            sage: TestSuite(e).run()

        It should contain the smallest ring over which the ambient
        space can be defined (`\ZZ` in type `A` or `\QQ` otherwise).
        Otherwise ``None`` is returned::

            sage: RootSystem(['B',2]).ambient_space(ZZ)

        The base ring should also be totally ordered. In practice,
        only `\ZZ` and `\QQ` are really supported at this point, but
        you are welcome to experiment::

            sage: e = RootSystem(['G',2]).ambient_space(RR)
            sage: TestSuite(e).run()
            Failure in _test_root_lattice_realization:
            Traceback (most recent call last):
            ...
            AssertionError: 2.00000000000000 != 2.00000000000000
            ------------------------------------------------------------
            The following tests failed: _test_root_lattice_realization
        """
        if not hasattr(self.cartan_type(),"AmbientSpace"):
            return None
        AmbientSpace = self.cartan_type().AmbientSpace
        if not base_ring.has_coerce_map_from(AmbientSpace.smallest_base_ring(self.cartan_type())):
            return None
        return AmbientSpace(self, base_ring)
Example #21
0
def DynkinDiagram(*args):
    r"""
    Return a Dynkin diagram for type ``ct``.

    INPUT:

    -  ``ct`` -- a Cartan Type

    The edge multiplicities are encoded as edge labels. This uses the
    convention in Hong and Kang, Kac, Fulton Harris, and crystals. This is the
    **opposite** convention in Bourbaki and Wikipedia's Dynkin diagram
    (:wikipedia:`Dynkin_diagram`). That is for `i \neq j`::

       i <--k-- j <==> a_ij = -k
                  <==> -scalar(coroot[i], root[j]) = k
                  <==> multiple arrows point from the longer root
                       to the shorter one

    For example, in type `C_2`, we have::

        sage: C2 = DynkinDiagram(['C',2]); C2
        O=<=O
        1   2
        C2
        sage: C2.cartan_matrix()
        [ 2 -2]
        [-1  2]

    However Bourbaki would have the Cartan matrix as:

    .. MATH::

        \begin{bmatrix}
        2 & -1 \\
        -2 & 2
        \end{bmatrix}.

    EXAMPLES::

        sage: DynkinDiagram(['A', 4])
        O---O---O---O
        1   2   3   4
        A4

        sage: DynkinDiagram(['A',1],['A',1])
        O
        1
        O
        2
        A1xA1

        sage: R = RootSystem("A2xB2xF4")
        sage: DynkinDiagram(R)
        O---O
        1   2
        O=>=O
        3   4
        O---O=>=O---O
        5   6   7   8
        A2xB2xF4

    .. SEEALSO::

        :func:`CartanType` for a general discussion on Cartan
        types and in particular node labeling conventions.
    """
    if len(args) == 0:
        return DynkinDiagram_class()
    ct = CartanType(*args)
    if hasattr(ct, "dynkin_diagram"):
        return ct.dynkin_diagram()
    else:
        raise ValueError, "Dynkin diagram data not yet hardcoded for type %s" % ct
Example #22
0
class RootSystem(UniqueRepresentation, SageObject):
    r"""
    A class for root systems.

    EXAMPLES:

    We construct the root system for type `B_3`::

        sage: R=RootSystem(['B',3]); R
        Root system of type ['B', 3]

    ``R`` models the root system abstractly. It comes equipped with various
    realizations of the root and weight lattices, where all computations
    take place. Let us play first with the root lattice::

        sage: space = R.root_lattice()
        sage: space
        Root lattice of the Root system of type ['B', 3]

    This is the free `\ZZ`-module `\bigoplus_i \ZZ.\alpha_i` spanned
    by the simple roots::

        sage: space.base_ring()
        Integer Ring
        sage: list(space.basis())
        [alpha[1], alpha[2], alpha[3]]

    Let us do some computations with the simple roots::

        sage: alpha = space.simple_roots()
        sage: alpha[1] + alpha[2]
        alpha[1] + alpha[2]

    There is a canonical pairing between the root lattice and the
    coroot lattice::

        sage: R.coroot_lattice()
        Coroot lattice of the Root system of type ['B', 3]

    We construct the simple coroots, and do some computations (see
    comments about duality below for some caveat)::

        sage: alphacheck = space.simple_coroots()
        sage: list(alphacheck)
        [alphacheck[1], alphacheck[2], alphacheck[3]]

    We can carry over the same computations in any of the other
    realizations of the root lattice, like the root space
    `\bigoplus_i \QQ.\alpha_i`, the weight lattice
    `\bigoplus_i \ZZ.\Lambda_i`, the weight
    space `\bigoplus_i \QQ.\Lambda_i`. For example::

        sage: space = R.weight_space()
        sage: space
        Weight space over the Rational Field of the Root system of type ['B', 3]

    ::

        sage: space.base_ring()
        Rational Field
        sage: list(space.basis())
        [Lambda[1], Lambda[2], Lambda[3]]

    ::

        sage: alpha = space.simple_roots()
        sage: alpha[1] + alpha[2]
        Lambda[1] + Lambda[2] - 2*Lambda[3]

    The fundamental weights are the dual basis of the coroots::

        sage: Lambda = space.fundamental_weights()
        sage: Lambda[1]
        Lambda[1]

    ::

        sage: alphacheck = space.simple_coroots()
        sage: list(alphacheck)
        [alphacheck[1], alphacheck[2], alphacheck[3]]

    ::

        sage: [Lambda[i].scalar(alphacheck[1]) for i in space.index_set()]
        [1, 0, 0]
        sage: [Lambda[i].scalar(alphacheck[2]) for i in space.index_set()]
        [0, 1, 0]
        sage: [Lambda[i].scalar(alphacheck[3]) for i in space.index_set()]
        [0, 0, 1]

    Let us use the simple reflections. In the weight space, they
    work as in the *number game*: firing the node `i` on an
    element `x` adds `c` times the simple root
    `\alpha_i`, where `c` is the coefficient of
    `i` in `x`::

        sage: s = space.simple_reflections()
        sage: Lambda[1].simple_reflection(1)
        -Lambda[1] + Lambda[2]
        sage: Lambda[2].simple_reflection(1)
        Lambda[2]
        sage: Lambda[3].simple_reflection(1)
        Lambda[3]
        sage: (-2*Lambda[1] + Lambda[2] + Lambda[3]).simple_reflection(1)
        2*Lambda[1] - Lambda[2] + Lambda[3]

    It can be convenient to manipulate the simple reflections
    themselves::

        sage: s = space.simple_reflections()
        sage: s[1](Lambda[1])
        -Lambda[1] + Lambda[2]
        sage: s[1](Lambda[2])
        Lambda[2]
        sage: s[1](Lambda[3])
        Lambda[3]

    .. RUBRIC:: Ambient spaces

    The root system may also come equipped with an ambient space.
    This is a `\QQ`-module, endowed with its canonical Euclidean
    scalar product, which admits simultaneous embeddings of the
    (extended) weight and the (extended) coweight lattice, and
    therefore the root and the coroot lattice. This is implemented on
    a type by type basis for the finite crystalographic root systems
    following Bourbaki's conventions and is extended to the affine
    cases. Coefficients permitting, this is also available as an
    ambient lattice.

    .. SEEALSO:: :meth:`ambient_space` and :meth:`ambient_lattice` for details

    In finite type `A`, we recover the natural representation of the
    symmetric group as group of permutation matrices::

        sage: RootSystem(["A",2]).ambient_space().weyl_group().simple_reflections()
        Finite family {1: [0 1 0]
                          [1 0 0]
                          [0 0 1],
                       2: [1 0 0]
                          [0 0 1]
                          [0 1 0]}

    In type `B`, `C`, and `D`, we recover the natural representation
    of the Weyl group as groups of signed permutation matrices::

        sage: RootSystem(["B",3]).ambient_space().weyl_group().simple_reflections()
        Finite family {1: [0 1 0]
                          [1 0 0]
                          [0 0 1],
                       2: [1 0 0]
                          [0 0 1]
                          [0 1 0],
                       3: [ 1  0  0]
                          [ 0  1  0]
                          [ 0  0 -1]}

    In (untwisted) affine types `A`, ..., `D`, one can recover from
    the ambient space the affine permutation representation, in window
    notation. Let us consider the ambient space for affine type `A`::

        sage: L = RootSystem(["A",2,1]).ambient_space(); L
        Ambient space of the Root system of type ['A', 2, 1]

    Define the "identity" by an appropriate vector at level -3::

        sage: e = L.basis(); Lambda = L.fundamental_weights()
        sage: id = e[0] + 2*e[1] + 3*e[2]  - 3*Lambda[0]

    The corresponding permutation is obtained by projecting it onto
    the classical ambient space::

        sage: L.classical()
        Ambient space of the Root system of type ['A', 2]
        sage: L.classical()(id)
        (1, 2, 3)

    Here is the orbit of the identity under the action of the finite
    group::

        sage: W = L.weyl_group()
        sage: S3 = [ w.action(id) for w in W.classical() ]
        sage: [L.classical()(x) for x in S3]
        [(1, 2, 3), (2, 1, 3), (3, 1, 2), (3, 2, 1), (1, 3, 2), (2, 3, 1)]

    And the action of `s_0` on these yields::

        sage: s = W.simple_reflections()
        sage: [L.classical()(s[0].action(x)) for x in S3]
        [(0, 2, 4), (0, 1, 5), (-1, 1, 6), (-2, 2, 6), (-1, 3, 4), (-2, 3, 5)]

    .. RUBRIC:: Dual root systems

    The root system is aware of its dual root system::

        sage: R.dual
        Dual of root system of type ['B', 3]

    R.dual is really the root system of type `C_3`::

        sage: R.dual.cartan_type()
        ['C', 3]

    And the coroot lattice that we have been manipulating before is
    really implemented as the root lattice of the dual root system::

        sage: R.dual.root_lattice()
        Coroot lattice of the Root system of type ['B', 3]

    In particular, the coroots for the root lattice are in fact the
    roots of the coroot lattice::

        sage: list(R.root_lattice().simple_coroots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
        sage: list(R.coroot_lattice().simple_roots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
        sage: list(R.dual.root_lattice().simple_roots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]

    The coweight lattice and space are defined similarly. Note that, to
    limit confusion, all the output have been tweaked appropriately.

    .. seealso::

        - :mod:`sage.combinat.root_system`
        - :class:`RootSpace`
        - :class:`WeightSpace`
        - :class:`AmbientSpace`
        - :class:`~sage.combinat.root_system.root_lattice_realizations.RootLatticeRealizations`
        - :class:`~sage.combinat.root_system.weight_lattice_realizations.WeightLatticeRealizations`

    TESTS::

        sage: R = RootSystem(['C',3])
        sage: TestSuite(R).run()
        sage: L = R.ambient_space()
        sage: s = L.simple_reflections() # this used to break the testsuite below due to caching an unpicklable method
        sage: s = L.simple_projections() # todo: not implemented
        sage: TestSuite(L).run()
        sage: L = R.root_space()
        sage: s = L.simple_reflections()
        sage: TestSuite(L).run()

    ::

        sage: for T in CartanType.samples(crystalographic=True):  # long time (13s on sage.math, 2012)
        ...       TestSuite(RootSystem(T)).run()
    """

    @staticmethod
    def __classcall__(cls, cartan_type, as_dual_of=None):
        """
        Straighten arguments to enable unique representation

        .. seealso:: :class:`UniqueRepresentation`

        TESTS::

            sage: RootSystem(["A",3]) is RootSystem(CartanType(["A",3]))
            True
            sage: RootSystem(["B",3], as_dual_of=None) is RootSystem("B3")
            True
        """
        return super(RootSystem, cls).__classcall__(cls, CartanType(cartan_type), as_dual_of)

    def __init__(self, cartan_type, as_dual_of=None):
        """
        TESTS::

            sage: R = RootSystem(['A',3])
            sage: R
            Root system of type ['A', 3]
        """
        self._cartan_type = CartanType(cartan_type)

        # Duality
        # The root system can be defined as dual of another root system. This will
        # only affects the pretty printing
        if as_dual_of is None:
            self.dual_side = False
            self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            # still fails for CartanType G2xA1
            try:
                self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            except StandardError:
                pass
        else:
            self.dual_side = True
            self.dual = as_dual_of

    def _test_root_lattice_realizations(self, **options):
        """
        Runs tests on all the root lattice realizations of this root
        system.

        EXAMPLES::

            sage: RootSystem(["A",3])._test_root_lattice_realizations()

        See also :class:`TestSuite`.
        """
        tester = self._tester(**options)
        options.pop('tester', None)
        from sage.misc.sage_unittest import TestSuite
        TestSuite(self.root_lattice()).run(**options)
        TestSuite(self.root_space()).run(**options)
        TestSuite(self.weight_lattice()).run(**options)
        TestSuite(self.weight_space()).run(**options)
        if self.cartan_type().is_affine():
            TestSuite(self.weight_lattice(extended=True)).run(**options)
            TestSuite(self.weight_space(extended=True)).run(**options)
        if self.ambient_lattice() is not None:
            TestSuite(self.ambient_lattice()).run(**options)
        if self.ambient_space() is not None:
            TestSuite(self.ambient_space()).run(**options)

    def _repr_(self):
        """
        EXAMPLES::

            sage: RootSystem(['A',3])    # indirect doctest
            Root system of type ['A', 3]
            sage: RootSystem(['B',3]).dual    # indirect doctest
            Dual of root system of type ['B', 3]
        """
        if self.dual_side:
            return "Dual of root system of type %s"%self.dual.cartan_type()
        else:
            return "Root system of type %s"%self.cartan_type()

    def cartan_type(self):
        """
        Returns the Cartan type of the root system.

        EXAMPLES::

            sage: R = RootSystem(['A',3])
            sage: R.cartan_type()
            ['A', 3]
        """
        return self._cartan_type

    @cached_method
    def dynkin_diagram(self):
        """
        Returns the Dynkin diagram of the root system.

        EXAMPLES::

            sage: R = RootSystem(['A',3])
            sage: R.dynkin_diagram()
            O---O---O
            1   2   3
            A3
        """
        return self.cartan_type().dynkin_diagram()

    @cached_method
    def cartan_matrix(self):
        """
        EXAMPLES::

            sage: RootSystem(['A',3]).cartan_matrix()
            [ 2 -1  0]
            [-1  2 -1]
            [ 0 -1  2]
        """
        return self.cartan_type().cartan_matrix()

    def index_set(self):
        """
        EXAMPLES::

            sage: RootSystem(['A',3]).index_set()
            [1, 2, 3]
        """
        return self.cartan_type().index_set()

    @cached_method
    def is_finite(self):
        """
        Returns True if self is a finite root system.

        EXAMPLES::

            sage: RootSystem(["A",3]).is_finite()
            True
            sage: RootSystem(["A",3,1]).is_finite()
            False
        """
        return self.cartan_type().is_finite()

    @cached_method
    def is_irreducible(self):
        """
        Returns True if self is an irreducible root system.

        EXAMPLES::

            sage: RootSystem(['A', 3]).is_irreducible()
            True
            sage: RootSystem("A2xB2").is_irreducible()
            False
        """
        return self.cartan_type().is_irreducible()

    def __cmp__(self, other):
        """
        EXAMPLES::

            sage: r1 = RootSystem(['A',3])
            sage: r2 = RootSystem(['B',3])
            sage: r1 == r1
            True
            sage: r1 == r2
            False
        """
        if self.__class__ != other.__class__:
            return cmp(self.__class__, other.__class__)
        if self._cartan_type != other._cartan_type:
            return cmp(self._cartan_type, other._cartan_type)
        return 0

    def root_lattice(self):
        """
        Returns the root lattice associated to self.

        EXAMPLES::

            sage: RootSystem(['A',3]).root_lattice()
            Root lattice of the Root system of type ['A', 3]
        """
        return self.root_space(ZZ)

    @cached_method
    def root_space(self, base_ring=QQ):
        """
        Returns the root space associated to self.

        EXAMPLES::

            sage: RootSystem(['A',3]).root_space()
            Root space over the Rational Field of the Root system of type ['A', 3]
        """
        return RootSpace(self, base_ring)

    def root_poset(self, restricted=False, facade=False):
        r"""
        Returns the (restricted) root poset associated to ``self``.

        The elements are given by the positive roots (resp. non-simple, positive roots), and
        `\alpha \leq \beta` iff `\beta - \alpha` is a non-negative linear combination of simple roots.

        INPUT:

        - ``restricted`` -- (default:False) if True, only non-simple roots are considered.
        - ``facade`` -- (default:False) passes facade option to the poset generator.

        EXAMPLES::

            sage: Phi = RootSystem(['A',2]).root_poset(); Phi
            Finite poset containing 3 elements
            sage: Phi.cover_relations()
            [[alpha[1], alpha[1] + alpha[2]], [alpha[2], alpha[1] + alpha[2]]]

            sage: Phi = RootSystem(['A',3]).root_poset(restricted=True); Phi
            Finite poset containing 3 elements
            sage: Phi.cover_relations()
            [[alpha[1] + alpha[2], alpha[1] + alpha[2] + alpha[3]], [alpha[2] + alpha[3], alpha[1] + alpha[2] + alpha[3]]]

            sage: Phi = RootSystem(['B',2]).root_poset(); Phi
            Finite poset containing 4 elements
            sage: Phi.cover_relations()
            [[alpha[1], alpha[1] + alpha[2]], [alpha[2], alpha[1] + alpha[2]], [alpha[1] + alpha[2], alpha[1] + 2*alpha[2]]]
        """
        return self.root_lattice().root_poset(restricted=restricted,facade=facade)

    def coroot_lattice(self):
        """
        Returns the coroot lattice associated to self.

        EXAMPLES::

            sage: RootSystem(['A',3]).coroot_lattice()
            Coroot lattice of the Root system of type ['A', 3]
        """
        return self.dual.root_lattice()

    def coroot_space(self, base_ring=QQ):
        """
        Returns the coroot space associated to self.

        EXAMPLES::

            sage: RootSystem(['A',3]).coroot_space()
            Coroot space over the Rational Field of the Root system of type ['A', 3]
        """
        return self.dual.root_space(base_ring)

    @cached_method
    def weight_lattice(self, extended = False):
        """
        Returns the weight lattice associated to self.

        .. see also::

            - :meth:`weight_space`
            - :meth:`coweight_space`, :meth:`coweight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).weight_lattice()
            Weight lattice of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).weight_space(extended = True)
            Extended weight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return WeightSpace(self, ZZ, extended = extended)

    @cached_method
    def weight_space(self, base_ring=QQ, extended = False):
        """
        Returns the weight space associated to self.

        .. see also::

            - :meth:`weight_lattice`
            - :meth:`coweight_space`, :meth:`coweight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).weight_space()
            Weight space over the Rational Field of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).weight_space(extended = True)
            Extended weight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return WeightSpace(self, base_ring, extended = extended)

    def coweight_lattice(self, extended = False):
        """
        Returns the coweight lattice associated to self.

        This is the weight lattice of the dual root system.

        .. see also::

            - :meth:`coweight_space`
            - :meth:`weight_space`, :meth:`weight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).coweight_lattice()
            Coweight lattice of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).coweight_lattice(extended = True)
            Extended coweight lattice of the Root system of type ['A', 3, 1]
        """
        return self.dual.weight_lattice(extended = extended)

    def coweight_space(self, base_ring=QQ, extended = False):
        """
        Returns the coweight space associated to self.

        This is the weight space of the dual root system.

        .. see also::

            - :meth:`coweight_lattice`
            - :meth:`weight_space`, :meth:`weight_lattice`
            - :class:`~sage.combinat.root_system.WeightSpace`

        EXAMPLES::

            sage: RootSystem(['A',3]).coweight_space()
            Coweight space over the Rational Field of the Root system of type ['A', 3]

            sage: RootSystem(['A',3,1]).coweight_space(extended=True)
            Extended coweight space over the Rational Field of the Root system of type ['A', 3, 1]
        """
        return self.dual.weight_space(base_ring, extended = extended)

    def ambient_lattice(self):
        r"""
        Return the ambient lattice for this root_system.

        This is the ambient space, over `\ZZ`.

        .. SEEALSO::

            - :meth:`ambient_space`
            - :meth:`root_lattice`
            - :meth:`weight_lattice`

        EXAMPLES::

            sage: RootSystem(['A',4]).ambient_lattice()
            Ambient lattice of the Root system of type ['A', 4]
            sage: RootSystem(['A',4,1]).ambient_lattice()
            Ambient lattice of the Root system of type ['A', 4, 1]

        Except in type A, only an ambient space can be realized::

            sage: RootSystem(['B',4]).ambient_lattice()
            sage: RootSystem(['C',4]).ambient_lattice()
            sage: RootSystem(['D',4]).ambient_lattice()
            sage: RootSystem(['E',6]).ambient_lattice()
            sage: RootSystem(['F',4]).ambient_lattice()
            sage: RootSystem(['G',2]).ambient_lattice()
        """
        return self.ambient_space(ZZ)

    @cached_method
    def ambient_space(self, base_ring=QQ):
        r"""
        Return the usual ambient space for this root_system.

        INPUT:

        - ``base_ring`` -- a base ring (default: `\QQ`)

        This is a ``base_ring``-module, endowed with its canonical
        Euclidean scalar product, which admits simultaneous embeddings
        into the weight and the coweight lattice, and therefore the
        root and the coroot lattice, and preserves scalar products
        between elements of the coroot lattice and elements of the
        root or weight lattice (and dually).

        There is no mechanical way to define the ambient space just
        from the Cartan matrix. Instead is is constructed from hard
        coded type by type data, according to the usual Bourbaki
        conventions. Such data is provided for all the finite
        (crystalographic) types. From this data, ambient spaces can be
        built as well for dual types, reducible types and affine
        types. When no data is available, or if the base ring is not
        large enough, None is returned.

        .. WARNING:: for affine types

        .. SEEALSO::

            - The section on ambient spaces in :class:`RootSystem`
            - :meth:`ambient_lattice`
            - :class:`~sage.combinat.root_system.ambient_space.AmbientSpace`
            - :class:`~sage.combinat.root_system.ambient_space.type_affine.AmbientSpace`
            - :meth:`root_space`
            - :meth:`weight:space`

        EXAMPLES::

            sage: RootSystem(['A',4]).ambient_space()
            Ambient space of the Root system of type ['A', 4]

        ::

            sage: RootSystem(['B',4]).ambient_space()
            Ambient space of the Root system of type ['B', 4]

        ::

            sage: RootSystem(['C',4]).ambient_space()
            Ambient space of the Root system of type ['C', 4]

        ::

            sage: RootSystem(['D',4]).ambient_space()
            Ambient space of the Root system of type ['D', 4]

        ::

            sage: RootSystem(['E',6]).ambient_space()
            Ambient space of the Root system of type ['E', 6]

        ::

            sage: RootSystem(['F',4]).ambient_space()
            Ambient space of the Root system of type ['F', 4]

        ::

            sage: RootSystem(['G',2]).ambient_space()
            Ambient space of the Root system of type ['G', 2]

        An alternative base ring can be provided as an option::

            sage: e = RootSystem(['B',3]).ambient_space(RR)
            sage: TestSuite(e).run()

        It should contain the smallest ring over which the ambient
        space can be defined (`\ZZ` in type `A` or `\QQ` otherwise).
        Otherwise ``None`` is returned::

            sage: RootSystem(['B',2]).ambient_space(ZZ)

        The base ring should also be totally ordered. In practice,
        only `\ZZ` and `\QQ` are really supported at this point, but
        you are welcome to experiment::

            sage: e = RootSystem(['G',2]).ambient_space(RR)
            sage: TestSuite(e).run()
            Failure in _test_root_lattice_realization:
            Traceback (most recent call last):
            ...
            AssertionError: 2.00000000000000 != 2.00000000000000
            ------------------------------------------------------------
            The following tests failed: _test_root_lattice_realization
        """
        if not hasattr(self.cartan_type(),"AmbientSpace"):
            return None
        AmbientSpace = self.cartan_type().AmbientSpace
        if not base_ring.has_coerce_map_from(AmbientSpace.smallest_base_ring(self.cartan_type())):
            return None
        return AmbientSpace(self, base_ring)
Example #23
0
class RootSystem(UniqueRepresentation, SageObject):
    r"""
    A class for root systems.
    
    EXAMPLES:

    We construct the root system for type `B_3`::
    
        sage: R=RootSystem(['B',3]); R
        Root system of type ['B', 3]
    
    ``R`` models the root system abstractly. It comes equipped with various
    realizations of the root and weight lattices, where all computation
    take place. Let us play first with the root lattice::
    
        sage: space = R.root_lattice()
        sage: space
        Root lattice of the Root system of type ['B', 3]
    
    It is the free `\ZZ`-module
    `\bigoplus_i \ZZ.\alpha_i` spanned by the simple
    roots::
    
        sage: space.base_ring()
        Integer Ring
        sage: list(space.basis()) 
        [alpha[1], alpha[2], alpha[3]]
    
    Let us do some computations with the simple roots::
    
        sage: alpha = space.simple_roots()
        sage: alpha[1] + alpha[2]
        alpha[1] + alpha[2]
    
    There is a canonical pairing between the root lattice and the
    coroot lattice::
    
        sage: R.coroot_lattice()
        Coroot lattice of the Root system of type ['B', 3]
    
    We construct the simple coroots, and do some computations (see
    comments about duality below for some caveat)::
    
        sage: alphacheck = space.simple_coroots()
        sage: list(alphacheck)
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    We can carry over the same computations in any of the other
    realizations of the root lattice, like the root space
    `\bigoplus_i \QQ.\alpha_i`, the weight lattice
    `\bigoplus_i \ZZ.\Lambda_i`, the weight
    space `\bigoplus_i \QQ.\Lambda_i`. For example::
    
        sage: space = R.weight_space()
        sage: space
        Weight space over the Rational Field of the Root system of type ['B', 3]
    
    ::
    
        sage: space.base_ring()
        Rational Field
        sage: list(space.basis())
        [Lambda[1], Lambda[2], Lambda[3]]
    
    ::
    
        sage: alpha = space.simple_roots()
        sage: alpha[1] + alpha[2]
        Lambda[1] + Lambda[2] - 2*Lambda[3]
    
    The fundamental weights are the dual basis of the coroots::
    
        sage: Lambda = space.fundamental_weights()
        sage: Lambda[1]
        Lambda[1]
    
    ::
    
        sage: alphacheck = space.simple_coroots()
        sage: list(alphacheck)
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    ::
    
        sage: [Lambda[i].scalar(alphacheck[1]) for i in space.index_set()]
        [1, 0, 0]
        sage: [Lambda[i].scalar(alphacheck[2]) for i in space.index_set()]
        [0, 1, 0]
        sage: [Lambda[i].scalar(alphacheck[3]) for i in space.index_set()]
        [0, 0, 1]
    
    Let us use the simple reflections. In the weight space, they
    work as in the *number game*: firing the node `i` on an
    element `x` adds `c` times the simple root
    `\alpha_i`, where `c` is the coefficient of
    `i` in `x`::
    
        sage: s = space.simple_reflections()
        sage: Lambda[1].simple_reflection(1)
        -Lambda[1] + Lambda[2]
        sage: Lambda[2].simple_reflection(1)
        Lambda[2]
        sage: Lambda[3].simple_reflection(1)
        Lambda[3]
        sage: (-2*Lambda[1] + Lambda[2] + Lambda[3]).simple_reflection(1)
        2*Lambda[1] - Lambda[2] + Lambda[3]
    
    It can be convenient to manipulate the simple reflections
    themselves::
    
        sage: s = space.simple_reflections()
        sage: s[1](Lambda[1])
        -Lambda[1] + Lambda[2]
        sage: s[1](Lambda[2])
        Lambda[2]
        sage: s[1](Lambda[3])
        Lambda[3]
    
    The root system may also come equipped with an ambient space, that
    is a simultaneous realization of the weight lattice and the coroot
    lattice in a Euclidean vector space. This is implemented on a type
    by type basis, and is not always available. When the coefficients
    permit it, this is also available as an ambient lattice.
    
    TODO: Demo: signed permutations realization of type B
    
    The root system is aware of its dual root system::
    
        sage: R.dual
        Dual of root system of type ['B', 3]
    
    R.dual is really the root system of type `C_3`::
    
        sage: R.dual.cartan_type()
        ['C', 3]
    
    And the coroot lattice that we have been manipulating before is
    really implemented as the root lattice of the dual root system::
    
        sage: R.dual.root_lattice()
        Coroot lattice of the Root system of type ['B', 3]
    
    In particular, the coroots for the root lattice are in fact the
    roots of the coroot lattice::
    
        sage: list(R.root_lattice().simple_coroots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
        sage: list(R.coroot_lattice().simple_roots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
        sage: list(R.dual.root_lattice().simple_roots())
        [alphacheck[1], alphacheck[2], alphacheck[3]]
    
    The coweight lattice and space are defined similarly. Note that, to
    limit confusion, all the output have been tweaked appropriately.
    
    TESTS::
    
        sage: R = RootSystem(['C',3])
        sage: R == loads(dumps(R))
        True
        sage: L = R.ambient_space()
        sage: s = L.simple_reflections()
        sage: s = L.simple_projections() # todo: not implemented
        sage: L == loads(dumps(L))
        True
        sage: L = R.root_space()
        sage: s = L.simple_reflections()
        sage: L == loads(dumps(L))
        True
    
    ::
    
        sage: for T in CartanType.samples(finite=True,crystalographic=True):
        ...       TestSuite(RootSystem(T)).run()
    """  

    @staticmethod
    def __classcall__(cls, cartan_type, as_dual_of=None):
        return super(RootSystem, cls).__classcall__(cls, CartanType(cartan_type), as_dual_of)

    def __init__(self, cartan_type, as_dual_of=None):
        """
        TESTS::
        
            sage: R = RootSystem(['A',3])
            sage: R
            Root system of type ['A', 3]
        """
        self._cartan_type = CartanType(cartan_type)

        # Duality
        # The root system can be defined as dual of another root system. This will
        # only affects the pretty printing
        if as_dual_of is None:
            self.dual_side = False
            self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            # still fails for CartanType G2xA1
            try:
                self.dual = RootSystem(self._cartan_type.dual(), as_dual_of=self);
            except:
                pass
        else:
            self.dual_side = True
            self.dual = as_dual_of


    def _test_root_lattice_realizations(self, **options):
        """
        Runs tests on all the root lattice realizations of this root
        system.
        
        EXAMPLES::
        
            sage: RootSystem(["A",3])._test_root_lattice_realizations()

        See also :class:`TestSuite`.
        """
        tester = self._tester(**options)
        options.pop('tester', None)
        from sage.misc.sage_unittest import TestSuite
        TestSuite(self.root_lattice()).run(**options)
        TestSuite(self.root_space()).run(**options)
        TestSuite(self.weight_lattice()).run(**options)
        TestSuite(self.weight_space()).run(**options)
        if self.ambient_lattice() is not None:
            TestSuite(self.ambient_lattice()).run(**options)
        if self.ambient_space() is not None:
            TestSuite(self.ambient_space()).run(**options)

    def _repr_(self):
        """
        EXAMPLES::
        
            sage: RootSystem(['A',3])
            Root system of type ['A', 3]
        """
        if self.dual_side:
            return "Dual of root system of type %s"%self.dual.cartan_type()
        else:
            return "Root system of type %s"%self.cartan_type()

    def cartan_type(self):
        """
        Returns the Cartan type of the root system.
        
        EXAMPLES::
        
            sage: R = RootSystem(['A',3])
            sage: R.cartan_type()
            ['A', 3]
        """
        return self._cartan_type

    @cached_method
    def dynkin_diagram(self):
        """
        Returns the Dynkin diagram of the root system.
        
        EXAMPLES::
        
            sage: R = RootSystem(['A',3])
            sage: R.dynkin_diagram()
            O---O---O
            1   2   3
            A3
        """
        return self.cartan_type().dynkin_diagram()

    @cached_method
    def cartan_matrix(self):
        """
        EXAMPLES::
        
            sage: RootSystem(['A',3]).cartan_matrix()
            [ 2 -1  0]
            [-1  2 -1]
            [ 0 -1  2]
        """
        return self.cartan_type().cartan_matrix()

    def index_set(self):
        """
        EXAMPLES::
        
            sage: RootSystem(['A',3]).index_set()
            [1, 2, 3]
        """
        return self.cartan_type().index_set()

    @cached_method
    def is_finite(self):
        """
        Returns True if self is a finite root system.
        
        EXAMPLES::
        
            sage: RootSystem(["A",3]).is_finite()
            True
            sage: RootSystem(["A",3,1]).is_finite()
            False
        """
        return self.cartan_type().is_finite()

    @cached_method
    def is_irreducible(self):
        """
        Returns True if self is an irreducible root system.
        
        EXAMPLES::
        
            sage: RootSystem(['A', 3]).is_irreducible()
            True
            sage: RootSystem("A2xB2").is_irreducible()
            False
        """
        return self.cartan_type().is_irreducible()

    def __cmp__(self, other):
        """
        EXAMPLES::
        
            sage: r1 = RootSystem(['A',3])
            sage: r2 = RootSystem(['B',3])
            sage: r1 == r1
            True
            sage: r1 == r2
            False
        """
        if self.__class__ != other.__class__:
            return cmp(self.__class__, other.__class__)
        if self._cartan_type != other._cartan_type:
            return cmp(self._cartan_type, other._cartan_type)
        return 0

    def root_lattice(self):
        """
        Returns the root lattice associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).root_lattice()
            Root lattice of the Root system of type ['A', 3]
        """
        return self.root_space(ZZ)

    @cached_method
    def root_space(self, base_ring=QQ):
        """
        Returns the root space associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).root_space()
            Root space over the Rational Field of the Root system of type ['A', 3]
        """
        return RootSpace(self, base_ring)

    def root_poset(self, restricted=False):
        r"""
        Returns the (restricted) root poset associated to ``self``.

        The elements are given by the positive roots (resp. non-simple, positive roots), and
        `\alpha \leq \beta` iff `\beta - \alpha` is a non-negative linear combination of simple roots.

        INPUT:

        - restricted -- (default:False) if True, only non-simple roots are considered.

        EXAMPLES::

            sage: Phi = RootSystem(['A',2]).root_poset(); Phi
            Finite poset containing 3 elements
            sage: Phi.cover_relations()
            [[alpha[1], alpha[1] + alpha[2]], [alpha[2], alpha[1] + alpha[2]]]

            sage: Phi = RootSystem(['A',3]).root_poset(restricted=True); Phi
            Finite poset containing 3 elements
            sage: Phi.cover_relations()
            [[alpha[1] + alpha[2], alpha[1] + alpha[2] + alpha[3]], [alpha[2] + alpha[3], alpha[1] + alpha[2] + alpha[3]]]

            sage: Phi = RootSystem(['B',2]).root_poset(); Phi
            Finite poset containing 4 elements
            sage: Phi.cover_relations()
            [[alpha[1], alpha[1] + alpha[2]], [alpha[2], alpha[1] + alpha[2]], [alpha[1] + alpha[2], alpha[1] + 2*alpha[2]]]
        """
        return self.root_lattice().root_poset(restricted=restricted)

    def coroot_lattice(self):
        """
        Returns the coroot lattice associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).coroot_lattice()
            Coroot lattice of the Root system of type ['A', 3]
        """
        return self.dual.root_lattice()
    
    def coroot_space(self, base_ring=QQ):
        """
        Returns the coroot space associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).coroot_space()
            Coroot space over the Rational Field of the Root system of type ['A', 3]
        """
        return self.dual.root_space(base_ring)

    def weight_lattice(self):
        """
        Returns the weight lattice associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).weight_lattice()
            Weight lattice of the Root system of type ['A', 3]
        """
        return self.weight_space(ZZ)

    @cached_method
    def weight_space(self, base_ring=QQ):
        """
        Returns the weight space associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).weight_space()
            Weight space over the Rational Field of the Root system of type ['A', 3]
        """
        return WeightSpace(self, base_ring)

    def coweight_lattice(self):
        """
        Returns the coweight lattice associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).coweight_lattice()
            Coweight lattice of the Root system of type ['A', 3]
        """
        return self.dual.weight_lattice()
    
    def coweight_space(self, base_ring=QQ):
        """
        Returns the weight space associated to self.
        
        EXAMPLES::
        
            sage: RootSystem(['A',3]).coweight_space()
            Coweight space over the Rational Field of the Root system of type ['A', 3]
        """
        return self.dual.weight_space(base_ring)


    def ambient_lattice(self):
        r"""
        Returns the usual ambient lattice for this root_system, if it
        exists and is implemented, and None otherwise. This is a
        `\ZZ`-module, endowed with its canonical euclidean
        scalar product, which embeds simultaneously the root lattice
        and the coroot lattice (what about the weight lattice?)
        
        EXAMPLES::
        
            sage: RootSystem(['A',4]).ambient_lattice()
            Ambient lattice of the Root system of type ['A', 4]
        
        ::
        
            sage: RootSystem(['B',4]).ambient_lattice()
            sage: RootSystem(['C',4]).ambient_lattice()
            sage: RootSystem(['D',4]).ambient_lattice()
            sage: RootSystem(['E',6]).ambient_lattice()
            sage: RootSystem(['F',4]).ambient_lattice()
            sage: RootSystem(['G',2]).ambient_lattice()
        """
        return self.ambient_space(ZZ)

    @cached_method
    def ambient_space(self, base_ring=QQ):
        r"""
        Returns the usual ambient space for this root_system, if it is
        implemented, and None otherwise. This is a `\QQ`-module, endowed with
        its canonical euclidean scalar product, which embeds simultaneously
        the root lattice and the coroot lattice (what about the weight
        lattice?). An alternative base ring can be provided as an option;
        it must contain the smallest ring over which the ambient space can
        be defined (`\ZZ` or `\QQ`, depending on the type).
        
        EXAMPLES::
        
            sage: RootSystem(['A',4]).ambient_space()
            Ambient space of the Root system of type ['A', 4]
        
        ::
        
            sage: RootSystem(['B',4]).ambient_space()
            Ambient space of the Root system of type ['B', 4]
        
        ::
        
            sage: RootSystem(['C',4]).ambient_space()
            Ambient space of the Root system of type ['C', 4]
        
        ::
        
            sage: RootSystem(['D',4]).ambient_space()
            Ambient space of the Root system of type ['D', 4]
        
        ::
        
            sage: RootSystem(['E',6]).ambient_space()
            Ambient space of the Root system of type ['E', 6]
        
        ::
        
            sage: RootSystem(['F',4]).ambient_space()
            Ambient space of the Root system of type ['F', 4]
        
        ::
        
            sage: RootSystem(['G',2]).ambient_space()
            Ambient space of the Root system of type ['G', 2]
        """
        # Intention: check that the ambient_space is implemented and that
        # base_ring contains the smallest base ring for this ambient space
        if not hasattr(self.cartan_type(),"AmbientSpace"):
            return None
        AmbientSpace = self.cartan_type().AmbientSpace
        if base_ring == ZZ and AmbientSpace.smallest_base_ring() == QQ:
            return None
        return AmbientSpace(self, base_ring)
Example #24
0
 def __classcall__(cls, cartan_type, as_dual_of=None):
     return super(RootSystem,
                  cls).__classcall__(cls, CartanType(cartan_type),
                                     as_dual_of)
Example #25
0
def DynkinDiagram(*args):
    r"""
    Return a Dynkin diagram for type ``ct``.

    INPUT:

    -  ``ct`` -- a Cartan Type

    The edge multiplicities are encoded as edge labels. This uses the
    convention in Hong and Kang, Kac, Fulton Harris, and crystals. This is the
    **opposite** convention in Bourbaki and Wikipedia's Dynkin diagram
    (:wikipedia:`Dynkin_diagram`). That is for `i \neq j`::

       i <--k-- j <==> a_ij = -k
                  <==> -scalar(coroot[i], root[j]) = k
                  <==> multiple arrows point from the shorter root
                       to the longer one

    For example, in type `C_2`, we have::

        sage: C2 = DynkinDiagram(['C',2]); C2
        O=<=O
        1   2
        C2
        sage: C2.cartan_matrix()
        [ 2 -2]
        [-1  2]

    However Bourbaki would have the Cartan matrix as:

    .. MATH::

        \begin{bmatrix}
        2 & -1 \\
        -2 & 2
        \end{bmatrix}.

    EXAMPLES::

        sage: DynkinDiagram(['A', 4])
        O---O---O---O
        1   2   3   4
        A4

        sage: DynkinDiagram(['A',1],['A',1])
        O
        1
        O
        2
        A1xA1

        sage: R = RootSystem("A2xB2xF4")
        sage: DynkinDiagram(R)
        O---O
        1   2
        O=>=O
        3   4
        O---O=>=O---O
        5   6   7   8
        A2xB2xF4

    .. SEEALSO::

        :func:`CartanType` for a general discussion on Cartan
        types and in particular node labeling conventions.
    """
    if len(args) == 0:
       return DynkinDiagram_class()
    ct = CartanType(*args)
    if hasattr(ct, "dynkin_diagram"):
        return ct.dynkin_diagram()
    else:
        raise ValueError, "Dynkin diagram data not yet hardcoded for type %s"%ct
Example #26
0
def cartan_matrix(t):
    """
    Returns the Cartan matrix corresponding to type t.
    
    EXAMPLES::
    
        sage: cartan_matrix(['A', 4])
        [ 2 -1  0  0]
        [-1  2 -1  0]
        [ 0 -1  2 -1]
        [ 0  0 -1  2]
        sage: cartan_matrix(['B', 6])
        [ 2 -1  0  0  0  0]
        [-1  2 -1  0  0  0]
        [ 0 -1  2 -1  0  0]
        [ 0  0 -1  2 -1  0]
        [ 0  0  0 -1  2 -1]
        [ 0  0  0  0 -2  2]
        sage: cartan_matrix(['C', 4])
        [ 2 -1  0  0]
        [-1  2 -1  0]
        [ 0 -1  2 -2]
        [ 0  0 -1  2]
        sage: cartan_matrix(['D', 6])
        [ 2 -1  0  0  0  0]
        [-1  2 -1  0  0  0]
        [ 0 -1  2 -1  0  0]
        [ 0  0 -1  2 -1 -1]
        [ 0  0  0 -1  2  0]
        [ 0  0  0 -1  0  2]
        sage: cartan_matrix(['E',6])
        [ 2  0 -1  0  0  0]
        [ 0  2  0 -1  0  0]
        [-1  0  2 -1  0  0]
        [ 0 -1 -1  2 -1  0]
        [ 0  0  0 -1  2 -1]
        [ 0  0  0  0 -1  2]
        sage: cartan_matrix(['E',7])
        [ 2  0 -1  0  0  0  0]
        [ 0  2  0 -1  0  0  0]
        [-1  0  2 -1  0  0  0]
        [ 0 -1 -1  2 -1  0  0]
        [ 0  0  0 -1  2 -1  0]
        [ 0  0  0  0 -1  2 -1]
        [ 0  0  0  0  0 -1  2]
        sage: cartan_matrix(['E', 8])
        [ 2  0 -1  0  0  0  0  0]
        [ 0  2  0 -1  0  0  0  0]
        [-1  0  2 -1  0  0  0  0]
        [ 0 -1 -1  2 -1  0  0  0]
        [ 0  0  0 -1  2 -1  0  0]
        [ 0  0  0  0 -1  2 -1  0]
        [ 0  0  0  0  0 -1  2 -1]
        [ 0  0  0  0  0  0 -1  2]
        sage: cartan_matrix(['F', 4])
        [ 2 -1  0  0]
        [-1  2 -1  0]
        [ 0 -2  2 -1]
        [ 0  0 -1  2]
    
    This is different from MuPAD-Combinat, due to different node
    convention?
    
    ::
    
        sage: cartan_matrix(['G', 2])
        [ 2 -3]
        [-1  2]
        sage: cartan_matrix(['A',1,1])
        [ 2 -2]
        [-2  2]
        sage: cartan_matrix(['A', 3, 1])
        [ 2 -1  0 -1]
        [-1  2 -1  0]
        [ 0 -1  2 -1]
        [-1  0 -1  2]
        sage: cartan_matrix(['B', 3, 1])
        [ 2  0 -1  0]
        [ 0  2 -1  0]
        [-1 -1  2 -1]
        [ 0  0 -2  2]
        sage: cartan_matrix(['C', 3, 1])
        [ 2 -1  0  0]
        [-2  2 -1  0]
        [ 0 -1  2 -2]
        [ 0  0 -1  2]
        sage: cartan_matrix(['D', 4, 1])
        [ 2  0 -1  0  0]
        [ 0  2 -1  0  0]
        [-1 -1  2 -1 -1]
        [ 0  0 -1  2  0]
        [ 0  0 -1  0  2]
        sage: cartan_matrix(['E', 6, 1])
        [ 2  0 -1  0  0  0  0]
        [ 0  2  0 -1  0  0  0]
        [-1  0  2  0 -1  0  0]
        [ 0 -1  0  2 -1  0  0]
        [ 0  0 -1 -1  2 -1  0]
        [ 0  0  0  0 -1  2 -1]
        [ 0  0  0  0  0 -1  2]
        sage: cartan_matrix(['E', 7, 1])
        [ 2 -1  0  0  0  0  0  0]
        [-1  2  0 -1  0  0  0  0]
        [ 0  0  2  0 -1  0  0  0]
        [ 0 -1  0  2 -1  0  0  0]
        [ 0  0 -1 -1  2 -1  0  0]
        [ 0  0  0  0 -1  2 -1  0]
        [ 0  0  0  0  0 -1  2 -1]
        [ 0  0  0  0  0  0 -1  2]
        sage: cartan_matrix(['E', 8, 1])
        [ 2  0  0  0  0  0  0  0 -1]
        [ 0  2  0 -1  0  0  0  0  0]
        [ 0  0  2  0 -1  0  0  0  0]
        [ 0 -1  0  2 -1  0  0  0  0]
        [ 0  0 -1 -1  2 -1  0  0  0]
        [ 0  0  0  0 -1  2 -1  0  0]
        [ 0  0  0  0  0 -1  2 -1  0]
        [ 0  0  0  0  0  0 -1  2 -1]
        [-1  0  0  0  0  0  0 -1  2]
        sage: cartan_matrix(['F', 4, 1])
        [ 2 -1  0  0  0]
        [-1  2 -1  0  0]
        [ 0 -1  2 -1  0]
        [ 0  0 -2  2 -1]
        [ 0  0  0 -1  2]
        sage: cartan_matrix(['G', 2, 1])
        [ 2  0 -1]
        [ 0  2 -3]
        [-1 -1  2]

    .. note::

        This function is likely to be deprecated in favor of
        ``CartanType(...).cartan_matrix()``, to avoid polluting the
        global namespace.
    """
    return CartanType(t).cartan_matrix()
        def plot(self,
                 size=[[0], [0]],
                 projection='usual',
                 simple_roots=True,
                 fundamental_weights=True,
                 alcovewalks=[]):
            r"""
            Return a graphics object built from a space of weight(space/lattice).
            There is a different technic to plot if the Cartan type is affine or not.
            The graphics returned is a Graphics object.

            This function is experimental, and is subject to short term evolutions.

            EXAMPLES::

              By default, the plot returned has no axes and the ratio between axes is 1.
                sage: G = RootSystem(['C',2]).weight_lattice().plot()
                sage: G.axes(True)
                sage: G.set_aspect_ratio(2)

              For a non affine Cartan type, the plot method work for type with 2 generators,
              it will draw the hyperlane(line for this dimension) accrow the fundamentals weights.
                sage: G = RootSystem(['A',2]).weight_lattice().plot()
                sage: G = RootSystem(['B',2]).weight_lattice().plot()
                sage: G = RootSystem(['G',2]).weight_lattice().plot()

              The plot returned has a size of one fundamental polygon by default. We can
              ask plot to give a bigger plot by using the argument size
                sage: G = RootSystem(['G',2,1]).weight_space().plot(size = [[0..1],[-1..1]])
                sage: G = RootSystem(['A',2,1]).weight_space().plot(size = [[-1..1],[-1..1]])

              A very important argument is the projection which will draw the plot. There are
              some usual projections is this method. If you want to draw in the plane a very
              special Cartan type, Sage will ask you to specify the projection. The projection
              is a matrix over a ring. In practice, calcul over float is a good way to draw.
                sage: L = RootSystem(['A',2,1]).weight_space()
                sage: G = L.plot(projection=matrix(RR, [[0,0.5,-0.5],[0,0.866,0.866]]))
                sage: G = RootSystem(['C',2,1]).weight_space().plot()

              By default, the plot method draw the simple roots, this can be disabled by setting
              the argument simple_roots=False
                sage: G = RootSystem(['A',2]).weight_space().plot(simple_roots=False)

              By default, the plot method draw the fundamental weights,this can be disabled by
              setting the argument fundamental_weights=False
                sage: G = RootSystem(['A',2]).weight_space().plot(fundamental_weights=False, simple_roots=False)

              There is in a plot an argument to draw alcoves walks. The good way to do this is
              to use the crystals theory. the plot method contains only the drawing part...
                sage: L = RootSystem(['A',2,1]).weight_space()
                sage: G = L.plot(size=[[-1..1],[-1..1]],alcovewalks=[[0,2,0,1,2,1,2,0,2,1]])
            """

            from sage.plot.all import Graphics
            from sage.plot.line import line
            from cartan_type import CartanType
            from sage.matrix.constructor import matrix
            from sage.rings.all import QQ, RR
            from sage.plot.arrow import arrow
            from sage.plot.point import point

            # We begin with an empty plot G
            G = Graphics()

            ct = self.cartan_type()
            n = ct.n

            # Define a set of colors
            # TODO : Colors in option ?
            colors = [(0, 1, 0), (1, 0, 0), (0, 0, 1), (1, 1, 0), (0, 1, 1),
                      (1, 0, 1)]

            # plot the affine types:
            if ct.is_affine():

                # Check the projection
                # TODO : try to have usual_projection for main plotable types
                if projection == 'usual':
                    if ct == CartanType(['A', 2, 1]):
                        projection = matrix(
                            RR, [[0, 0.5, -0.5], [0, 0.866, 0.866]])
                    elif ct == CartanType(['C', 2, 1]):
                        projection = matrix(QQ, [[0, 1, 1], [0, 0, 1]])
                    elif ct == CartanType(['G', 2, 1]):
                        projection = matrix(RR,
                                            [[0, 0.5, 0], [0, 0.866, 1.732]])
                    else:
                        raise 'There is no usual projection for this Cartan type, you have to give one in argument'

                assert (n + 1 == projection.ncols())
                assert (2 == projection.nrows())

                # Check the size is correct with the lattice
                assert (len(size) == n)

                # Select the center of the translated fundamental polygon to plot
                translation_factors = ct.translation_factors()
                simple_roots = self.simple_roots()
                translation_vectors = [
                    translation_factors[i] * simple_roots[i]
                    for i in ct.classical().index_set()
                ]

                initial = [[]]
                for i in range(n):
                    prod_list = []
                    for elem in size[i]:
                        for partial_list in initial:
                            prod_list.append([elem] + partial_list)
                    initial = prod_list

                part_lattice = []
                for combinaison in prod_list:
                    elem_lattice = self.zero()
                    for i in range(n):
                        elem_lattice = elem_lattice + combinaison[
                            i] * translation_vectors[i]
                    part_lattice.append(elem_lattice)

                # Get the vertices of the fundamental alcove
                fundamental_weights = self.fundamental_weights()
                vertices = map(lambda x: (1 / x.level()) * x,
                               fundamental_weights.list())

                # Recup the group which act on the fundamental polygon
                classical = self.weyl_group().classical()

                for center in part_lattice:
                    for w in classical:
                        # for each center of polygon and each element of classical
                        # parabolic subgroup, we have to draw an alcove.

                        #first, iterate over pairs of fundamental weights, drawing lines border of polygons:
                        for i in range(1, n + 1):
                            for j in range(i + 1, n + 1):
                                p1 = projection * (
                                    (w.action(vertices[i])).to_vector() +
                                    center.to_vector())
                                p2 = projection * (
                                    (w.action(vertices[j])).to_vector() +
                                    center.to_vector())
                                G += line([p1, p2],
                                          rgbcolor=(0, 0, 0),
                                          thickness=2)

                        #next, get all lines from point to a fundamental weight, that separe different
                        #chanber in a same polygon (important: associate a color with a fundamental weight)
                        pcenter = projection * (center.to_vector())
                        for i in range(1, n + 1):
                            p3 = projection * (
                                (w.action(vertices[i])).to_vector() +
                                center.to_vector())
                            G += line([p3, pcenter],
                                      rgbcolor=colors[n - i + 1])

                #Draw alcovewalks
                #FIXME : The good way to draw this is to use the alcoves walks works made in Cristals
                #The code here just draw like example and import the good things.
                rho = (1 / self.rho().level()) * self.rho()
                W = self.weyl_group()
                for walk in alcovewalks:
                    target = W.from_reduced_word(walk).action(rho)
                    for i in range(len(walk)):
                        walk.pop()
                        origin = W.from_reduced_word(walk).action(rho)
                        G += arrow(projection * (origin.to_vector()),
                                   projection * (target.to_vector()),
                                   rgbcolor=(0.6, 0, 0.6),
                                   width=1,
                                   arrowsize=5)
                        target = origin

            else:
                # non affine plot

                # Check the projection
                # TODO : try to have usual_projection for main plotable types
                if projection == 'usual':
                    if ct == CartanType(['A', 2]):
                        projection = matrix(RR, [[0.5, -0.5], [0.866, 0.866]])
                    elif ct == CartanType(['B', 2]):
                        projection = matrix(QQ, [[1, 0], [1, 1]])
                    elif ct == CartanType(['C', 2]):
                        projection = matrix(QQ, [[1, 1], [0, 1]])
                    elif ct == CartanType(['G', 2]):
                        projection = matrix(RR, [[0.5, 0], [0.866, 1.732]])
                    else:
                        raise 'There is no usual projection for this Cartan type, you have to give one in argument'

                # Get the fundamental weights
                fundamental_weights = self.fundamental_weights()
                WeylGroup = self.weyl_group()

                #Draw not the alcove but the cones delimited by the hyperplanes
                #The size of the line depend of the fundamental weights.
                pcenter = projection * (self.zero().to_vector())
                for w in WeylGroup:
                    for i in range(1, n + 1):
                        p3 = 3 * projection * (
                            (w.action(fundamental_weights[i])).to_vector())
                        G += line([p3, pcenter], rgbcolor=colors[n - i + 1])

            #Draw the simple roots
            if simple_roots:
                SimpleRoots = self.simple_roots()
                if ct.is_affine():
                    G += arrow((0, 0),
                               projection * (SimpleRoots[0].to_vector()),
                               rgbcolor=(0, 0, 0))
                for j in range(1, n + 1):
                    G += arrow((0, 0),
                               projection * (SimpleRoots[j].to_vector()),
                               rgbcolor=colors[j])

            #Draw the fundamental weights
            if fundamental_weights:
                FundWeight = self.fundamental_weights()
                for j in range(1, n + 1):
                    G += point(projection * (FundWeight[j].to_vector()),
                               rgbcolor=colors[j],
                               pointsize=60)

            G.set_aspect_ratio(1)
            G.axes(False)
            return G