Пример #1
0
    def _homogeneous_generators_noncommutative_variables_zero_Hecke(self, r):
        r"""
        Returns the ``r^{th}`` homogeneous generator, viewed as an element inside the
        affine zero Hecke algebra. This is the sum of all cyclicly decreasing elements
        of order ``r``.

        INPUT:

        - ``r`` -- A positive integer

        OUTPUT:

        - An element of the affine zero Hecke algebra.

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(2)
            T1*T0 + T2*T0 + T0*T3 + T3*T2 + T3*T1 + T2*T1
            sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(0)
            1
        """
        from sage.combinat.root_system.weyl_group import WeylGroup
        from sage.algebras.iwahori_hecke_algebra import IwahoriHeckeAlgebraT
        W = WeylGroup(['A',self.k,1])
        H = IwahoriHeckeAlgebraT(W, 0, base_ring = self.base_ring())
        Hgens = H.algebra_generators()
        S = [w.reduced_word() for w in W.pieri_factors() if w.length() == r]
        return sum( (prod((Hgens[i] for i in w), 1) for w in S), 0 )
Пример #2
0
    def _homogeneous_generators_noncommutative_variables_zero_Hecke(self, r):
        r"""
        Returns the ``r^{th}`` homogeneous generator, viewed as an element inside the
        affine zero Hecke algebra. This is the sum of all cyclicly decreasing elements
        of order ``r``.

        INPUT:

        - ``r`` -- A positive integer

        OUTPUT:

        - An element of the affine zero Hecke algebra.

        EXAMPLES::

            sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur()
            sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(2)
            T1*T0 + T2*T0 + T0*T3 + T3*T2 + T3*T1 + T2*T1
            sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(0)
            1
        """
        from sage.combinat.root_system.weyl_group import WeylGroup
        from sage.algebras.iwahori_hecke_algebra import IwahoriHeckeAlgebraT
        W = WeylGroup(['A',self.k,1])
        H = IwahoriHeckeAlgebraT(W, 0, base_ring = self.base_ring())
        Hgens = H.algebra_generators()
        S = [w.reduced_word() for w in W.pieri_factors() if w.length() == r]
        return sum( (prod((Hgens[i] for i in w), 1) for w in S), 0 )
Пример #3
0
    def __classcall_private__(cls, w, n, x=None, k=None):
        r"""
        Classcall to mend the input.

        TESTS::

            sage: A = crystals.AffineFactorization([[3,1],[1]], 4, k=3); A
            Crystal on affine factorizations of type A3 associated to s3*s2*s1
            sage: AC = crystals.AffineFactorization([Core([4,1],4),Core([1],4)], 4, k=3)
            sage: AC is A
            True
        """
        if k is not None:
            from sage.combinat.core import Core
            from sage.combinat.partition import Partition
            W = WeylGroup(['A', k, 1], prefix='s')
            if isinstance(w[0], Core):
                w = [w[0].to_bounded_partition(), w[1].to_bounded_partition()]
            else:
                w = [Partition(w[0]), Partition(w[1])]
            w0 = W.from_reduced_word(w[0].from_kbounded_to_reduced_word(k))
            w1 = W.from_reduced_word(w[1].from_kbounded_to_reduced_word(k))
            w = w0 * (w1.inverse())
        return super(AffineFactorizationCrystal,
                     cls).__classcall__(cls, w, n, x)
Пример #4
0
    def __init__(self, root_system, hot_start=None):
        self.root_system = root_system

        if hot_start is None:
            self.W = WeylGroup(root_system)
            self.weyl_dic = self._compute_weyl_dictionary()
        else:
            self.W = hot_start["W"]
            self.weyl_dic = hot_start["weyl_dic"]

        self.domain = self.W.domain()

        self.reduced_words = sorted(self.weyl_dic.keys(), key=len)

        self.simple_roots = self.domain.simple_roots().values()
        self.rank = len(self.simple_roots)
        self.rho = self.domain.rho()

        self.pos_roots = self.domain.positive_roots()

        # Matrix of all simple roots, for faster matrix solving
        self.simple_root_matrix = matrix(
            [list(s.to_vector()) for s in self.simple_roots]).transpose()

        self.action_dic, self.rho_action_dic = self.get_action_dic()
Пример #5
0
        def weyl_group_representation(self):
            r"""
            Transforms the weights in the LS path ``self`` to elements in the Weyl group.

            Each LS path can be written as the piecewise linear map:

            .. MATH::

                \pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'} - \sigma_{u'-1}) \nu_{u'} + (t-\sigma_{u-1}) \nu_{u}

            for `0<\sigma_1<\sigma_2<\cdots<\sigma_s=1` and `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`.
            Each weight `\nu_u` is also associated to a Weyl group element. This method returns the list
            of Weyl group elements associated to the `\nu_u` for `1\le u\le s`.

            EXAMPLES::

                sage: R = RootSystem(['C',3,1])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])
                sage: b = LS.module_generators[0]
                sage: c = b.f(1).f(3).f(2)
                sage: c.weyl_group_representation()
                [s2*s3*s1, s3*s1]
            """
            cartan = self.parent().weight.parent().cartan_type().classical()
            I = cartan.index_set()
            W = WeylGroup(cartan,prefix='s')
            return [W.from_reduced_word(x.to_dominant_chamber(index_set=I, reduced_word=True)[1]) for x in self.value]
Пример #6
0
        def weyl_group_representation(self):
            r"""
            Transforms the weights in the LS path ``self`` to elements in the Weyl group.

            Each LS path can be written as the piecewise linear map:

            .. MATH::

                \pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'} - \sigma_{u'-1}) \nu_{u'} + (t-\sigma_{u-1}) \nu_{u}

            for `0<\sigma_1<\sigma_2<\cdots<\sigma_s=1` and `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`.
            Each weight `\nu_u` is also associated to a Weyl group element. This method returns the list
            of Weyl group elements associated to the `\nu_u` for `1\le u\le s`.

            EXAMPLES::

                sage: R = RootSystem(['C',3,1])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])
                sage: b = LS.module_generators[0]
                sage: c = b.f(1).f(3).f(2)
                sage: c.weyl_group_representation()
                [s2*s3*s1, s3*s1]
            """
            weight = self.parent().weight
            level = -weight.coefficient(0)
            La = weight.parent().fundamental_weights()
            cartan = weight.parent().cartan_type().classical()
            W = WeylGroup(cartan, prefix="s")
            scalars = [0] + self.scalar_factors()
            l = [
                (self.value[i] / (scalars[i + 1] - scalars[i]) + level * La[0]).to_dominant_chamber(reduced_word=True)
                for i in range(len(scalars) - 1)
            ]
            return [W.from_reduced_word(a[1]) for a in l]
Пример #7
0
    def __classcall_private__(cls, W, q1, q2=-1, base_ring=None, prefix="T"):
        """
        TESTS::

            sage: H = IwahoriHeckeAlgebraT("A2", 1)
            sage: H.coxeter_group() == WeylGroup("A2")
            True
            sage: H.cartan_type() == CartanType("A2")
            True
            sage: H.base_ring() == ZZ
            True
            sage: H._q2 == -1
            True
        """
        if W not in CoxeterGroups():
            W = WeylGroup(W)
        if base_ring is None:
            base_ring = q1.parent()
        q2 = base_ring(q2)
        return super(IwahoriHeckeAlgebraT,
                     cls).__classcall__(cls,
                                        W,
                                        q1=q1,
                                        q2=q2,
                                        base_ring=base_ring,
                                        prefix=prefix)
Пример #8
0
    def weyl_group(self):
        """
        Return the Weyl group of ``self``.

        EXAMPLES::

            sage: L = LieAlgebra(QQ, cartan_type=['A', 2])
            sage: L.weyl_group()
            Weyl Group of type ['A', 2] (as a matrix group acting on the ambient space)
        """
        from sage.combinat.root_system.weyl_group import WeylGroup
        return WeylGroup(self._cartan_type)
Пример #9
0
    def __classcall__(cls, W):
        """
        EXAMPLES::

            sage: from sage.monoids.j_trivial_monoids import *
            sage: HeckeMonoid(['A',3]).cardinality()
            24
        """
        from sage.categories.coxeter_groups import CoxeterGroups
        if not W in CoxeterGroups():
            from sage.combinat.root_system.weyl_group import WeylGroup
            W = WeylGroup(W)  # CoxeterGroup(W)
        return super(PiMonoid, cls).__classcall__(cls, W)
Пример #10
0
    def __init__(self, n, R = ZZ, prefix = 'a'):
        """
        Initiates the affine nilTemperley Lieb algebra over the ring `R`.

        EXAMPLES::

            sage: A = AffineNilTemperleyLiebTypeA(3, prefix="a"); A
            The affine nilTemperley Lieb algebra A3 over the ring Integer Ring
            sage: TestSuite(A).run()
            sage: A = AffineNilTemperleyLiebTypeA(3, QQ); A
            The affine nilTemperley Lieb algebra A3 over the ring Rational Field
        """
        if not isinstance(R, Ring):
            raise TypeError("Argument R must be a ring.")
        self._cartan_type = CartanType(['A',n-1,1])
        self._n = n
        W = WeylGroup(self._cartan_type)
        self._prefix = prefix
        self._index_set = W.index_set()
        self._base_ring = R
        category = AlgebrasWithBasis(R)
        CombinatorialFreeModule.__init__(self, R, W, category = category)
Пример #11
0
    def __init__(self, n, R=ZZ, prefix='a'):
        """
        Initiates the affine nilTemperley Lieb algebra over the ring `R`.

        EXAMPLES::

            sage: A = AffineNilTemperleyLiebTypeA(3, prefix="a"); A
            The affine nilTemperley Lieb algebra A3 over the ring Integer Ring
            sage: TestSuite(A).run()
            sage: A = AffineNilTemperleyLiebTypeA(3, QQ); A
            The affine nilTemperley Lieb algebra A3 over the ring Rational Field
        """
        if not isinstance(R, Ring):
            raise TypeError("Argument R must be a ring.")
        self._cartan_type = CartanType(['A', n - 1, 1])
        self._n = n
        W = WeylGroup(self._cartan_type)
        self._prefix = prefix
        self._index_set = W.index_set()
        self._base_ring = R
        category = AlgebrasWithBasis(R)
        CombinatorialFreeModule.__init__(self, R, W, category=category)
Пример #12
0
    def weyl_group(self, prefix=None):
        """
        Returns the Weyl group associated to self.

        EXAMPLES::
        
            sage: RootSystem(['F',4]).ambient_space().weyl_group()
            Weyl Group of type ['F', 4] (as a matrix group acting on the ambient space)
            sage: RootSystem(['F',4]).root_space().weyl_group()
            Weyl Group of type ['F', 4] (as a matrix group acting on the root space)

        """
        from sage.combinat.root_system.weyl_group import WeylGroup
        return WeylGroup(self, prefix=prefix)
Пример #13
0
    def __classcall_private__(cls, w, n, x = None, k = None):
        r"""
        Classcall to mend the input.

        TESTS::

            sage: A = crystals.AffineFactorization([[3,1],[1]], 4, k=3); A
            Crystal on affine factorizations of type A3 associated to s3*s2*s1
            sage: AC = crystals.AffineFactorization([Core([4,1],4),Core([1],4)], 4, k=3)
            sage: AC is A
            True
        """
        if k is not None:
            from sage.combinat.core import Core
            from sage.combinat.partition import Partition
            W = WeylGroup(['A',k,1], prefix='s')
            if isinstance(w[0], Core):
                w = [w[0].to_bounded_partition(), w[1].to_bounded_partition()]
            else:
                w = [Partition(w[0]), Partition(w[1])]
            w0 = W.from_reduced_word(w[0].from_kbounded_to_reduced_word(k))
            w1 = W.from_reduced_word(w[1].from_kbounded_to_reduced_word(k))
            w = w0*(w1.inverse())
        return super(AffineFactorizationCrystal, cls).__classcall__(cls, w, n, x)
Пример #14
0
    def maximal_elements(self):
        r"""
        The current algorithm uses the fact that the maximal Pieri factors
        of affine type A,B,C, or D either contain a finite Weyl group
        element, or contain an affine Weyl group element whose reflection
        by `s_0` gets a finite Weyl group element, and that either of
        these finite group elements will serve as a maximal element for
        finite Pieri factors. A better algorithm is desirable.

        EXAMPLES::

            sage: PF = WeylGroup(['A',5]).pieri_factors()
            sage: [v.reduced_word() for v in PF.maximal_elements()]
            [[5, 4, 3, 2, 1]]

            sage: WeylGroup(['B',4]).pieri_factors().maximal_elements()
            [
            [-1  0  0  0]
            [ 0  1  0  0]
            [ 0  0  1  0]
            [ 0  0  0  1]
            ]
        """
        ct = self.W.cartan_type()

        # The following line may need to be changed when generalizing to more than types A and B.
        if ct.type() != 'A' and ct.type() != 'B':
            raise NotImplementedError(
                "currently only implemented for finite types A and B")

        ct_aff = ct.dual().affine()

        max_elts_affine = WeylGroup(ct_aff).pieri_factors().maximal_elements()

        for w in max_elts_affine:
            if 0 not in w.reduced_word():
                return [self.W.from_reduced_word(w.reduced_word())]
        for w in max_elts_affine:
            if 0 not in w.apply_simple_reflection(0).reduced_word():
                return [
                    self.W.from_reduced_word(
                        w.apply_simple_reflection(0).reduced_word())
                ]
Пример #15
0
    def stanley_symm_poly_weight(self,w):
        r"""
        Weight used in computing Stanley symmetric polynomials of type
        `B`.  The weight for finite type B is the number of components
        of the support of an element minus the number of occurrences
        of `n` in a reduced word.

        EXAMPLES::

            sage: W = WeylGroup(['B',5])
            sage: PF = W.pieri_factors()
            sage: PF.stanley_symm_poly_weight(W.from_reduced_word([3,1,5]))
            2
            sage: PF.stanley_symm_poly_weight(W.from_reduced_word([3,4,5]))
            0
            sage: PF.stanley_symm_poly_weight(W.from_reduced_word([1,2,3,4,5,4]))
            0
        """
        r = w.reduced_word().count(self.W.n)
        return WeylGroup(self.W.cartan_type().dual().affine()).pieri_factors().stanley_symm_poly_weight(w) - r
Пример #16
0
def CoxeterGroup(data,
                 implementation="reflection",
                 base_ring=None,
                 index_set=None):
    """
    Return an implementation of the Coxeter group given by ``data``.

    INPUT:

    - ``data`` -- a Cartan type (or coercible into; see :class:`CartanType`)
      or a Coxeter matrix or graph

    - ``implementation`` -- (default: ``'reflection'``) can be one of
      the following:

      * ``'permutation'`` - as a permutation representation
      * ``'matrix'`` - as a Weyl group (as a matrix group acting on the
        root space); if this is not implemented, this uses the "reflection"
        implementation
      * ``'coxeter3'`` - using the coxeter3 package
      * ``'reflection'`` - as elements in the reflection representation; see
        :class:`~sage.groups.matrix_gps.coxeter_groups.CoxeterMatrixGroup`

    - ``base_ring`` -- (optional) the base ring for the ``'reflection'``
      implementation

    - ``index_set`` -- (optional) the index set for the ``'reflection'``
      implementation

    EXAMPLES:

    Now assume that ``data`` represents a Cartan type. If
    ``implementation`` is not specified, the reflection representation
    is returned::

        sage: W = CoxeterGroup(["A",2])
        sage: W
        Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
        [1 3]
        [3 1]

        sage: W = CoxeterGroup(["A",3,1]); W
        Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
        [1 3 2 3]
        [3 1 3 2]
        [2 3 1 3]
        [3 2 3 1]

        sage: W = CoxeterGroup(['H',3]); W
        Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
        [1 3 2]
        [3 1 5]
        [2 5 1]

    We now use the ``implementation`` option::

        sage: W = CoxeterGroup(["A",2], implementation = "permutation") # optional - chevie
        sage: W                                                         # optional - chevie
        Permutation Group with generators [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6)]
        sage: W.category()                       # optional - chevie
        Join of Category of finite permutation groups and Category of finite coxeter groups

        sage: W = CoxeterGroup(["A",2], implementation="matrix")
        sage: W
        Weyl Group of type ['A', 2] (as a matrix group acting on the ambient space)

        sage: W = CoxeterGroup(["H",3], implementation="matrix")
        sage: W
        Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
        [1 3 2]
        [3 1 5]
        [2 5 1]

        sage: W = CoxeterGroup(["H",3], implementation="reflection")
        sage: W
        Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix:
        [1 3 2]
        [3 1 5]
        [2 5 1]

        sage: W = CoxeterGroup(["A",4,1], implementation="permutation")
        Traceback (most recent call last):
        ...
        NotImplementedError: Coxeter group of type ['A', 4, 1] as permutation group not implemented

    We use the different options for the "reflection" implementation::

        sage: W = CoxeterGroup(["H",3], implementation="reflection", base_ring=RR)
        sage: W
        Finite Coxeter group over Real Field with 53 bits of precision with Coxeter matrix:
        [1 3 2]
        [3 1 5]
        [2 5 1]
        sage: W = CoxeterGroup([[1,10],[10,1]], implementation="reflection", index_set=['a','b'], base_ring=SR)
        sage: W
        Finite Coxeter group over Symbolic Ring with Coxeter matrix:
        [ 1 10]
        [10  1]

    TESTS::

        sage: W = groups.misc.CoxeterGroup(["H",3])
    """
    if implementation not in [
            "permutation", "matrix", "coxeter3", "reflection", None
    ]:
        raise ValueError("invalid type implementation")

    try:
        cartan_type = CartanType(data)
    except (
            TypeError, ValueError
    ):  # If it is not a Cartan type, try to see if we can represent it as a matrix group
        return CoxeterMatrixGroup(data, base_ring, index_set)

    if implementation is None:
        implementation = "matrix"

    if implementation == "reflection":
        return CoxeterMatrixGroup(cartan_type, base_ring, index_set)
    if implementation == "coxeter3":
        try:
            from sage.libs.coxeter3.coxeter_group import CoxeterGroup
        except ImportError:
            raise RuntimeError("coxeter3 must be installed")
        else:
            return CoxeterGroup(cartan_type)
    if implementation == "permutation" and is_chevie_available() and \
       cartan_type.is_finite() and cartan_type.is_irreducible():
        return CoxeterGroupAsPermutationGroup(cartan_type)
    elif implementation == "matrix":
        if cartan_type.is_crystallographic():
            return WeylGroup(cartan_type)
        return CoxeterMatrixGroup(cartan_type, base_ring, index_set)

    raise NotImplementedError(
        "Coxeter group of type {} as {} group not implemented".format(
            cartan_type, implementation))
Пример #17
0
        def energy_function(self):
            r"""
            Return the energy function of ``self``.

            The energy function `D(\pi)` of the level zero LS path `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)`
            requires a series of definitions; for simplicity the root system is assumed to be untwisted affine.

            The LS path `\pi` is a piecewise linear map from the unit interval `[0,1]` to the weight lattice.
            It is specified by "times" `0=\sigma_0<\sigma_1<\dotsm<\sigma_s=1` and "direction vectors"
            `x_u \lambda` where `x_u \in W/W_J` for `1\le u\le s`, and `W_J` is the
            stabilizer of `\lambda` in the finite Weyl group `W`. Precisely,

            .. MATH::

                \pi(t)=\sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1})x_{u'}\lambda+(t-\sigma_{u-1})x_{u}\lambda

            for `1\le u\le s` and `\sigma_{u-1} \le t \le \sigma_{u}`.

            For any `x,y\in W/W_J` let

            .. MATH::

                d: x= w_{0} \stackrel{\beta_{1}}{\leftarrow}
                w_{1} \stackrel{\beta_{2}}{\leftarrow} \cdots
                \stackrel{\beta_{n}}{\leftarrow} w_{n}=y

            be a shortest directed path in the parabolic quantum Bruhat graph. Define

            .. MATH::

                \mathrm{wt}(d):=\sum_{\substack{1\le k\le n \\  \ell(w_{k-1})<\ell(w_k)}}
                \beta_{k}^{\vee}

            It can be shown that `\mathrm{wt}(d)` depends only on `x,y`;
            call its value `\mathrm{wt}(x,y)`. The energy function `D(\pi)` is defined by

            .. MATH::

                D(\pi)=-\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda,\mathrm{wt}(x_u,x_{u+1}) \rangle

            For more information, see [LNSSS2013]_.

            REFERENCES:

            .. [LNSSS2013] C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono,
               A uniform model for Kirillov-Reshetikhin crystals. Extended abstract.
               DMTCS proc, to appear ( {{{:arXiv:`1211.6019`}}} )

            .. NOTE::

                In the dual-of-untwisted case the parabolic quantum Bruhat graph that is used is obtained by
                exchanging the roles of roots and coroots. Moreover, in the computation of the
                pairing the short roots must be doubled (or tripled for type `G`). This factor
                is determined by the translation factor of the corresponding root.
                Type `BC` is viewed as untwisted type, whereas the dual of `BC` is viewed as twisted.
                Except for the untwisted cases, these formulas are currently still conjectural.

            EXAMPLES::

                sage: R = RootSystem(['C',3,1])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])
                sage: b = LS.module_generators[0]
                sage: c = b.f(1).f(3).f(2)
                sage: c.energy_function()
                0
                sage: c=b.e(0)
                sage: c.energy_function()
                1

                sage: R = RootSystem(['A',2,1])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1])
                sage: b = LS.module_generators[0]
                sage: c = b.e(0)
                sage: c.energy_function()
                1
                sage: [c.energy_function() for c in sorted(LS.list())]
                [0, 1, 0, 0, 0, 1, 0, 1, 0]

            The next test checks that the energy function is constant on classically connected components::

                sage: R = RootSystem(['A',2,1])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])
                sage: G = LS.digraph(index_set=[1,2])
                sage: C = G.connected_components()
                sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]
                [True, True, True, True]

                sage: R = RootSystem(['D',4,2])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[2])
                sage: J = R.cartan_type().classical().index_set()
                sage: hw = [x for x in LS if x.is_highest_weight(J)]
                sage: [(x.weight(), x.energy_function()) for x in hw]
                [(-2*Lambda[0] + Lambda[2], 0), (-2*Lambda[0] + Lambda[1], 1), (0, 2)]
                sage: G = LS.digraph(index_set=J)
                sage: C = G.connected_components()
                sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]
                [True, True, True]

                sage: R = RootSystem(CartanType(['G',2,1]).dual())
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])
                sage: G = LS.digraph(index_set=[1,2])
                sage: C = G.connected_components()
                sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]
                [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]

                sage: ct = CartanType(['BC',2,2]).dual()
                sage: R = RootSystem(ct)
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])
                sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set())
                sage: C = G.connected_components()
                sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]
                [True, True, True, True, True, True, True, True, True, True, True]

                sage: R = RootSystem(['BC',2,2])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])
                sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set())
                sage: C = G.connected_components()
                sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]
                [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True,
                True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]
            """
            weight = self.parent().weight
            P = weight.parent()
            c_weight = P.classical()(weight)
            ct = P.cartan_type()
            cartan = ct.classical()
            Qv = RootSystem(cartan).coroot_lattice()
            W = WeylGroup(cartan,prefix='s')
            J = tuple(weight.weyl_stabilizer())
            L = self.weyl_group_representation()
            if ct.is_untwisted_affine() or ct.type() == 'BC':
                untwisted = True
                G = W.quantum_bruhat_graph(J)
            else:
                untwisted = False
                cartan_dual = cartan.dual()
                Wd = WeylGroup(cartan_dual, prefix='s')
                G = Wd.quantum_bruhat_graph(J)
                Qd = RootSystem(cartan_dual).root_lattice()
                dualize = lambda x: Qv.from_vector(x.to_vector())
                L = [Wd.from_reduced_word(x.reduced_word()) for x in L]
                def stretch_short_root(a):
                    # stretches roots by translation factor
                    if ct.dual().type() == 'BC':
                        return ct.c()[a.to_simple_root()]*a
                    return ct.dual().c()[a.to_simple_root()]*a
                    #if a.is_short_root():
                    #    if cartan_dual.type() == 'G':
                    #        return 3*a
                    #    else:
                    #        return 2*a
                    #return a
            paths = [G.shortest_path(L[i+1],L[i]) for i in range(len(L)-1)]
            paths_labels = [[G.edge_label(p[i],p[i+1]) for i in range(len(p)-1) if p[i].length()+1 != p[i+1].length()] for p in paths]
            scalars = self.scalar_factors()
            if untwisted:
                s = sum((1-scalars[i])*c_weight.scalar( Qv.sum(root.associated_coroot()
                       for root in paths_labels[i]) ) for i in range(len(paths_labels)))
                if ct.type() == 'BC':
                    return 2*s
                else:
                    return s
            else:
                s = sum((1-scalars[i])*c_weight.scalar( dualize (Qd.sum(stretch_short_root(root) for root in paths_labels[i])) ) for i in range(len(paths_labels)))
                if ct.dual().type() == 'BC':
                    return s/2
                else:
                    return s
Пример #18
0
def CoxeterGroup(cartan_type, implementation=None):
    """
    INPUT:

     - ``cartan_type`` -- a cartan type (or coercible into; see :class:`CartanType`)
     - ``implementation`` -- "permutation", "matrix", "coxeter3", or None (default: None)

    Returns an implementation of the Coxeter group of type
    ``cartan_type``.

    EXAMPLES:

    If ``implementation`` is not specified, a permutation
    representation is returned whenever possible (finite irreducible
    Cartan type, with the GAP3 Chevie package available)::

        sage: W = CoxeterGroup(["A",2])
        sage: W                                   # optional - chevie
        Permutation Group with generators [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6)]

    Otherwise, a Weyl group is returned::

        sage: W = CoxeterGroup(["A",3,1])
        sage: W
        Weyl Group of type ['A', 3, 1] (as a matrix group acting on the root space)

    We now use the ``implementation`` option::

        sage: W = CoxeterGroup(["A",2], implementation = "permutation") # optional - chevie
        sage: W                                                         # optional - chevie
        Permutation Group with generators [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6)]
        sage: W.category()                       # optional - chevie
        Join of Category of finite permutation groups and Category of finite coxeter groups

        sage: W = CoxeterGroup(["A",2], implementation = "matrix")
        sage: W
        Weyl Group of type ['A', 2] (as a matrix group acting on the ambient space)

        sage: W = CoxeterGroup(["H",3], implementation = "matrix")
        Traceback (most recent call last):
        ...
        NotImplementedError: Coxeter group of type ['H', 3] as matrix group not implemented

        sage: W = CoxeterGroup(["A",4,1], implementation = "permutation")
        Traceback (most recent call last):
        ...
        NotImplementedError: Coxeter group of type ['A', 4, 1] as permutation group not implemented

    """
    assert implementation in ["permutation", "matrix", "coxeter3", None]
    cartan_type = CartanType(cartan_type)

    if implementation is None:
        if cartan_type.is_finite() and cartan_type.is_irreducible(
        ) and is_chevie_available():
            implementation = "permutation"
        else:
            implementation = "matrix"

    if implementation == "coxeter3":
        try:
            from sage.libs.coxeter3.coxeter_group import CoxeterGroup
        except ImportError:
            raise RuntimeError, "coxeter3 must be installed"
        else:
            return CoxeterGroup(cartan_type)
    if implementation == "permutation" and is_chevie_available() and \
       cartan_type.is_finite() and cartan_type.is_irreducible():
        return CoxeterGroupAsPermutationGroup(cartan_type)
    elif implementation == "matrix" and cartan_type.is_crystalographic():
        return WeylGroup(cartan_type)
    else:
        raise NotImplementedError, "Coxeter group of type %s as %s group not implemented " % (
            cartan_type, implementation)
Пример #19
0
        def string_parameters(self, word=None):
            r"""
            Return the string parameters of ``self`` corresponding to the
            reduced word ``word``.

            Given a reduced expression `w = s_{i_1} \cdots s_{i_k}`,
            the string parameters of `b \in B` corresponding to `w`
            are `(a_1, \ldots, a_k)` such that

            .. MATH::

                \begin{aligned}
                e_{i_m}^{a_m} \cdots e_{i_1}^{a_1} b & \neq 0 \\
                e_{i_m}^{a_m+1} \cdots e_{i_1}^{a_1} b & = 0
                \end{aligned}

            for all `1 \leq m \leq k`.

            For connected components isomorphic to `B(\lambda)` or
            `B(\infty)`, if `w = w_0` is the longest element of the
            Weyl group, then the path determined by the string
            parametrization terminates at the highest weight vector.

            INPUT:

            - ``word`` -- a word in the alphabet of the index set; if not
              specified and we are in finite type, then this will be some
              reduced expression for the long element determined by the
              Weyl group

            EXAMPLES::

                sage: B = crystals.infinity.NakajimaMonomials(['A',3])
                sage: mg = B.highest_weight_vector()
                sage: w0 = [1,2,1,3,2,1]
                sage: mg.string_parameters(w0)
                [0, 0, 0, 0, 0, 0]
                sage: mg.f_string([1]).string_parameters(w0)
                [1, 0, 0, 0, 0, 0]
                sage: mg.f_string([1,1,1]).string_parameters(w0)
                [3, 0, 0, 0, 0, 0]
                sage: mg.f_string([1,1,1,2,2]).string_parameters(w0)
                [1, 2, 2, 0, 0, 0]
                sage: mg.f_string([1,1,1,2,2]) == mg.f_string([1,1,2,2,1])
                True
                sage: x = mg.f_string([1,1,1,2,2,1,3,3,2,1,1,1])
                sage: x.string_parameters(w0)
                [4, 1, 1, 2, 2, 2]
                sage: x.string_parameters([3,2,1,3,2,3])
                [2, 3, 7, 0, 0, 0]
                sage: x == mg.f_string([1]*7 + [2]*3 + [3]*2)
                True

            ::

                sage: B = crystals.infinity.Tableaux("A5")
                sage: b = B(rows=[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,6,6,6,6,6,6],
                ....:             [2,2,2,2,2,2,2,2,2,4,5,5,5,6],
                ....:             [3,3,3,3,3,3,3,5],
                ....:             [4,4,4,6,6,6],
                ....:             [5,6]])
                sage: b.string_parameters([1,2,1,3,2,1,4,3,2,1,5,4,3,2,1])
                [0, 1, 1, 1, 1, 0, 4, 4, 3, 0, 11, 10, 7, 7, 6]

                sage: B = crystals.infinity.Tableaux("G2")
                sage: b = B(rows=[[1,1,1,1,1,3,3,0,-3,-3,-2,-2,-1,-1,-1,-1],[2,3,3,3]])
                sage: b.string_parameters([2,1,2,1,2,1])
                [5, 13, 11, 15, 4, 4]
                sage: b.string_parameters([1,2,1,2,1,2])
                [7, 12, 15, 8, 10, 0]

            ::

                sage: C = crystals.Tableaux(['C',2], shape=[2,1])
                sage: mg = C.highest_weight_vector()
                sage: lw = C.lowest_weight_vectors()[0]
                sage: lw.string_parameters([1,2,1,2])
                [1, 2, 3, 1]
                sage: lw.string_parameters([2,1,2,1])
                [1, 3, 2, 1]
                sage: lw.e_string([2,1,1,1,2,2,1]) == mg
                True
                sage: lw.e_string([1,2,2,1,1,1,2]) == mg
                True

            TESTS::

                sage: B = crystals.infinity.NakajimaMonomials(['B',3])
                sage: mg = B.highest_weight_vector()
                sage: mg.string_parameters()
                [0, 0, 0, 0, 0, 0, 0, 0, 0]
                sage: w0 = WeylGroup(['B',3]).long_element().reduced_word()
                sage: def f_word(params):
                ....:     return reversed([index for i, index in enumerate(w0)
                ....:                      for _ in range(params[i])])
                sage: all(mg.f_string( f_word(x.value.string_parameters(w0)) ) == x.value
                ....:     for x in B.subcrystal(max_depth=4))
                True

                sage: B = crystals.infinity.NakajimaMonomials(['A',2,1])
                sage: mg = B.highest_weight_vector()
                sage: mg.string_parameters()
                Traceback (most recent call last):
                ...
                ValueError: the word must be specified because the
                 Weyl group is not finite
            """
            if word is None:
                if not self.cartan_type().is_finite():
                    raise ValueError("the word must be specified because"
                                     " the Weyl group is not finite")
                from sage.combinat.root_system.weyl_group import WeylGroup
                word = WeylGroup(
                    self.cartan_type()).long_element().reduced_word()
            x = self
            params = []
            for i in word:
                count = 0
                y = x.e(i)
                while y is not None:
                    x = y
                    y = x.e(i)
                    count += 1
                params.append(count)
            return params
Пример #20
0
class WeightSet:
    """Class to do simple computations with the weights of a weight module.

    Parameters
    ----------
    root_system : str
        String representing the root system (e.g. 'A2')

    Attributes
    ----------
    root_system : str
        String representing the root system (e.g. 'A2')
    W : WeylGroup
        Object encoding the Weyl group.
    weyl_dic : dict(str, WeylGroup.element_class)
        Dictionary mapping strings representing a Weyl group element as reduced word
        in simple reflections, to the Weyl group element.
    reduced_words : list[str]
        Sorted list of all strings representing Weyl group elements as reduced word in
        simple reflections.
    simple_roots : list[RootSpace.element_class]
        List of the simple roots
    rank : int
        Rank of root system
    rho : RootSpace.element_class
        Half the sum of all positive roots
    pos_roots : List[RootSpace.element_class]
        List of all the positive roots
    action_dic : Dict[str, np.array(np.int32, np.int32)]
        dictionary mapping each string representing an
        element of the Weyl group to a matrix expressing the action on the simple roots.
    rho_action_dic : Dict[str, np.array(np.int32)]
        dictionary mapping each string representing an
        element of the Weyl group to a vector representing the image
        of the dot action on rho.
    """
    @classmethod
    def from_bgg(cls, BGG):
        """Initialize from an instance of BGGComplex.

        Some data can be reused, and this gives roughly 3x faster initialization.
        
        Parameters
        ----------
        BGG : BGGComplex
            The BGGComplex to initialize from.
        """
        hot_start = {"W": BGG.W, "weyl_dic": BGG.reduced_word_dic}
        return cls(BGG.root_system, hot_start=hot_start)

    def __init__(self, root_system, hot_start=None):
        self.root_system = root_system

        if hot_start is None:
            self.W = WeylGroup(root_system)
            self.weyl_dic = self._compute_weyl_dictionary()
        else:
            self.W = hot_start["W"]
            self.weyl_dic = hot_start["weyl_dic"]

        self.domain = self.W.domain()

        self.reduced_words = sorted(self.weyl_dic.keys(), key=len)

        self.simple_roots = self.domain.simple_roots().values()
        self.rank = len(self.simple_roots)
        self.rho = self.domain.rho()

        self.pos_roots = self.domain.positive_roots()

        # Matrix of all simple roots, for faster matrix solving
        self.simple_root_matrix = matrix(
            [list(s.to_vector()) for s in self.simple_roots]).transpose()

        self.action_dic, self.rho_action_dic = self.get_action_dic()

    def _compute_weyl_dictionary(self):
        """Construct a dictionary enumerating all of the elements of the Weyl group.

        The keys are reduced words of the elements
        """
        reduced_word_dic = {
            "".join([str(s) for s in g.reduced_word()]): g
            for g in self.W
        }
        return reduced_word_dic

    def weight_to_tuple(self, weight):
        """Convert element of weight lattice to a sum of simple roots.

        Parameters
        ----------
        weight : RootSpace.element_class

        Returns
        -------
        tuple[int]
            tuple representing root as linear combination of simple roots
        """
        b = weight.to_vector()
        b = matrix(b).transpose()
        return tuple(self.simple_root_matrix.solve_right(b).transpose().list())

    def tuple_to_weight(self, t):
        """Inverse of `weight_to_tuple`.
        
        Parameters
        ----------
        t : tuple[int]

        Returns
        -------
        RootSpace.element_class
        """
        return sum(int(a) * b for a, b in zip(t, self.simple_roots))

    def get_action_dic(self):
        """Compute weyl group action as well as action on rho.

        Returns
        -------
        Dict[str, np.array(np.int32, np.int32)] : dictionary mapping each string representing an
            element of the Weyl group to a matrix expressing the action on the simple roots.
        Dict[str, np.array(np.int32)] : dictionary mapping each string representing an
            element of the Weyl group to a vector representing the image
            of the dot action on rho.
        """
        action_dic = dict()
        rho_action_dic = dict()
        for s, w in self.weyl_dic.items():  # s is a string, w is a matrix
            # Compute action of w on every simple root, decompose result in simple roots, encode result as matrix.
            action_mat = []
            for mu in self.simple_roots:
                action_mat.append(self.weight_to_tuple(w.action(mu)))
            action_dic[s] = np.array(action_mat, dtype=np.int32)

            # Encode the dot action of w on rho.
            rho_action_dic[s] = np.array(
                self.weight_to_tuple(w.action(self.rho) - self.rho),
                dtype=np.int32)
        return action_dic, rho_action_dic

    def dot_action(self, w, mu):
        r"""Compute the dot action of w on mu.

        The dot action :math:`w\cdot\mu = w(\mu+\rho)-\rho`, with :math:`\rho` half
        the sum of all the positive roots.
        
        Parameters
        ----------
        w : str
            string representing the weyl group element
        mu : iterable(int)
            the weight

        Returns
        -------
        np.array[np.int32]
            vector encoding the new weight
        """
        # The dot action w.mu = w(mu+rho)-rho = w*mu + (w*rho-rho).
        # The former term is given by action_dic, the latter by rho_action_dic
        return (np.matmul(self.action_dic[w].T, np.array(mu, dtype=np.int32)) +
                self.rho_action_dic[w])

    def dot_orbit(self, mu):
        """Compute the orbit of the Weyl group action on a weight.

        Parameters
        ----------
        mu : iterable(int)
            A weight

        Returns
        -------
        dict(str, np.array[np.int32])
            Dictionary mapping Weyl group elements to weights encoded as numpy vectors.
        """
        return {w: self.dot_action(w, mu) for w in self.reduced_words}

    def is_dot_regular(self, mu):
        """Check if mu has a non-trivial stabilizer under the dot action.
        
        Parameters
        ----------
        mu : iterable(int)
            The weight

        Returns
        -------
        bool
            `True` if the weight is dot-regular
        """
        for s in self.reduced_words[1:]:
            if np.all(self.dot_action(s, mu) == mu):
                return False
        # no stabilizer found
        return True

    def compute_weights(self, weights):
        """Find dot-regular weights and associated dominant weights of a set of weights.

        Parameters
        ----------
        weights : iterable(iterable(int))
            Iterable of weights
        
        returns
        -------
        list(tuple[tuple(int), tuple(int), int])
            list of triples consisting of
            dot-regular weight, associated dominant, and the length of the Weyl group
            element making the weight dominant under the dot action.
        """
        regular_weights = []
        for mu in weights:
            if self.is_dot_regular(mu):
                mu_prime, w = self.make_dominant(mu)
                regular_weights.append((mu, tuple(mu_prime), len(w)))
        return regular_weights

    def is_dominant(self, mu):
        """Use sagemath built-in function to check if weight is dominant.
        
        Parameters
        ----------
        mu : iterable(int)
            the weight

        Returns
        -------
        bool
            `True` if weight is dominant
        """
        return self.tuple_to_weight(mu).is_dominant()

    def make_dominant(self, mu):
        """For a dot-regular weight mu, w such that if w.mu is dominant.
        
         Such a w exists iff mu is dot-regular, in which case it is also unique.

        Parameters
        ----------
        mu : iterable(int)
            the dot-regular weight
        
        Returns
        -------
        tuple(int)
            The dominant weight w.mu
        str
            the string representing the Weyl group element w.
        """
        for w in self.reduced_words:
            new_mu = self.dot_action(w, mu)
            if self.is_dominant(new_mu):
                return new_mu, w
        else:
            raise ValueError(
                "Could not make weight %s dominant, probably it is not dot-regular."
            )

    def get_vertex_weights(self, mu):
        """For a given dot-regular mu, return its orbit under the dot-action.
        
        Parameters
        ----------
        mu : iterable(int)

        Returns
        -------
        list[tuple[int]]
            list of weights
        """
        vertex_weights = dict()
        for w in self.reduced_words:
            vertex_weights[w] = tuple(self.dot_action(w, mu))
        return vertex_weights

    def highest_weight_rep_dim(self, mu):
        """Give dimension of highest weight representation of integral dominant weight.

        Parameters
        ----------
        mu : tuple(int)
            A integral dominant weight

        Returns
        -------
        int
            dimension of highest weight representation.
        """
        mu_weight = self.tuple_to_weight(mu)
        numerator = 1
        denominator = 1
        for alpha in self.pos_roots:
            numerator *= (mu_weight + self.rho).dot_product(alpha)
            denominator *= self.rho.dot_product(alpha)
        return numerator // denominator
Пример #21
0
class BGGComplex:
    """A class encoding all the things we need of the BGG complex.
    
    Parameters
    ----------
    root_system : str
        String encoding the Dynkin diagram of the root system (e.g. `'A3'`)
    pickle_directory : str (optional)
            Directory where to store `.pkl` files to save computations
            regarding maps in the BGG complex. If `None`, maps are not saved. (default: `None`)


    Attributes
    -------
    root_system : str
        String encoding Dynkin diagram.
    W : WeylGroup
        Object encoding the Weyl group.
    LA : LieAlgebraChevalleyBasis
        Object encoding the Lie algebra in the Chevalley basis over Q
    PBW : PoincareBirkhoffWittBasis
        Poincaré-Birkhoff-Witt basis of the universal enveloping 
        algebra. The maps in the BGG complex are elements of this basis. This package uses
        a slight modification of the PBW class as implemented in Sagemath proper.
    lattice : RootSpace
        The space of roots
    S : FiniteFamily
        Set of simple reflections in the Weyl group
    T : FIniteFamily
        Set of reflections in the Weyl group
    cycles : List 
        List of 4-tuples encoding the length-4 cycles in the Bruhat graph
    simple_roots : List
        (Ordered) list of the simple roots as elements of `lattice`
    rank : int
        Rank of the root system
    neg_roots : list
        List of the negative roots in the root system, encoded as `numpy.ndarray`.
    alpha_to_index : dict
        Dictionary mapping negative roots (as elements of `lattice`) to
        an ordered index encoding the negative root as basis element of the lie algebra $n$
        spanned by the negative roots.
    zero_root : element of `lattice`
        The zero root
    rho : element of `lattice`
        Half the sum of all positive roots
    """
    def __init__(self, root_system, pickle_directory=None):
        self.root_system = root_system
        self.W = WeylGroup(root_system)
        self.domain = self.W.domain()
        self.LA = LieAlgebra(QQ, cartan_type=root_system)
        self.PBW = PoincareBirkhoffWittBasis(self.LA,
                                             None,
                                             "PBW",
                                             cache_degree=5)
        # self.PBW = self.LA.pbw_basis()
        self.PBW_alg_gens = self.PBW.algebra_generators()
        self.lattice = self.domain.root_system.root_lattice()
        self.S = self.W.simple_reflections()
        self.T = self.W.reflections()
        self.signs = None
        self.cycles = None

        self._compute_weyl_dictionary()
        self._construct_BGG_graph()

        self.find_cycles()

        self.simple_roots = self.domain.simple_roots().values()
        self.rank = len(self.simple_roots)

        # for PBW computations we need to put the right order on the negative roots.
        # This order coincides with that of the sagemath source code.
        lie_alg_order = {k: i for i, k in enumerate(self.LA.basis().keys())}
        ordered_roots = sorted(
            [
                self._weight_to_alpha_sum(r)
                for r in self.domain.negative_roots()
            ],
            key=lambda rr: lie_alg_order[rr],
        )
        self.neg_roots = [-self._alpha_sum_to_array(r) for r in ordered_roots]

        self.alpha_to_index = {
            self._weight_to_alpha_sum(-self._tuple_to_weight(r)): i
            for i, r in enumerate(self.neg_roots)
        }
        self.zero_root = self.domain.zero()

        self.pickle_directory = pickle_directory
        if pickle_directory is None:
            self.pickle_maps = False
        else:
            self.pickle_maps = True

        if self.pickle_maps:
            self._maps = self._read_maps()
        else:
            self._maps = dict()

        self.rho = self.domain.rho()

        self._action_dic = dict()
        for s, w in self.reduced_word_dic.items():
            self._action_dic[s] = {
                i: self._weight_to_alpha_sum(w.action(mu))
                for i, mu in dict(self.domain.simple_roots()).items()
            }
        self._rho_action_dic = dict()
        for s, w in self.reduced_word_dic.items():
            self._rho_action_dic[s] = self._weight_to_alpha_sum(
                w.action(self.rho) - self.rho)

    def _compute_weyl_dictionary(self):
        """Construct a dictionary enumerating all of the elements of the Weyl group."""
        self.reduced_word_dic = {
            "".join([str(s) for s in g.reduced_word()]): g
            for g in self.W
        }
        self.reduced_word_dic_reversed = dict(
            [[v, k] for k, v in self.reduced_word_dic.items()])
        self.reduced_words = sorted(
            self.reduced_word_dic.keys(),
            key=len)  # sort the reduced words by their length
        long_element = self.W.long_element()
        self.dual_words = {
            s: self.reduced_word_dic_reversed[long_element * w]
            for s, w in self.reduced_word_dic.items()
        }  # the dual word is the word times the longest element

        self.column = defaultdict(list)
        max_len = 0
        for red_word in self.reduced_words:
            length = len(red_word)
            max_len = max(max_len, length)
            self.column[length] += [red_word]
        self.max_word_length = max_len

    def _construct_BGG_graph(self):
        """Find all the arrows in the BGG Graph.

        There is an arrow w->w' if len(w')=len(w)+1 and w' = t.w for some t in T.
        """
        self.arrows = []
        for w in self.reduced_words:
            for t in self.T:
                product_word = self.reduced_word_dic_reversed[
                    t * self.reduced_word_dic[w]]
                if len(product_word) == len(w) + 1:
                    self.arrows += [(w, product_word)]
        self.arrows = sorted(
            self.arrows,
            key=lambda t: len(t[0]))  # sort the arrows by the word length
        self.graph = DiGraph(self.arrows)

    def plot_graph(self):
        """Create a pretty plot of the BGG graph, with vertices colored by word lenght."""
        BGGVertices = sorted(self.reduced_words, key=len)
        BGGPartition = [list(v) for k, v in groupby(BGGVertices, len)]

        BGGGraphPlot = self.graph.to_undirected().graphplot(
            partition=BGGPartition, vertex_labels=None, vertex_size=30)
        display(BGGGraphPlot.plot())

    def find_cycles(self):
        """Find all the admitted cycles in the BGG graph.
        
        An admitted cycle consists of two paths a->b->c and a->b'->c, 
        where the word length increases by 1 each step. 
        The cycles are returned as tuples (a,b,c,b',a).
        """
        # only compute cycles if we haven't yet done so already
        if self.cycles is None:
            # for faster searching, make a dictionary of pairs (v,[u_1,...,u_k]) where v is a vertex and u_i
            # are vertices such that there is an arrow v->u_i
            first = lambda x: x[0]
            second = lambda x: x[1]
            outgoing = {
                k: list(map(second, v))
                for k, v in groupby(sorted(self.arrows, key=first), first)
            }
            # outgoing[max(self.reduced_words,key=lambda x: len(x))]=[]
            outgoing[self.reduced_word_dic_reversed[
                self.W.long_element()]] = []

            # make a dictionary of pairs (v,[u_1,...,u_k]) where v is a vertex and u_i are vertices such that
            # there is an arrow u_i->v
            incoming = {
                k: list(map(first, v))
                for k, v in groupby(sorted(self.arrows, key=second), second)
            }
            incoming[""] = []

            # enumerate all paths of length 2, a->b->c, where length goes +1,+1
            self.cycles = chain.from_iterable(
                [[a + (v, ) for v in outgoing[a[-1]]] for a in self.arrows])

            # enumerate all paths of length 3, a->b->c->b' such that b' != b,
            # b<b' in lexicographic order (to avoid duplicates) and length goes +1,+1,-1
            self.cycles = chain.from_iterable(
                [[a + (v, ) for v in incoming[a[-1]] if v > a[1]]
                 for a in self.cycles])

            # enumerate all cycles of length 4, a->b->c->b'->a such that b'!=b and length goes +1,+1,-1,-1
            self.cycles = [
                a + (a[0], ) for a in self.cycles if a[0] in incoming[a[-1]]
            ]

        return self.cycles

    def compute_signs(self, force_recompute=False):
        """Compute signs making making product of signs around all squares equal to -1.

        Returns
        -------
        dict[tuple(str,str), int]
            Dictionary mapping edges in the Bruhat graph to {+1,-1}
        """
        if not force_recompute:
            if self.signs is not None:
                return self.signs

        self.signs = compute_signs(self)
        return self.signs

    def compute_maps(self, root, column=None, check=False, pbar=None):
        """Compute the (unsigned) maps of the BGG complex for a given weight.

        Parameters
        ----------
        root : tuple(int)
            root for which to compute the maps.
        column : int or `None` (default: `None`)
            Try to only compute the maps up to this particular column if not `None`. This
            is faster in particular for small or large values of `column`. 
        check : bool (default: False)
            After computing all the maps, perform a check whether they are correct for 
            debugging purposes.
        pbar : tqdm (default: None)
            tqdm progress bar to give status updates about the progress. If `None` this 
            feature is disabled.

        Returns
        -------
        dict mapping edges (in form ('w1', 'w2')) to elements of `self.PBW`.
        """
        # Convert to tuple to make sure root is hasheable
        root = tuple(root)

        # If the maps are not in the cache, compute them and cache the result
        if root in self._maps:
            cached_result = self._maps[root]
        else:
            cached_result = None
        MapSolver = BGGMapSolver(self,
                                 root,
                                 pbar=pbar,
                                 cached_results=cached_result)
        self._maps[root] = MapSolver.solve(column=column)
        if check:
            maps_OK = MapSolver.check_maps()
            if not maps_OK:
                raise ValueError(
                    "For root %s the map solver produced something wrong" %
                    root)

        if self.pickle_maps:
            self._store_maps()

        return self._maps[root]

    def _read_maps(self):
        target_path = os.path.join(self.pickle_directory,
                                   self.root_system + r"_maps.pkl")
        try:
            with open(target_path, "rb") as file:
                maps = pickle.load(file)
                return maps
        except IOError:
            return dict()

    def _store_maps(self):
        target_path = os.path.join(self.pickle_directory,
                                   self.root_system + r"_maps.pkl")
        try:
            with open(target_path, "wb") as file:
                pickle.dump(self._maps, file, pickle.HIGHEST_PROTOCOL)
        except IOError:
            pass

    def _weight_to_tuple(self, weight):
        """Convert rootspace element to tuple."""
        b = weight.to_vector()
        b = matrix(b).transpose()
        A = [list(a.to_vector()) for a in self.simple_roots]
        A = matrix(A).transpose()

        return tuple(A.solve_right(b).transpose().list())

    def _weight_to_alpha_sum(self, weight):
        """Express a weight in the lattice as a linear combination of alpha[i]'s.
        
        These objects form the keys for elements of the Lie algebra, 
        and for factors in the universal enveloping algebra.
        """
        if type(weight) is not tuple and type(weight) is not list:
            tup = self._weight_to_tuple(weight)
        else:
            tup = tuple(weight)
        alpha = self.lattice.alpha()
        zero = self.lattice.zero()
        return sum((int(c) * alpha[i + 1] for i, c in enumerate(tup)), zero)

    def _alpha_sum_to_array(self, weight):
        output = array(vector(ZZ, self.rank))
        for i, c in weight.monomial_coefficients().items():
            output[i - 1] = c
        return output

    def _tuple_to_weight(self, t):
        """Turn a tuple encoding a linear combination of simple roots back into a weight."""
        return sum(int(a) * b for a, b in zip(t, self.simple_roots))

    def _is_dot_regular(self, mu):
        """Check if a weight is dot-regular by checking that it has trivial stabilizer under dot action.
        
        Parameters
        ----------
        mu : element of `self.lattice`
            Weight encoded as linear combination of `alpha[i]`, use `self._weight_to_alpha_sum()`
            to convert to this format
        """
        for w in self.reduced_words[1:]:
            if (self._dot_action(w, mu) == mu
                ):  # Stabilizer is non-empty, mu is not dot regular
                return False

        # No nontrivial stabilizer found
        return True

    def _make_dominant(self, mu):
        """Given a dot-regular weight, find the associated dominant weight.

        Parameters
        ----------
        mu : element of `self.lattice`
            Weight encoded as linear combination of `alpha[i]`, use `self._weight_to_alpha_sum()`
            to convert to this format

        Returns
        -------
        dominant weight encoded in same format as input
        """
        for w in self.reduced_words:
            new_mu = self._dot_action(w, mu)
            if new_mu.is_dominant():
                return new_mu, w

        # Nothing found
        raise Exception(
            "The weight %s can not be made dominant. Probably it is not dot-regular."
            % mu)

    # def compute_weights(self, weight_module):
    #     all_weights = weight_module.weight_dic.keys()

    #     regular_weights = []
    #     for mu in all_weights:
    #         if self._is_dot_regular(mu):
    #             mu_prime, w = self._make_dominant(mu)
    #             # mu_prime = self._weight_to_alpha_sum(mu_prime)
    #             # w = self.reduced_word_dic_reversed[w]
    #             regular_weights.append((mu, mu_prime, len(w)))
    #     return all_weights, regular_weights

    def _dot_action(self, w, mu):
        """Dot action of Weyl group on weights.

        Parameters
        ----------
        w : element of `self.W`
        mu : RootSpace.element_class
            Weight encoded as linear combination of `alpha[i]`, use `self._weight_to_alpha_sum()`
            to convert to this format

        """
        action = self._action_dic[w]
        mu_action = sum(
            [
                action[i] * int(c)
                for i, c in mu.monomial_coefficients().items()
            ],
            self.lattice.zero(),
        )
        return mu_action + self._rho_action_dic[w]

    def display_pbw(self, f, notebook=True):
        """Typesets an element of PBW of the universal enveloping algebra with LaTeX.

        Parameters
        ----------
        f : PoincareBirkhoffWittBasis.element_class
            The element to display
        notebook : bool (optional, default: `True`)
            Uses IPython display with math if `True`, otherwise just returns the LaTeX code as string.
        """
        map_string = []
        first_term = True
        for monomial, coefficient in f.monomial_coefficients().items():
            alphas = monomial.dict().items()

            if first_term:
                if coefficient < 0:
                    sign_string = "-"
                else:
                    sign_string = ""
                first_term = False
            else:
                if coefficient < 0:
                    sign_string = "-"
                else:
                    sign_string = "+"
            if abs(coefficient) == 1:
                coeff_string = ""
            else:
                coeff_string = str(abs(coefficient))
            term_strings = [sign_string + coeff_string]
            for alpha, power in alphas:
                if power > 1:
                    power_string = r"^{" + str(power) + r"}"
                else:
                    power_string = ""
                alpha_string = "".join(
                    str(k) * int(-v)
                    for k, v in alpha.monomial_coefficients().items())
                term_strings.append(r"f_{" + alpha_string + r"}" +
                                    power_string)

            map_string.append(r"\,".join(term_strings))
        if notebook:
            display(Math(" ".join(map_string)))
        else:
            return " ".join(map_string)

    def _display_map(self, arrow, f):
        """Display a single arrow plus map of the BGG complex."""
        f_string = self.display_pbw(f, notebook=False)
        display(Math(r"\to".join(arrow) + r",\,\," + f_string))

    def display_maps(self, mu):
        """Display all the maps of the BGG complex for a given mu, in appropriate order.
        
        Parameters
        ----------
        mu : tuple
            tuple encoding the weight as linear combination of simple roots
        """
        maps = self.compute_maps(mu)
        maps = sorted(maps.items(), key=lambda s: (len(s[0][0]), s[0]))
        for arrow, f in maps:
            self._display_map(arrow, f)
Пример #22
0
    def __init__(self, root_system, pickle_directory=None):
        self.root_system = root_system
        self.W = WeylGroup(root_system)
        self.domain = self.W.domain()
        self.LA = LieAlgebra(QQ, cartan_type=root_system)
        self.PBW = PoincareBirkhoffWittBasis(self.LA,
                                             None,
                                             "PBW",
                                             cache_degree=5)
        # self.PBW = self.LA.pbw_basis()
        self.PBW_alg_gens = self.PBW.algebra_generators()
        self.lattice = self.domain.root_system.root_lattice()
        self.S = self.W.simple_reflections()
        self.T = self.W.reflections()
        self.signs = None
        self.cycles = None

        self._compute_weyl_dictionary()
        self._construct_BGG_graph()

        self.find_cycles()

        self.simple_roots = self.domain.simple_roots().values()
        self.rank = len(self.simple_roots)

        # for PBW computations we need to put the right order on the negative roots.
        # This order coincides with that of the sagemath source code.
        lie_alg_order = {k: i for i, k in enumerate(self.LA.basis().keys())}
        ordered_roots = sorted(
            [
                self._weight_to_alpha_sum(r)
                for r in self.domain.negative_roots()
            ],
            key=lambda rr: lie_alg_order[rr],
        )
        self.neg_roots = [-self._alpha_sum_to_array(r) for r in ordered_roots]

        self.alpha_to_index = {
            self._weight_to_alpha_sum(-self._tuple_to_weight(r)): i
            for i, r in enumerate(self.neg_roots)
        }
        self.zero_root = self.domain.zero()

        self.pickle_directory = pickle_directory
        if pickle_directory is None:
            self.pickle_maps = False
        else:
            self.pickle_maps = True

        if self.pickle_maps:
            self._maps = self._read_maps()
        else:
            self._maps = dict()

        self.rho = self.domain.rho()

        self._action_dic = dict()
        for s, w in self.reduced_word_dic.items():
            self._action_dic[s] = {
                i: self._weight_to_alpha_sum(w.action(mu))
                for i, mu in dict(self.domain.simple_roots()).items()
            }
        self._rho_action_dic = dict()
        for s, w in self.reduced_word_dic.items():
            self._rho_action_dic[s] = self._weight_to_alpha_sum(
                w.action(self.rho) - self.rho)
Пример #23
0
        def energy_function(self):
            r"""
            Returns the energy function of ``self`` for untwisted types.

            For level zero LS paths `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)`, the energy function is defined as follows:

            .. MATH::

                D(\pi)=-\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda,\mathrm{wt}(b_{u}) \rangle

            To make sense of this equation, we first need some definitions.
            Let us write the LS path (or ``self``) as a piecewise linear map

            .. MATH::

                \pi(t)=\sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1})x_{u'}+(t-\sigma_{u-1})x_{u}

            for `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`.
            Here `b_{u}` is a shortest directed path from `x_{u+1}` to `x_{u}` in the parabolic quantum
            Bruhat graph.

            For any `x,y\in W/W_J`, where `W_J` is a parabolic subgroup of `W` (the stabilizer of the weight `\lambda`), let

            .. MATH::

                d: x= w_{0} \stackrel{\beta_{1}}{\leftarrow}
                w_{1} \stackrel{\beta_{2}}{\leftarrow} \cdots
                \stackrel{\beta_{n}}{\leftarrow} w_{n}=y

            be a shortest path in the parabolic quantum Bruhat graph. The weight is defined as

            .. MATH::

                \mathrm{wt}(d):=\sum_{ \begin{subarray}{c}
                1 \le k \le n \text{ such that } \\
                w_{k-1} \stackrel{\beta_{k}}{\leftarrow} w_{k}
                \text{ is a down arrow}
                \end{subarray}
                }
                \beta_{k}^{\vee}.

            For more information, see [LNSSS2013]_.

            REFERENCES:

            .. [LNSSS2013] C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono,
               A uniform model for Kirillov-Reshetikhin crystals. Extended abstract.
               DMTCS proc, to appear ( {{{:arXiv:`1211.6019`}}} )

            .. WARNING::

                The energy function for LS paths is currently only implemented for
                untwisted types!


            EXAMPLES::

                sage: R = RootSystem(['C',3,1])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])
                sage: b = LS.module_generators[0]
                sage: c = b.f(1).f(3).f(2)
                sage: c.energy_function()
                0
                sage: c=b.e(0)
                sage: c.energy_function()
                1

                sage: R = RootSystem(['A',2,1])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1])
                sage: b = LS.module_generators[0]
                sage: c = b.e(0)
                sage: c.energy_function()
                1
                sage: [c.energy_function() for c in sorted(LS.list())]
                [0, 1, 0, 0, 0, 1, 0, 1, 0]

            The next test checks that the energy function is constant on classically connected components::

                sage: R = RootSystem(['A',2,1])
                sage: La = R.weight_space().basis()
                sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])
                sage: G = LS.digraph(index_set=[1,2])
                sage: C = G.connected_components()
                sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]
                [True, True, True, True]
            """
            weight = self.parent().weight
            c_weight = weight.parent().classical()(weight)
            cartan = weight.parent().cartan_type().classical()
            W = WeylGroup(cartan, prefix="s")
            R = RootSystem(cartan).coroot_lattice()
            G = W.quantum_bruhat_graph(tuple(weight.weyl_stabilizer()))
            L = self.weyl_group_representation()
            paths = [G.shortest_path(L[i + 1], L[i]) for i in range(len(L) - 1)]
            paths_labels = [
                [G.edge_label(p[i], p[i + 1]) for i in range(len(p) - 1) if p[i].length() + 1 != p[i + 1].length()]
                for p in paths
            ]
            scalars = self.scalar_factors()
            return sum(
                (1 - scalars[i]) * c_weight.scalar(R.sum(root.associated_coroot() for root in paths_labels[i]))
                for i in range(len(paths_labels))
            )