Пример #1
0
    def weight_in_root_lattice(self):
        r"""
        Return the weight of ``self`` as an element of the root lattice.

        EXAMPLES::

            sage: M = crystals.infinity.NakajimaMonomials(['F',4])
            sage: m = M.module_generators[0].f_string([3,3,1,2,4])
            sage: m.weight_in_root_lattice()
            -alpha[1] - alpha[2] - 2*alpha[3] - alpha[4]

            sage: M = crystals.infinity.NakajimaMonomials(['B',3,1])
            sage: mg = M.module_generators[0]
            sage: m = mg.f_string([1,3,2,0,1,2,3,0,0,1])
            sage: m.weight_in_root_lattice()
            -3*alpha[0] - 3*alpha[1] - 2*alpha[2] - 2*alpha[3]

            sage: M = crystals.infinity.NakajimaMonomials(['C',3,1])
            sage: m = M.module_generators[0].f_string([3,0,1,2,0])
            sage: m.weight_in_root_lattice()
            -2*alpha[0] - alpha[1] - alpha[2] - alpha[3]
        """
        Q = RootSystem(self.parent().cartan_type()).root_lattice()
        al = Q.simple_roots()
        return Q.sum(e*al[k[0]] for k,e in six.iteritems(self._A))
Пример #2
0
    def _product_coroot_root(self, i, j):
        r"""
        Return the product `\alpha^{\vee}_i \alpha_j`.

        EXAMPLES::

            sage: k = QQ['c,t']
            sage: R = algebras.RationalCherednik(['A',3], k.gen(0), k.gen(1))
            sage: R._product_coroot_root(1, 1)
            ((1, 2*t), (s1*s2*s3*s2*s1, 1/2*c), (s2*s3*s2, 1/2*c),
             (s1*s2*s1, 1/2*c), (s1, 2*c), (s3, 0), (s2, 1/2*c))
            sage: R._product_coroot_root(1, 2)
            ((1, -t), (s1*s2*s3*s2*s1, 0), (s2*s3*s2, -1/2*c),
             (s1*s2*s1, 1/2*c), (s1, -c), (s3, 0), (s2, -c))
            sage: R._product_coroot_root(1, 3)
            ((1, 0), (s1*s2*s3*s2*s1, 1/2*c), (s2*s3*s2, -1/2*c),
             (s1*s2*s1, -1/2*c), (s1, 0), (s3, 0), (s2, 1/2*c))
        """
        Q = RootSystem(self._cartan_type).root_lattice()
        ac = Q.simple_coroot(i)
        al = Q.simple_root(j)

        R = self.base_ring()
        terms = [( self._weyl.one(), self._t * R(ac.scalar(al)) )]
        for s in self._reflections:
            # p[0] is the root, p[1] is the coroot, p[2] the value c_s
            pr, pc, c = self._reflections[s]
            terms.append(( s, c * R(ac.scalar(pr) * pc.scalar(al)
                                    / pc.scalar(pr)) ))
        return tuple(terms)
Пример #3
0
    def __classcall_private__(cls, starting_weight, cartan_type = None, starting_weight_parent = None):
        """
        Classcall to mend the input.

        Internally, the
        :class:`~sage.combinat.crystals.littlemann_path.CrystalOfLSPaths` code
        works with a ``starting_weight`` that is in the weight space associated
        to the crystal. The user can, however, also input a ``cartan_type``
        and the coefficients of the fundamental weights as
        ``starting_weight``. This code transforms the input into the right
        format (also necessary for UniqueRepresentation).

        TESTS::

            sage: crystals.LSPaths(['A',2,1],[-1,0,1])
            The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]

            sage: R = RootSystem(['B',2,1])
            sage: La = R.weight_space(extended=True).basis()
            sage: C = crystals.LSPaths(['B',2,1],[0,0,1])
            sage: B = crystals.LSPaths(La[2])
            sage: B is C
            True
        """
        if cartan_type is not None:
            cartan_type, starting_weight = CartanType(starting_weight), cartan_type
            if cartan_type.is_affine():
                extended = True
            else:
                extended = False

            R = RootSystem(cartan_type)
            P = R.weight_space(extended = extended)
            Lambda = P.basis()
            offset = R.index_set()[Integer(0)]
            starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set())
        if starting_weight_parent is None:
            starting_weight_parent = starting_weight.parent()
        else:
            # Both the weight and the parent of the weight are passed as arguments of init to be able
            # to distinguish between crystals with the extended and non-extended weight lattice!
            if starting_weight.parent() != starting_weight_parent:
                raise ValueError("The passed parent is not equal to parent of the inputted weight!")

        return super(CrystalOfLSPaths, cls).__classcall__(cls, starting_weight, starting_weight_parent = starting_weight_parent)
Пример #4
0
    def maximal_elements(self):
        r"""
        Return the maximal elements of ``self`` with respect to Bruhat order.

        The current implementation is via a conjectural type-free
        formula. Use maximal_elements_combinatorial() for proven
        type-specific implementations. To compare type-free and
        type-specific (combinatorial) implementations, use method
        :meth:`_test_maximal_elements`.

        EXAMPLES::

            sage: W = WeylGroup(['A',4,1])
            sage: PF = W.pieri_factors()
            sage: sorted([w.reduced_word() for w in PF.maximal_elements()], key=str)
            [[0, 4, 3, 2], [1, 0, 4, 3], [2, 1, 0, 4], [3, 2, 1, 0], [4, 3, 2, 1]]

            sage: W = WeylGroup(RootSystem(["C",3,1]).weight_space())
            sage: PF = W.pieri_factors()
            sage: sorted([w.reduced_word() for w in PF.maximal_elements()], key=str)
            [[0, 1, 2, 3, 2, 1], [1, 0, 1, 2, 3, 2], [1, 2, 3, 2, 1, 0],
             [2, 1, 0, 1, 2, 3], [2, 3, 2, 1, 0, 1], [3, 2, 1, 0, 1, 2]]

            sage: W = WeylGroup(RootSystem(["B",3,1]).weight_space())
            sage: PF = W.pieri_factors()
            sage: sorted([w.reduced_word() for w in PF.maximal_elements()], key=str)
            [[0, 2, 3, 2, 0], [1, 0, 2, 3, 2], [1, 2, 3, 2, 1],
             [2, 1, 0, 2, 3], [2, 3, 2, 1, 0], [3, 2, 1, 0, 2]]

            sage: W = WeylGroup(['D',4,1])
            sage: PF = W.pieri_factors()
            sage: sorted([w.reduced_word() for w in PF.maximal_elements()], key=str)
            [[0, 2, 4, 3, 2, 0], [1, 0, 2, 4, 3, 2], [1, 2, 4, 3, 2, 1],
             [2, 1, 0, 2, 4, 3], [2, 4, 3, 2, 1, 0], [3, 2, 1, 0, 2, 3],
             [4, 2, 1, 0, 2, 4], [4, 3, 2, 1, 0, 2]]
        """
        ct = self.W.cartan_type()
        s = ct.translation_factors()[1]
        R = RootSystem(ct).weight_space()
        Lambda = R.fundamental_weights()
        orbit = [R.reduced_word_of_translation(x)
                 for x in (s*(Lambda[1]-Lambda[1].level()*Lambda[0]))._orbit_iter()]
        return [self.W.from_reduced_word(x) for x in orbit]
Пример #5
0
    def weight_in_root_lattice(self):
        r"""
        Return the weight of ``self`` as an element of the root lattice.

        EXAMPLES::

            sage: M = crystals.infinity.NakajimaMonomials(['F',4])
            sage: m = M.module_generators[0].f_string([3,3,1,2,4])
            sage: m.weight_in_root_lattice()
            -alpha[1] - alpha[2] - 2*alpha[3] - alpha[4]

            sage: M = crystals.infinity.NakajimaMonomials(['B',3,1])
            sage: mg = M.module_generators[0]
            sage: m = mg.f_string([1,3,2,0,1,2,3,0,0,1])
            sage: m.weight_in_root_lattice()
            -3*alpha[0] - 3*alpha[1] - 2*alpha[2] - 2*alpha[3]
        """
        Q = RootSystem(self.parent().cartan_type()).root_lattice()
        alpha = Q.simple_roots()
        path = self.to_highest_weight()
        return Q(sum(-alpha[j] for j in path[1]))
Пример #6
0
    def __classcall_private__(cls, crystals, weight):
        """
        Normalize input to ensure a unique representation.

        EXAMPLES::

            sage: B = crystals.KirillovReshetikhin(['A',2,1], 1,1)
            sage: L = RootSystem(['A',2,1]).weight_lattice()
            sage: C = crystals.KyotoPathModel(B, L.fundamental_weight(0))
            sage: C2 = crystals.KyotoPathModel((B,), L.fundamental_weight(0))
            sage: C3 = crystals.KyotoPathModel([B], L.fundamental_weight(0))
            sage: C is C2 and C2 is C3
            True

            sage: L = RootSystem(['A',2,1]).weight_space()
            sage: C = KyotoPathModel(B, L.fundamental_weight(0))
            Traceback (most recent call last):
            ...
            ValueError: Lambda[0] is not in the weight lattice
        """
        if isinstance(crystals, list):
            crystals = tuple(crystals)
        elif not isinstance(crystals, tuple):
            crystals = (crystals,)

        if any(not B.is_perfect() for B in crystals):
            raise ValueError("all crystals must be perfect")
        level = crystals[0].level()
        if any(B.level() != level for B in crystals[1:]):
            raise ValueError("all crystals must have the same level")
        ct = crystals[0].cartan_type()
        P = RootSystem(ct).weight_lattice()
        if weight.parent() is not P:
            raise ValueError("{} is not in the weight lattice".format(weight))
        if sum( ct.dual().c()[i] * weight.scalar(h) for i,h in
                enumerate(P.simple_coroots()) ) != level:
            raise ValueError( "{} is not a level {} weight".format(weight, level) )

        return super(KyotoPathModel, cls).__classcall__(cls, crystals, weight)
Пример #7
0
    def __classcall_private__(cls, starting_weight, cartan_type = None):
        """
        Classcall to mend the input.

        Internally, the CrystalOfLSPaths code works with a ``starting_weight`` that
        is in the ``weight_space`` associated to the crystal. The user can, however,
        also input a ``cartan_type`` and the coefficients of the fundamental weights
        as ``starting_weight``. This code transforms the input into the right
        format (also necessary for UniqueRepresentation).

        TESTS::

            sage: CrystalOfLSPaths(['A',2,1],[-1,0,1])
            The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]

            sage: R = RootSystem(['B',2,1])
            sage: La = R.weight_space().basis()
            sage: C = CrystalOfLSPaths(['B',2,1],[0,0,1])
            sage: B = CrystalOfLSPaths(La[2])
            sage: B is C
            True
        """
        if cartan_type is not None:
            cartan_type, starting_weight = CartanType(starting_weight), cartan_type
            if cartan_type.is_affine():
                extended = True
            else:
                extended = False

            R = RootSystem(cartan_type)
            P = R.weight_space(extended = extended)
            Lambda = P.basis()
            offset = R.index_set()[Integer(0)]
            starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set())

        return super(CrystalOfLSPaths, cls).__classcall__(cls, starting_weight)
Пример #8
0
    def __init__(self, cartan_type, starting_weight):
        """
        EXAMPLES::

            sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1]); C
            The crystal of LS paths of type ['A', 2, 1] and weight (-1, 0, 1)
            sage: C.R
            Root system of type ['A', 2, 1]
            sage: C.weight
            -Lambda[0] + Lambda[2]
            sage: C.weight.parent()
            Extended weight space over the Rational Field of the Root system of type ['A', 2, 1]
            sage: C.module_generators
            [(-Lambda[0] + Lambda[2],)]
        """
        self._cartan_type = CartanType(cartan_type)
        self.R = RootSystem(cartan_type)

        self._name = "The crystal of LS paths of type %s and weight %s"%(cartan_type,starting_weight)

        if self._cartan_type.is_affine():
            self.extended = True
            if all(i>=0 for i in starting_weight):
                Parent.__init__(self, category = HighestWeightCrystals())
            else:
                Parent.__init__(self, category = Crystals())
        else:
            self.extended = False
            Parent.__init__(self, category = FiniteCrystals())

        Lambda = self.R.weight_space(extended = self.extended).basis()
        offset = self.R.index_set()[Integer(0)]

        zero_weight = self.R.weight_space(extended = self.extended).zero()
        self.weight = sum([zero_weight]+[starting_weight[j-offset]*Lambda[j] for j in self.R.index_set()])

        if self.weight == zero_weight:
            initial_element = self(tuple([]))
        else:
            initial_element = self(tuple([self.weight]))
        self.module_generators = [initial_element]
Пример #9
0
    def __init__(self, cartan_type, highest_weight):
        """
        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[1,0,0])
            sage: C.list()
            [[], [0], [0, 1], [0, 1, 2]]
            sage: TestSuite(C).run()
        """
        Parent.__init__(self, category = ClassicalCrystals())
        self._cartan_type = CartanType(cartan_type)
        self._name = "The crystal of alcove paths for type %s"%cartan_type
        self.chain_cache = {}
        self.endweight_cache = {}

        self.R = RootSystem(cartan_type)
        alpha = self.R.root_space().simple_roots()
        Lambda = self.R.weight_space().basis()

        self.positive_roots = sorted(self.R.root_space().positive_roots());

        self.weight = Lambda[Integer(1)] - Lambda[Integer(1)]
        offset = self.R.index_set()[Integer(0)]
        for j in self.R.index_set():
            self.weight = self.weight + highest_weight[j-offset]*Lambda[j]

        self.initial_element = self([])

        self.initial_element.chain = self.get_initial_chain(self.weight)
        rho = (Integer(1)/Integer(2))*sum(self.positive_roots)
        self.initial_element.endweight = rho

        self.chain_cache[ str([]) ] = self.initial_element.chain
        self.endweight_cache[ str([]) ] = self.initial_element.endweight

        self.module_generators = [self.initial_element]

        self._list = super(ClassicalCrystalOfAlcovePaths, self).list()
        self._digraph = super(ClassicalCrystalOfAlcovePaths, self).digraph()
        self._digraph_closure = self.digraph().transitive_closure()
Пример #10
0
    def __init__(self, ct, c, t, base_ring, prefix):
        r"""
        Initialize ``self``.

        EXAMPLES::

            sage: k = QQ['c,t']
            sage: R = algebras.RationalCherednik(['A',2], k.gen(0), k.gen(1))
            sage: TestSuite(R).run()  # long time
        """
        self._c = c
        self._t = t
        self._cartan_type = ct
        self._weyl = RootSystem(ct).root_lattice().weyl_group(prefix=prefix[1])
        self._hd = IndexedFreeAbelianMonoid(ct.index_set(), prefix=prefix[0],
                                            bracket=False)
        self._h = IndexedFreeAbelianMonoid(ct.index_set(), prefix=prefix[2],
                                           bracket=False)
        indices = DisjointUnionEnumeratedSets([self._hd, self._weyl, self._h])
        CombinatorialFreeModule.__init__(self, base_ring, indices,
                                         category=Algebras(base_ring).WithBasis().Graded(),
                                         sorting_key=self._genkey)
Пример #11
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
Пример #12
0
class ClassicalCrystalOfAlcovePaths(UniqueRepresentation, Parent):
    r"""
    Implementation of crystal of alcove paths of the given classical type with
    given highest weight, based on the Lenart--Postnikov model [LP2008].

    These are highest weight crystals for classical types `A_n`, `B_n`, `C_n`,
    `D_n` and the exceptional types `F_4`, `G_2`, `E_6`, `E_7`, `E_8`.

    INPUT:

        - ``cartan_type`` is the Cartan type of a classical Dynkin diagram
        - ``highest_weight`` is a dominant weight as a list of coefficients of
          the fundamental weights `Lambda_i`

    In this model, a chain of roots is associated to the given highest_weight,
    and then the elements of the crystal are indexed by "admissible subsets"
    which indicate "folding positions" along the chain of roots.  See [LP2008]
    for details.

    TODO:

    - Resolve speed issues;  `E_6(\Lambda_1)` takes just under 4 minutes to list().
      To construct the highest-weight node takes 15 sec for `E_6(\Lambda_4)`.
      The initial chain has 42 roots.

    TESTS:

    The following example appears in Figure 2 of [LP2008]::

        sage: C = ClassicalCrystalOfAlcovePaths(['G',2],[0,1]);
        sage: G = C.digraph()
        sage: GG= DiGraph({
        ...       ()        : {(0)         : 2 },
        ...       (0)       : {(0,8)       : 1 },
        ...       (0,1)     : {(0,1,7)     : 2 },
        ...       (0,1,2)   : {(0,1,2,9)   : 1 },
        ...       (0,1,2,3) : {(0,1,2,3,4) : 2 },
        ...       (0,1,2,6) : {(0,1,2,3)   : 1 },
        ...       (0,1,2,9) : {(0,1,2,6)   : 1 },
        ...       (0,1,7)   : {(0,1,2)     : 2 },
        ...       (0,1,7,9) : {(0,1,2,9)   : 2 },
        ...       (0,5)     : {(0,1)       : 1, (0,5,7) : 2 },
        ...       (0,5,7)   : {(0,5,7,9)   : 1 },
        ...       (0,5,7,9) : {(0,1,7,9)   : 1 },
        ...       (0,8)     : {(0,5)       : 1 },
        ...       })
        sage: G.is_isomorphic(GG)
        True

        sage: for edge in sorted([(u.value, v.value, i) for (u,v,i) in G.edges()]):
        ...       print edge
        ([], [0], 2)
        ([0], [0, 8], 1)
        ([0, 1], [0, 1, 7], 2)
        ([0, 1, 2], [0, 1, 2, 9], 1)
        ([0, 1, 2, 3], [0, 1, 2, 3, 4], 2)
        ([0, 1, 2, 6], [0, 1, 2, 3], 1)
        ([0, 1, 2, 9], [0, 1, 2, 6], 1)
        ([0, 1, 7], [0, 1, 2], 2)
        ([0, 1, 7, 9], [0, 1, 2, 9], 2)
        ([0, 5], [0, 1], 1)
        ([0, 5], [0, 5, 7], 2)
        ([0, 5, 7], [0, 5, 7, 9], 1)
        ([0, 5, 7, 9], [0, 1, 7, 9], 1)
        ([0, 8], [0, 5], 1)

    REFERENCES:

        .. [LP2008]  C. Lenart and A. Postnikov. A combinatorial model for crystals of Kac-Moody algebras. Trans. Amer. Math. Soc.  360  (2008), 4349-4381. 
    """

    @staticmethod
    def __classcall__(cls, cartan_type, highest_weight):
        """
        cartan_type and heighest_weight are lists, which are mutable, this
        causes a problem for class UniqueRepresentation, the following code
        fixes this problem.

        EXAMPLES::
            sage: ClassicalCrystalOfAlcovePaths.__classcall__(ClassicalCrystalOfAlcovePaths,['A',3],[0,1,0])
            <class 'sage.combinat.crystals.alcove_path.ClassicalCrystalOfAlcovePaths_with_category'>
        """
        cartan_type = CartanType(cartan_type)
        highest_weight = tuple(highest_weight)
        return super(ClassicalCrystalOfAlcovePaths, cls).__classcall__(cls, cartan_type, highest_weight)

    def __init__(self, cartan_type, highest_weight):
        """
        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[1,0,0])
            sage: C.list()
            [[], [0], [0, 1], [0, 1, 2]]
            sage: TestSuite(C).run()
        """
        Parent.__init__(self, category = ClassicalCrystals())
        self._cartan_type = CartanType(cartan_type)
        self._name = "The crystal of alcove paths for type %s"%cartan_type
        self.chain_cache = {}
        self.endweight_cache = {}

        self.R = RootSystem(cartan_type)
        alpha = self.R.root_space().simple_roots()
        Lambda = self.R.weight_space().basis()

        self.positive_roots = sorted(self.R.root_space().positive_roots());

        self.weight = Lambda[Integer(1)] - Lambda[Integer(1)]
        offset = self.R.index_set()[Integer(0)]
        for j in self.R.index_set():
            self.weight = self.weight + highest_weight[j-offset]*Lambda[j]

        self.initial_element = self([])

        self.initial_element.chain = self.get_initial_chain(self.weight)
        rho = (Integer(1)/Integer(2))*sum(self.positive_roots)
        self.initial_element.endweight = rho

        self.chain_cache[ str([]) ] = self.initial_element.chain
        self.endweight_cache[ str([]) ] = self.initial_element.endweight

        self.module_generators = [self.initial_element]

        self._list = super(ClassicalCrystalOfAlcovePaths, self).list()
        self._digraph = super(ClassicalCrystalOfAlcovePaths, self).digraph()
        self._digraph_closure = self.digraph().transitive_closure()

    def get_initial_chain(self, highest_weight):
        """
        Called internally by __init__() to construct the chain of roots
        associated to the highest weight element.

        EXAMPLES::
            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[0,1,0])
            sage: C.get_initial_chain(RootSystem(['A',3]).weight_space().basis()[1])
            [[alpha[1], alpha[1]], [alpha[1] + alpha[2], alpha[1] + alpha[2]], [alpha[1] + alpha[2] + alpha[3], alpha[1] + alpha[2] + alpha[3]]]
        """
        pos_roots = self.positive_roots
        tis = self.R.index_set()
        tis.reverse()
        cv_to_pos_root = {}
        to_sort = []
        for r in pos_roots:
            j = highest_weight.scalar( r.associated_coroot() )
            if (int(math.floor(j)) - j == Integer(0)):
                j = j - Integer(1)
            j = int(math.floor(j))
            for k in (ellipsis_range(Integer(0),Ellipsis,j)):
                cv = []
                cv.append((Integer(1)/highest_weight.scalar(r.associated_coroot()))*k)
                for i in tis:
                    cv.append((Integer(1)/highest_weight.scalar(r.associated_coroot()))*r.associated_coroot().coefficient(i))
                cv_to_pos_root[ str(cv) ] = r
                to_sort.append( cv )

        to_sort.sort() # Note:  Python sorts nested lists lexicographically by default.
        lambda_chain = []
        for t in to_sort:
            lambda_chain.append( [ cv_to_pos_root[str(t)], cv_to_pos_root[str(t)] ] )
        return lambda_chain

    def _element_constructor_(self, value):
        """
        Coerces value into self.

        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[1,0,0])
            sage: C.module_generators
            [[]]
            sage: C([]).e(1)
            sage: C([]).f(1)
            [0]
        """
        return self.element_class(self, value)

    def list(self):
        """
        Returns a list of the elements of self.

        .. warning::

           This can be slow!

        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[0,1,0])
            sage: C.list()
            [[], [0], [0, 1], [0, 2], [0, 1, 2], [0, 1, 2, 3]]
        """
        return self._list

    def digraph(self):
        """
        Returns the directed graph associated to self.

        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[0,1,0])
            sage: C.digraph().vertices()
            [[], [0], [0, 1], [0, 2], [0, 1, 2], [0, 1, 2, 3]]
            sage: C.digraph().edges()
            [([], [0], 2), ([0], [0, 1], 1), ([0], [0, 2], 3), ([0, 1], [0, 1, 2], 3), ([0, 2], [0, 1, 2], 1), ([0, 1, 2], [0, 1, 2, 3], 2)]
        """
        return self._digraph

    def lt_elements(self, x, y):
        r"""
        Returns True if and only if there is a path
        from x to y in the crystal graph.

        Because the crystal graph is classical, it is a directed
        acyclic graph which can be interpreted as a poset. This
        function implements the comparison function of this poset.

        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[0,1,0])
            sage: x = C([])
            sage: y = C([0,2])
            sage: C.lt_elements(x,y)
            True
            sage: C.lt_elements(y,x)
            False
            sage: C.lt_elements(x,x)
            False
        """
        assert x.parent() == self and y.parent() == self
        if self._digraph_closure.has_edge(x,y):
            return True
        return False
Пример #13
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))
            )
Пример #14
0
    def __init__(self, cartan_type, prefix, finite=True):
        r"""

        EXAMPLES::

            sage: from sage.combinat.root_system.fundamental_group import FundamentalGroupOfExtendedAffineWeylGroup
            sage: F = FundamentalGroupOfExtendedAffineWeylGroup(['A',3,1])
            sage: F in Groups().Commutative().Finite()
            True
            sage: TestSuite(F).run()
        """
        def leading_support(beta):
            r"""
            Given a dictionary with one key, return this key
            """
            supp = beta.support()
            assert len(supp) == 1
            return supp[0]

        self._cartan_type = cartan_type
        self._prefix = prefix
        special_node = cartan_type.special_node()
        self._special_nodes = cartan_type.special_nodes()

        # initialize dictionaries with the entries for the distinguished special node
        # dictionary of inverse elements
        inverse_dict = {}
        inverse_dict[special_node] = special_node
        # dictionary for the action of special automorphisms by permutations of the affine Dynkin nodes
        auto_dict = {}
        for i in cartan_type.index_set():
            auto_dict[special_node,i] = i
        # dictionary for the finite Weyl component of the special automorphisms
        reduced_words_dict = {}
        reduced_words_dict[0] = tuple([])

        if cartan_type.dual().is_untwisted_affine():
            # this combines the computations for an untwisted affine type and its affine dual
            cartan_type = cartan_type.dual()
        if cartan_type.is_untwisted_affine():
            cartan_type_classical = cartan_type.classical()
            I = [i for i in cartan_type_classical.index_set()]
            Q = RootSystem(cartan_type_classical).root_lattice()
            alpha = Q.simple_roots()
            omega = RootSystem(cartan_type_classical).weight_lattice().fundamental_weights()
            W = Q.weyl_group(prefix="s")
            for i in self._special_nodes:
                if i == special_node:
                    continue
                antidominant_weight, reduced_word = omega[i].to_dominant_chamber(reduced_word=True, positive=False)
                reduced_words_dict[i] = tuple(reduced_word)
                w0i = W.from_reduced_word(reduced_word)
                idual = leading_support(-antidominant_weight)
                inverse_dict[i] = idual
                auto_dict[i,special_node] = i
                for j in I:
                    if j == idual:
                        auto_dict[i,j] = special_node
                    else:
                        auto_dict[i,j] = leading_support(w0i.action(alpha[j]))

        self._action = Family(self._special_nodes, lambda i: Family(cartan_type.index_set(), lambda j: auto_dict[i,j]))
        self._dual_node = Family(self._special_nodes, inverse_dict.__getitem__)
        self._reduced_words = Family(self._special_nodes, reduced_words_dict.__getitem__)

        if finite:
            cat = Category.join((Groups().Commutative().Finite(),EnumeratedSets()))
        else:
            cat = Groups().Commutative().Infinite()
        Parent.__init__(self, category = cat)
Пример #15
0
    def one_dimensional_configuration_sum(self, q = None, group_components = True):
        r"""
        Compute the one-dimensional configuration sum.

        INPUT:

        - ``q`` -- (default: ``None``) a variable or ``None``; if ``None``,
          a variable ``q`` is set in the code
        - ``group_components`` -- (default: ``True``) boolean; if ``True``,
          then the terms are grouped by classical component

        The one-dimensional configuration sum is the sum of the weights of all elements in the crystal
        weighted by the energy function. For untwisted types it uses the parabolic quantum Bruhat graph, see [LNSSS2013]_.
        In the dual-of-untwisted case, the parabolic quantum Bruhat graph is defined by
        exchanging the roles of roots and coroots (which is still conjectural at this point).

        EXAMPLES::

            sage: R = RootSystem(['A',2,1])
            sage: La = R.weight_space().basis()
            sage: LS = crystals.ProjectedLevelZeroLSPaths(2*La[1])
            sage: LS.one_dimensional_configuration_sum() # long time
            B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]]
            + (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]]
            sage: R.<t> = ZZ[]
            sage: LS.one_dimensional_configuration_sum(t, False) # long time
            B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + (t+1)*B[Lambda[1] - Lambda[2]]
            + B[2*Lambda[1]] + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]]

        TESTS::

            sage: R = RootSystem(['B',3,1])
            sage: La = R.weight_space().basis()
            sage: LS = crystals.ProjectedLevelZeroLSPaths(La[1]+La[2])
            sage: LS.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum(group_components=False) # long time
            True
            sage: K1 = crystals.KirillovReshetikhin(['B',3,1],1,1)
            sage: K2 = crystals.KirillovReshetikhin(['B',3,1],2,1)
            sage: T = crystals.TensorProduct(K2,K1)
            sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum() # long time
            True

            sage: R = RootSystem(['D',4,2])
            sage: La = R.weight_space().basis()
            sage: LS = crystals.ProjectedLevelZeroLSPaths(La[1]+La[2])
            sage: K1 = crystals.KirillovReshetikhin(['D',4,2],1,1)
            sage: K2 = crystals.KirillovReshetikhin(['D',4,2],2,1)
            sage: T = crystals.TensorProduct(K2,K1)
            sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum() # long time
            True

            sage: R = RootSystem(['A',5,2])
            sage: La = R.weight_space().basis()
            sage: LS = crystals.ProjectedLevelZeroLSPaths(3*La[1])
            sage: K1 = crystals.KirillovReshetikhin(['A',5,2],1,1)
            sage: T = crystals.TensorProduct(K1,K1,K1)
            sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum() # long time
            True
        """
        if q is None:
            from sage.rings.all import QQ
            q = QQ['q'].gens()[0]
        #P0 = self.weight_lattice_realization().classical()
        P0 = RootSystem(self.cartan_type().classical()).weight_lattice()
        B = P0.algebra(q.parent())
        def weight(x):
            w = x.weight()
            return P0.sum(int(c)*P0.basis()[i] for i,c in w if i in P0.index_set())
        if group_components:
            G = self.digraph(index_set = self.cartan_type().classical().index_set())
            C = G.connected_components()
            return sum(q**(c[0].energy_function())*B.sum(B(weight(b)) for b in c) for c in C)
        return B.sum(q**(b.energy_function())*B(weight(b)) for b in self)
Пример #16
0
    def product_on_basis(self, left, right):
        r"""
        Return ``left`` multiplied by ``right`` in ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: a2 = R.algebra_generators()['a2']
            sage: ac1 = R.algebra_generators()['ac1']
            sage: a2 * ac1  # indirect doctest
            a2*ac1
            sage: ac1 * a2
            -I + a2*ac1 - s1 - s2 + 1/2*s1*s2*s1
            sage: x = R.an_element()
            sage: [y * x for y in R.some_elements()]
            [0,
             3*ac1 + 2*s1 + a1,
             9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2,
             3*a1*ac1 + 2*a1*s1 + a1^2,
             3*a2*ac1 + 2*a2*s1 + a1*a2,
             3*s1*ac1 + 2*I - a1*s1,
             3*s2*ac1 + 2*s2*s1 + a1*s2 + a2*s2,
             3*ac1^2 - 2*s1*ac1 + 2*I + a1*ac1 + 2*s1 + 1/2*s2 + 1/2*s1*s2*s1,
             3*ac1*ac2 + 2*s1*ac1 + 2*s1*ac2 - I + a1*ac2 - s1 - s2 + 1/2*s1*s2*s1]
            sage: [x * y for y in R.some_elements()]
            [0,
             3*ac1 + 2*s1 + a1,
             9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2,
             6*I + 3*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 - 2*a1*s1 + a1^2,
             -3*I + 3*a2*ac1 - 3*s1 - 3*s2 + 3/2*s1*s2*s1 + 2*a1*s1 + 2*a2*s1 + a1*a2,
             -3*s1*ac1 + 2*I + a1*s1,
             3*s2*ac1 + 3*s2*ac2 + 2*s1*s2 + a1*s2,
             3*ac1^2 + 2*s1*ac1 + a1*ac1,
             3*ac1*ac2 + 2*s1*ac2 + a1*ac2]
        """
        # Make copies of the internal dictionaries
        dl = dict(left[2]._monomial)
        dr = dict(right[0]._monomial)

        # If there is nothing to commute
        if not dl and not dr:
            return self.monomial((left[0], left[1] * right[1], right[2]))

        R = self.base_ring()
        I = self._cartan_type.index_set()
        P = PolynomialRing(R, 'x', len(I))
        G = P.gens()
        gens_dict = {a:G[i] for i,a in enumerate(I)}
        Q = RootSystem(self._cartan_type).root_lattice()
        alpha = Q.simple_roots()
        alphacheck = Q.simple_coroots()

        def commute_w_hd(w, al): # al is given as a dictionary
            ret = P.one()
            for k in al:
                x = sum(c * gens_dict[i] for i,c in alpha[k].weyl_action(w))
                ret *= x**al[k]
            ret = ret.dict()
            for k in ret:
                yield (self._hd({I[i]: e for i,e in enumerate(k) if e != 0}), ret[k])

        # Do Lac Ra if they are both non-trivial
        if dl and dr:
            il = dl.keys()[0]
            ir = dr.keys()[0]

            # Compute the commutator
            terms = self._product_coroot_root(il, ir)

            # remove the generator from the elements
            dl[il] -= 1
            if dl[il] == 0:
                del dl[il]
            dr[ir] -= 1
            if dr[ir] == 0:
                del dr[ir]

            # We now commute right roots past the left reflections: s Ra = Ra' s
            cur = self._from_dict({ (hd, s*right[1], right[2]): c * cc
                                    for s,c in terms
                                    for hd, cc in commute_w_hd(s, dr) })
            cur = self.monomial( (left[0], left[1], self._h(dl)) ) * cur

            # Add back in the commuted h and hd elements
            rem = self.monomial( (left[0], left[1], self._h(dl)) )
            rem = rem * self.monomial( (self._hd({ir:1}), self._weyl.one(),
                                        self._h({il:1})) )
            rem = rem * self.monomial( (self._hd(dr), right[1], right[2]) )

            return cur + rem

        if dl:
            # We have La Ls Lac Rs Rac,
            #   so we must commute Lac Rs = Rs Lac'
            #   and obtain La (Ls Rs) (Lac' Rac)
            ret = P.one()
            for k in dl:
                x = sum(c * gens_dict[i]
                        for i,c in alphacheck[k].weyl_action(right[1].reduced_word(),
                                                             inverse=True))
                ret *= x**dl[k]
            ret = ret.dict()
            w = left[1]*right[1]
            return self._from_dict({ (left[0], w,
                                      self._h({I[i]: e for i,e in enumerate(k)
                                               if e != 0}) * right[2]
                                     ): ret[k]
                                     for k in ret })

        # Otherwise dr is non-trivial and we have La Ls Ra Rs Rac,
        #   so we must commute Ls Ra = Ra' Ls
        w = left[1]*right[1]
        return self._from_dict({ (left[0] * hd, w, right[2]): c
                                 for hd, c in commute_w_hd(left[1], dr) })
Пример #17
0
class RationalCherednikAlgebra(CombinatorialFreeModule):
    r"""
    A rational Cherednik algebra.

    Let `k` be a field. Let `W` be a complex reflection group acting on
    a vector space `\mathfrak{h}` (over `k`). Let `\mathfrak{h}^*` denote
    the corresponding dual vector space. Let `\cdot` denote the
    natural action of `w` on `\mathfrak{h}` and `\mathfrak{h}^*`. Let
    `\mathcal{S}` denote the set of reflections of `W` and  `\alpha_s`
    and `\alpha_s^{\vee}` are the associated root and coroot of `s`. Let
    `c = (c_s)_{s \in W}` such that `c_s = c_{tst^{-1}}` for all `t \in W`.

    The *rational Cherednik algebra* is the `k`-algebra
    `H_{c,t}(W) = T(\mathfrak{h} \oplus \mathfrak{h}^*) \otimes kW` with
    parameters `c, t \in k` that is subject to the relations:

    .. MATH::

        \begin{aligned}
        w \alpha & = (w \cdot \alpha) w,
        \\ \alpha^{\vee} w & = w (w^{-1} \cdot \alpha^{\vee}),
        \\ \alpha \alpha^{\vee} & = \alpha^{\vee} \alpha
        + t \langle \alpha^{\vee}, \alpha \rangle
        + \sum_{s \in \mathcal{S}} c_s \frac{\langle \alpha^{\vee},
        \alpha_s \rangle \langle \alpha^{\vee}_s, \alpha \rangle}{
        \langle \alpha^{\vee}, \alpha \rangle} s,
        \end{aligned}

    where `w \in W` and `\alpha \in \mathfrak{h}` and
    `\alpha^{\vee} \in \mathfrak{h}^*`.

    INPUT:

    - ``ct`` -- a finite Cartan type
    - ``c`` -- the parameters `c_s` given as an element or a tuple, where
      the first entry is the one for the long roots and (for
      non-simply-laced types) the second is for the short roots
    - ``t`` -- the parameter `t`
    - ``base_ring`` -- (optional) the base ring
    - ``prefix`` -- (default: ``('a', 's', 'ac')``) the prefixes

    .. TODO::

        Implement a version for complex reflection groups.

    REFERENCES:

    - [GGOR2003]_
    - [EM2001]_
    """
    @staticmethod
    def __classcall_private__(cls, ct, c=1, t=None, base_ring=None, prefix=('a', 's', 'ac')):
        """
        Normalize input to ensure a unique representation.

        EXAMPLES::

            sage: R1 = algebras.RationalCherednik(['B',2], 1, 1, QQ)
            sage: R2 = algebras.RationalCherednik(CartanType(['B',2]), [1,1], 1, QQ, ('a', 's', 'ac'))
            sage: R1 is R2
            True
        """
        ct = CartanType(ct)
        if not ct.is_finite():
            raise ValueError("the Cartan type must be finite")
        if base_ring is None:
            if t is None:
                base_ring = QQ
            else:
                base_ring = t.parent()
        if t is None:
            t = base_ring.one()
        else:
            t = base_ring(t)

        # Normalize the parameter c
        if isinstance(c, (tuple, list)):
            if ct.is_simply_laced():
                if len(c) != 1:
                    raise ValueError("1 parameter c_s must be given for simply-laced types")
                c = (base_ring(c[0]),)
            else:
                if len(c) != 2:
                    raise ValueError("2 parameters c_s must be given for non-simply-laced types")
                c = (base_ring(c[0]), base_ring(c[1]))
        else:
            c = base_ring(c)
            if ct.is_simply_laced():
                c = (c,)
            else:
                c = (c, c)

        return super(RationalCherednikAlgebra, cls).__classcall__(cls, ct, c, t, base_ring, tuple(prefix))

    def __init__(self, ct, c, t, base_ring, prefix):
        r"""
        Initialize ``self``.

        EXAMPLES::

            sage: k = QQ['c,t']
            sage: R = algebras.RationalCherednik(['A',2], k.gen(0), k.gen(1))
            sage: TestSuite(R).run()  # long time
        """
        self._c = c
        self._t = t
        self._cartan_type = ct
        self._weyl = RootSystem(ct).root_lattice().weyl_group(prefix=prefix[1])
        self._hd = IndexedFreeAbelianMonoid(ct.index_set(), prefix=prefix[0],
                                            bracket=False)
        self._h = IndexedFreeAbelianMonoid(ct.index_set(), prefix=prefix[2],
                                           bracket=False)
        indices = DisjointUnionEnumeratedSets([self._hd, self._weyl, self._h])
        CombinatorialFreeModule.__init__(self, base_ring, indices,
                                         category=Algebras(base_ring).WithBasis().Graded(),
                                         sorting_key=self._genkey)

    def _genkey(self, t):
        """
        Construct a key for comparison for a term indexed by ``t``.

        The key we create is the tuple in the following order:

        - overall degree
        - length of the Weyl group element
        - the Weyl group element
        - the element of `\mathfrak{h}`
        - the element of `\mathfrak{h}^*`

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.an_element()**2 # indirect doctest
            9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2
        """
        return (self.degree_on_basis(t), t[1].length(), t[1], str(t[0]), str(t[2]))

    @lazy_attribute
    def _reflections(self):
        """
        A dictionary of reflections to a pair of the associated root
        and coroot.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['B',2], [1,2], 1, QQ)
            sage: [R._reflections[k] for k in sorted(R._reflections, key=str)]
            [(alpha[1], alphacheck[1], 1),
             (alpha[1] + alpha[2], 2*alphacheck[1] + alphacheck[2], 2),
             (alpha[2], alphacheck[2], 2),
             (alpha[1] + 2*alpha[2], alphacheck[1] + alphacheck[2], 1)]
        """
        d = {}
        for r in RootSystem(self._cartan_type).root_lattice().positive_roots():
            s = self._weyl.from_reduced_word(r.associated_reflection())
            if r.is_short_root():
                c = self._c[1]
            else:
                c = self._c[0]
            d[s] = (r, r.associated_coroot(), c)
        return d

    def _repr_(self):
        r"""
        Return a string representation of ``self``.

        EXAMPLES ::

            sage: RationalCherednikAlgebra(['A',4], 2, 1, QQ)
            Rational Cherednik Algebra of type ['A', 4] with c=2 and t=1
             over Rational Field
            sage: algebras.RationalCherednik(['B',2], [1,2], 1, QQ)
            Rational Cherednik Algebra of type ['B', 2] with c_L=1 and c_S=2
             and t=1 over Rational Field
        """
        ret = "Rational Cherednik Algebra of type {} with ".format(self._cartan_type)
        if self._cartan_type.is_simply_laced():
            ret += "c={}".format(self._c[0])
        else:
            ret += "c_L={} and c_S={}".format(*self._c)
        return ret + " and t={} over {}".format(self._t, self.base_ring())

    def _repr_term(self, t):
        """
        Return a string representation of the term indexed by ``t``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.an_element() # indirect doctest
            3*ac1 + 2*s1 + a1
            sage: R.one() # indirect doctest
            I
        """
        r = []
        if t[0] != self._hd.one():
            r.append(t[0])
        if t[1] != self._weyl.one():
            r.append(t[1])
        if t[2] != self._h.one():
            r.append(t[2])
        if not r:
            return 'I'
        return '*'.join(repr(x) for x in r)

    def algebra_generators(self):
        """
        Return the algebra generators of ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: list(R.algebra_generators())
            [a1, a2, s1, s2, ac1, ac2]
        """
        keys  = ['a'+str(i) for i in self._cartan_type.index_set()]
        keys += ['s'+str(i) for i in self._cartan_type.index_set()]
        keys += ['ac'+str(i) for i in self._cartan_type.index_set()]
        def gen_map(k):
            if k[0] == 's':
                i = int(k[1:])
                return self.monomial( (self._hd.one(),
                                       self._weyl.group_generators()[i],
                                       self._h.one()) )
            if k[1] == 'c':
                i = int(k[2:])
                return self.monomial( (self._hd.one(),
                                       self._weyl.one(),
                                       self._h.monoid_generators()[i]) )

            i = int(k[1:])
            return self.monomial( (self._hd.monoid_generators()[i],
                                   self._weyl.one(),
                                   self._h.one()) )
        return Family(keys, gen_map)

    @cached_method
    def one_basis(self):
        """
        Return the index of the element `1`.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.one_basis()
            (1, 1, 1)
        """
        return (self._hd.one(), self._weyl.one(), self._h.one())

    def product_on_basis(self, left, right):
        r"""
        Return ``left`` multiplied by ``right`` in ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: a2 = R.algebra_generators()['a2']
            sage: ac1 = R.algebra_generators()['ac1']
            sage: a2 * ac1  # indirect doctest
            a2*ac1
            sage: ac1 * a2
            -I + a2*ac1 - s1 - s2 + 1/2*s1*s2*s1
            sage: x = R.an_element()
            sage: [y * x for y in R.some_elements()]
            [0,
             3*ac1 + 2*s1 + a1,
             9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2,
             3*a1*ac1 + 2*a1*s1 + a1^2,
             3*a2*ac1 + 2*a2*s1 + a1*a2,
             3*s1*ac1 + 2*I - a1*s1,
             3*s2*ac1 + 2*s2*s1 + a1*s2 + a2*s2,
             3*ac1^2 - 2*s1*ac1 + 2*I + a1*ac1 + 2*s1 + 1/2*s2 + 1/2*s1*s2*s1,
             3*ac1*ac2 + 2*s1*ac1 + 2*s1*ac2 - I + a1*ac2 - s1 - s2 + 1/2*s1*s2*s1]
            sage: [x * y for y in R.some_elements()]
            [0,
             3*ac1 + 2*s1 + a1,
             9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2,
             6*I + 3*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 - 2*a1*s1 + a1^2,
             -3*I + 3*a2*ac1 - 3*s1 - 3*s2 + 3/2*s1*s2*s1 + 2*a1*s1 + 2*a2*s1 + a1*a2,
             -3*s1*ac1 + 2*I + a1*s1,
             3*s2*ac1 + 3*s2*ac2 + 2*s1*s2 + a1*s2,
             3*ac1^2 + 2*s1*ac1 + a1*ac1,
             3*ac1*ac2 + 2*s1*ac2 + a1*ac2]
        """
        # Make copies of the internal dictionaries
        dl = dict(left[2]._monomial)
        dr = dict(right[0]._monomial)

        # If there is nothing to commute
        if not dl and not dr:
            return self.monomial((left[0], left[1] * right[1], right[2]))

        R = self.base_ring()
        I = self._cartan_type.index_set()
        P = PolynomialRing(R, 'x', len(I))
        G = P.gens()
        gens_dict = {a:G[i] for i,a in enumerate(I)}
        Q = RootSystem(self._cartan_type).root_lattice()
        alpha = Q.simple_roots()
        alphacheck = Q.simple_coroots()

        def commute_w_hd(w, al): # al is given as a dictionary
            ret = P.one()
            for k in al:
                x = sum(c * gens_dict[i] for i,c in alpha[k].weyl_action(w))
                ret *= x**al[k]
            ret = ret.dict()
            for k in ret:
                yield (self._hd({I[i]: e for i,e in enumerate(k) if e != 0}), ret[k])

        # Do Lac Ra if they are both non-trivial
        if dl and dr:
            il = dl.keys()[0]
            ir = dr.keys()[0]

            # Compute the commutator
            terms = self._product_coroot_root(il, ir)

            # remove the generator from the elements
            dl[il] -= 1
            if dl[il] == 0:
                del dl[il]
            dr[ir] -= 1
            if dr[ir] == 0:
                del dr[ir]

            # We now commute right roots past the left reflections: s Ra = Ra' s
            cur = self._from_dict({ (hd, s*right[1], right[2]): c * cc
                                    for s,c in terms
                                    for hd, cc in commute_w_hd(s, dr) })
            cur = self.monomial( (left[0], left[1], self._h(dl)) ) * cur

            # Add back in the commuted h and hd elements
            rem = self.monomial( (left[0], left[1], self._h(dl)) )
            rem = rem * self.monomial( (self._hd({ir:1}), self._weyl.one(),
                                        self._h({il:1})) )
            rem = rem * self.monomial( (self._hd(dr), right[1], right[2]) )

            return cur + rem

        if dl:
            # We have La Ls Lac Rs Rac,
            #   so we must commute Lac Rs = Rs Lac'
            #   and obtain La (Ls Rs) (Lac' Rac)
            ret = P.one()
            for k in dl:
                x = sum(c * gens_dict[i]
                        for i,c in alphacheck[k].weyl_action(right[1].reduced_word(),
                                                             inverse=True))
                ret *= x**dl[k]
            ret = ret.dict()
            w = left[1]*right[1]
            return self._from_dict({ (left[0], w,
                                      self._h({I[i]: e for i,e in enumerate(k)
                                               if e != 0}) * right[2]
                                     ): ret[k]
                                     for k in ret })

        # Otherwise dr is non-trivial and we have La Ls Ra Rs Rac,
        #   so we must commute Ls Ra = Ra' Ls
        w = left[1]*right[1]
        return self._from_dict({ (left[0] * hd, w, right[2]): c
                                 for hd, c in commute_w_hd(left[1], dr) })

    @cached_method
    def _product_coroot_root(self, i, j):
        r"""
        Return the product `\alpha^{\vee}_i \alpha_j`.

        EXAMPLES::

            sage: k = QQ['c,t']
            sage: R = algebras.RationalCherednik(['A',3], k.gen(0), k.gen(1))
            sage: R._product_coroot_root(1, 1)
            ((1, 2*t), (s1*s2*s3*s2*s1, 1/2*c), (s2*s3*s2, 1/2*c),
             (s1*s2*s1, 1/2*c), (s1, 2*c), (s3, 0), (s2, 1/2*c))
            sage: R._product_coroot_root(1, 2)
            ((1, -t), (s1*s2*s3*s2*s1, 0), (s2*s3*s2, -1/2*c),
             (s1*s2*s1, 1/2*c), (s1, -c), (s3, 0), (s2, -c))
            sage: R._product_coroot_root(1, 3)
            ((1, 0), (s1*s2*s3*s2*s1, 1/2*c), (s2*s3*s2, -1/2*c),
             (s1*s2*s1, -1/2*c), (s1, 0), (s3, 0), (s2, 1/2*c))
        """
        Q = RootSystem(self._cartan_type).root_lattice()
        ac = Q.simple_coroot(i)
        al = Q.simple_root(j)

        R = self.base_ring()
        terms = [( self._weyl.one(), self._t * R(ac.scalar(al)) )]
        for s in self._reflections:
            # p[0] is the root, p[1] is the coroot, p[2] the value c_s
            pr, pc, c = self._reflections[s]
            terms.append(( s, c * R(ac.scalar(pr) * pc.scalar(al)
                                    / pc.scalar(pr)) ))
        return tuple(terms)

    def degree_on_basis(self, m):
        """
        Return the degree on the monomial indexed by ``m``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: [R.degree_on_basis(g.leading_support())
            ....:  for g in R.algebra_generators()]
            [1, 1, 0, 0, -1, -1]
        """
        return m[0].length() - m[2].length()

    @cached_method
    def trivial_idempotent(self):
        """
        Return the trivial idempotent of ``self``.

        Let `e = |W|^{-1} \sum_{w \in W} w` is the trivial idempotent.
        Thus `e^2 = e` and `eW = We`. The trivial idempotent is used
        in the construction of the spherical Cherednik algebra from
        the rational Cherednik algebra by `U_{c,t}(W) = e H_{c,t}(W) e`.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.trivial_idempotent()
            1/6*I + 1/6*s1 + 1/6*s2 + 1/6*s2*s1 + 1/6*s1*s2 + 1/6*s1*s2*s1
        """
        coeff = self.base_ring()(~self._weyl.cardinality())
        hd_one = self._hd.one() # root - a
        h_one = self._h.one() # coroot - ac
        return self._from_dict({(hd_one, w, h_one): coeff for w in self._weyl},
                               remove_zeros=False)

    @cached_method
    def deformed_euler(self):
        """
        Return the element `eu_k`.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.deformed_euler()
            2*I + 2/3*a1*ac1 + 1/3*a1*ac2 + 1/3*a2*ac1 + 2/3*a2*ac2
             + s1 + s2 + s1*s2*s1
        """
        I = self._cartan_type.index_set()
        G = self.algebra_generators()
        cm = ~CartanMatrix(self._cartan_type)
        n = len(I)
        ac = [G['ac'+str(i)] for i in I]
        la = [sum(cm[i,j]*G['a'+str(I[i])] for i in range(n)) for j in range(n)]
        return self.sum(ac[i]*la[i] for i in range(n))

    @cached_method
    def an_element(self):
        """
        Return an element of ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.an_element()
            3*ac1 + 2*s1 + a1
        """
        G = self.algebra_generators()
        i = str(self._cartan_type.index_set()[0])
        return G['a'+i] + 2*G['s'+i] + 3*G['ac'+i]

    def some_elements(self):
        """
        Return some elements of ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.some_elements()
            [0, I, 3*ac1 + 2*s1 + a1, a1, a2, s1, s2, ac1, ac2]
        """
        ret = [self.zero(), self.one(), self.an_element()]
        ret += list(self.algebra_generators())
        return ret
    def product_on_basis(self, left, right):
        r"""
        Return ``left`` multiplied by ``right`` in ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: a2 = R.algebra_generators()['a2']
            sage: ac1 = R.algebra_generators()['ac1']
            sage: a2 * ac1  # indirect doctest
            a2*ac1
            sage: ac1 * a2
            -I + a2*ac1 - s1 - s2 + 1/2*s1*s2*s1
            sage: x = R.an_element()
            sage: [y * x for y in R.some_elements()]
            [0,
             3*ac1 + 2*s1 + a1,
             9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2,
             3*a1*ac1 + 2*a1*s1 + a1^2,
             3*a2*ac1 + 2*a2*s1 + a1*a2,
             3*s1*ac1 + 2*I - a1*s1,
             3*s2*ac1 + 2*s2*s1 + a1*s2 + a2*s2,
             3*ac1^2 - 2*s1*ac1 + 2*I + a1*ac1 + 2*s1 + 1/2*s2 + 1/2*s1*s2*s1,
             3*ac1*ac2 + 2*s1*ac1 + 2*s1*ac2 - I + a1*ac2 - s1 - s2 + 1/2*s1*s2*s1]
            sage: [x * y for y in R.some_elements()]
            [0,
             3*ac1 + 2*s1 + a1,
             9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2,
             6*I + 3*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 - 2*a1*s1 + a1^2,
             -3*I + 3*a2*ac1 - 3*s1 - 3*s2 + 3/2*s1*s2*s1 + 2*a1*s1 + 2*a2*s1 + a1*a2,
             -3*s1*ac1 + 2*I + a1*s1,
             3*s2*ac1 + 3*s2*ac2 + 2*s1*s2 + a1*s2,
             3*ac1^2 + 2*s1*ac1 + a1*ac1,
             3*ac1*ac2 + 2*s1*ac2 + a1*ac2]
        """
        # Make copies of the internal dictionaries
        dl = dict(left[2]._monomial)
        dr = dict(right[0]._monomial)

        # If there is nothing to commute
        if not dl and not dr:
            return self.monomial((left[0], left[1] * right[1], right[2]))

        R = self.base_ring()
        I = self._cartan_type.index_set()
        P = PolynomialRing(R, 'x', len(I))
        G = P.gens()
        gens_dict = {a: G[i] for i, a in enumerate(I)}
        Q = RootSystem(self._cartan_type).root_lattice()
        alpha = Q.simple_roots()
        alphacheck = Q.simple_coroots()

        def commute_w_hd(w, al):  # al is given as a dictionary
            ret = P.one()
            for k in al:
                x = sum(c * gens_dict[i] for i, c in alpha[k].weyl_action(w))
                ret *= x**al[k]
            ret = ret.dict()
            for k in ret:
                yield (self._hd({I[i]: e
                                 for i, e in enumerate(k) if e != 0}), ret[k])

        # Do Lac Ra if they are both non-trivial
        if dl and dr:
            il = next(iter(dl.keys()))
            ir = next(iter(dr.keys()))

            # Compute the commutator
            terms = self._product_coroot_root(il, ir)

            # remove the generator from the elements
            dl[il] -= 1
            if dl[il] == 0:
                del dl[il]
            dr[ir] -= 1
            if dr[ir] == 0:
                del dr[ir]

            # We now commute right roots past the left reflections: s Ra = Ra' s
            cur = self._from_dict({(hd, s * right[1], right[2]): c * cc
                                   for s, c in terms
                                   for hd, cc in commute_w_hd(s, dr)})
            cur = self.monomial((left[0], left[1], self._h(dl))) * cur

            # Add back in the commuted h and hd elements
            rem = self.monomial((left[0], left[1], self._h(dl)))
            rem = rem * self.monomial(
                (self._hd({ir: 1}), self._weyl.one(), self._h({il: 1})))
            rem = rem * self.monomial((self._hd(dr), right[1], right[2]))

            return cur + rem

        if dl:
            # We have La Ls Lac Rs Rac,
            #   so we must commute Lac Rs = Rs Lac'
            #   and obtain La (Ls Rs) (Lac' Rac)
            ret = P.one()
            for k in dl:
                x = sum(c * gens_dict[i] for i, c in alphacheck[k].weyl_action(
                    right[1].reduced_word(), inverse=True))
                ret *= x**dl[k]
            ret = ret.dict()
            w = left[1] * right[1]
            return self._from_dict({
                (left[0], w,
                 self._h({I[i]: e
                          for i, e in enumerate(k) if e != 0}) * right[2]):
                ret[k]
                for k in ret
            })

        # Otherwise dr is non-trivial and we have La Ls Ra Rs Rac,
        #   so we must commute Ls Ra = Ra' Ls
        w = left[1] * right[1]
        return self._from_dict({(left[0] * hd, w, right[2]): c
                                for hd, c in commute_w_hd(left[1], dr)})
class RationalCherednikAlgebra(CombinatorialFreeModule):
    r"""
    A rational Cherednik algebra.

    Let `k` be a field. Let `W` be a complex reflection group acting on
    a vector space `\mathfrak{h}` (over `k`). Let `\mathfrak{h}^*` denote
    the corresponding dual vector space. Let `\cdot` denote the
    natural action of `w` on `\mathfrak{h}` and `\mathfrak{h}^*`. Let
    `\mathcal{S}` denote the set of reflections of `W` and  `\alpha_s`
    and `\alpha_s^{\vee}` are the associated root and coroot of `s`. Let
    `c = (c_s)_{s \in W}` such that `c_s = c_{tst^{-1}}` for all `t \in W`.

    The *rational Cherednik algebra* is the `k`-algebra
    `H_{c,t}(W) = T(\mathfrak{h} \oplus \mathfrak{h}^*) \otimes kW` with
    parameters `c, t \in k` that is subject to the relations:

    .. MATH::

        \begin{aligned}
        w \alpha & = (w \cdot \alpha) w,
        \\ \alpha^{\vee} w & = w (w^{-1} \cdot \alpha^{\vee}),
        \\ \alpha \alpha^{\vee} & = \alpha^{\vee} \alpha
        + t \langle \alpha^{\vee}, \alpha \rangle
        + \sum_{s \in \mathcal{S}} c_s \frac{\langle \alpha^{\vee},
        \alpha_s \rangle \langle \alpha^{\vee}_s, \alpha \rangle}{
        \langle \alpha^{\vee}, \alpha \rangle} s,
        \end{aligned}

    where `w \in W` and `\alpha \in \mathfrak{h}` and
    `\alpha^{\vee} \in \mathfrak{h}^*`.

    INPUT:

    - ``ct`` -- a finite Cartan type
    - ``c`` -- the parameters `c_s` given as an element or a tuple, where
      the first entry is the one for the long roots and (for
      non-simply-laced types) the second is for the short roots
    - ``t`` -- the parameter `t`
    - ``base_ring`` -- (optional) the base ring
    - ``prefix`` -- (default: ``('a', 's', 'ac')``) the prefixes

    .. TODO::

        Implement a version for complex reflection groups.

    REFERENCES:

    - [GGOR2003]_
    - [EM2001]_
    """
    @staticmethod
    def __classcall_private__(cls,
                              ct,
                              c=1,
                              t=None,
                              base_ring=None,
                              prefix=('a', 's', 'ac')):
        """
        Normalize input to ensure a unique representation.

        EXAMPLES::

            sage: R1 = algebras.RationalCherednik(['B',2], 1, 1, QQ)
            sage: R2 = algebras.RationalCherednik(CartanType(['B',2]), [1,1], 1, QQ, ('a', 's', 'ac'))
            sage: R1 is R2
            True
        """
        ct = CartanType(ct)
        if not ct.is_finite():
            raise ValueError("the Cartan type must be finite")
        if base_ring is None:
            if t is None:
                base_ring = QQ
            else:
                base_ring = t.parent()
        if t is None:
            t = base_ring.one()
        else:
            t = base_ring(t)

        # Normalize the parameter c
        if isinstance(c, (tuple, list)):
            if ct.is_simply_laced():
                if len(c) != 1:
                    raise ValueError(
                        "1 parameter c_s must be given for simply-laced types")
                c = (base_ring(c[0]), )
            else:
                if len(c) != 2:
                    raise ValueError(
                        "2 parameters c_s must be given for non-simply-laced types"
                    )
                c = (base_ring(c[0]), base_ring(c[1]))
        else:
            c = base_ring(c)
            if ct.is_simply_laced():
                c = (c, )
            else:
                c = (c, c)

        return super(RationalCherednikAlgebra,
                     cls).__classcall__(cls, ct, c, t, base_ring,
                                        tuple(prefix))

    def __init__(self, ct, c, t, base_ring, prefix):
        r"""
        Initialize ``self``.

        EXAMPLES::

            sage: k = QQ['c,t']
            sage: R = algebras.RationalCherednik(['A',2], k.gen(0), k.gen(1))
            sage: TestSuite(R).run()  # long time
        """
        self._c = c
        self._t = t
        self._cartan_type = ct
        self._weyl = RootSystem(ct).root_lattice().weyl_group(prefix=prefix[1])
        self._hd = IndexedFreeAbelianMonoid(ct.index_set(),
                                            prefix=prefix[0],
                                            bracket=False)
        self._h = IndexedFreeAbelianMonoid(ct.index_set(),
                                           prefix=prefix[2],
                                           bracket=False)
        indices = DisjointUnionEnumeratedSets([self._hd, self._weyl, self._h])
        CombinatorialFreeModule.__init__(
            self,
            base_ring,
            indices,
            category=Algebras(base_ring).WithBasis().Graded(),
            sorting_key=self._genkey)

    def _genkey(self, t):
        r"""
        Construct a key for comparison for a term indexed by ``t``.

        The key we create is the tuple in the following order:

        - overall degree
        - length of the Weyl group element
        - the Weyl group element
        - the element of `\mathfrak{h}`
        - the element of `\mathfrak{h}^*`

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.an_element()**2 # indirect doctest
            9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2
        """
        return (self.degree_on_basis(t), t[1].length(), t[1], str(t[0]),
                str(t[2]))

    @lazy_attribute
    def _reflections(self):
        """
        A dictionary of reflections to a pair of the associated root
        and coroot.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['B',2], [1,2], 1, QQ)
            sage: [R._reflections[k] for k in sorted(R._reflections, key=str)]
            [(alpha[1], alphacheck[1], 1),
             (alpha[1] + alpha[2], 2*alphacheck[1] + alphacheck[2], 2),
             (alpha[2], alphacheck[2], 2),
             (alpha[1] + 2*alpha[2], alphacheck[1] + alphacheck[2], 1)]
        """
        d = {}
        for r in RootSystem(self._cartan_type).root_lattice().positive_roots():
            s = self._weyl.from_reduced_word(r.associated_reflection())
            if r.is_short_root():
                c = self._c[1]
            else:
                c = self._c[0]
            d[s] = (r, r.associated_coroot(), c)
        return d

    def _repr_(self):
        r"""
        Return a string representation of ``self``.

        EXAMPLES ::

            sage: RationalCherednikAlgebra(['A',4], 2, 1, QQ)
            Rational Cherednik Algebra of type ['A', 4] with c=2 and t=1
             over Rational Field
            sage: algebras.RationalCherednik(['B',2], [1,2], 1, QQ)
            Rational Cherednik Algebra of type ['B', 2] with c_L=1 and c_S=2
             and t=1 over Rational Field
        """
        ret = "Rational Cherednik Algebra of type {} with ".format(
            self._cartan_type)
        if self._cartan_type.is_simply_laced():
            ret += "c={}".format(self._c[0])
        else:
            ret += "c_L={} and c_S={}".format(*self._c)
        return ret + " and t={} over {}".format(self._t, self.base_ring())

    def _repr_term(self, t):
        """
        Return a string representation of the term indexed by ``t``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.an_element() # indirect doctest
            3*ac1 + 2*s1 + a1
            sage: R.one() # indirect doctest
            I
        """
        r = []
        if t[0] != self._hd.one():
            r.append(t[0])
        if t[1] != self._weyl.one():
            r.append(t[1])
        if t[2] != self._h.one():
            r.append(t[2])
        if not r:
            return 'I'
        return '*'.join(repr(x) for x in r)

    def algebra_generators(self):
        """
        Return the algebra generators of ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: list(R.algebra_generators())
            [a1, a2, s1, s2, ac1, ac2]
        """
        keys = ['a' + str(i) for i in self._cartan_type.index_set()]
        keys += ['s' + str(i) for i in self._cartan_type.index_set()]
        keys += ['ac' + str(i) for i in self._cartan_type.index_set()]

        def gen_map(k):
            if k[0] == 's':
                i = int(k[1:])
                return self.monomial(
                    (self._hd.one(), self._weyl.group_generators()[i],
                     self._h.one()))
            if k[1] == 'c':
                i = int(k[2:])
                return self.monomial((self._hd.one(), self._weyl.one(),
                                      self._h.monoid_generators()[i]))

            i = int(k[1:])
            return self.monomial((self._hd.monoid_generators()[i],
                                  self._weyl.one(), self._h.one()))

        return Family(keys, gen_map)

    @cached_method
    def one_basis(self):
        """
        Return the index of the element `1`.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.one_basis()
            (1, 1, 1)
        """
        return (self._hd.one(), self._weyl.one(), self._h.one())

    def product_on_basis(self, left, right):
        r"""
        Return ``left`` multiplied by ``right`` in ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: a2 = R.algebra_generators()['a2']
            sage: ac1 = R.algebra_generators()['ac1']
            sage: a2 * ac1  # indirect doctest
            a2*ac1
            sage: ac1 * a2
            -I + a2*ac1 - s1 - s2 + 1/2*s1*s2*s1
            sage: x = R.an_element()
            sage: [y * x for y in R.some_elements()]
            [0,
             3*ac1 + 2*s1 + a1,
             9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2,
             3*a1*ac1 + 2*a1*s1 + a1^2,
             3*a2*ac1 + 2*a2*s1 + a1*a2,
             3*s1*ac1 + 2*I - a1*s1,
             3*s2*ac1 + 2*s2*s1 + a1*s2 + a2*s2,
             3*ac1^2 - 2*s1*ac1 + 2*I + a1*ac1 + 2*s1 + 1/2*s2 + 1/2*s1*s2*s1,
             3*ac1*ac2 + 2*s1*ac1 + 2*s1*ac2 - I + a1*ac2 - s1 - s2 + 1/2*s1*s2*s1]
            sage: [x * y for y in R.some_elements()]
            [0,
             3*ac1 + 2*s1 + a1,
             9*ac1^2 + 10*I + 6*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 + a1^2,
             6*I + 3*a1*ac1 + 6*s1 + 3/2*s2 + 3/2*s1*s2*s1 - 2*a1*s1 + a1^2,
             -3*I + 3*a2*ac1 - 3*s1 - 3*s2 + 3/2*s1*s2*s1 + 2*a1*s1 + 2*a2*s1 + a1*a2,
             -3*s1*ac1 + 2*I + a1*s1,
             3*s2*ac1 + 3*s2*ac2 + 2*s1*s2 + a1*s2,
             3*ac1^2 + 2*s1*ac1 + a1*ac1,
             3*ac1*ac2 + 2*s1*ac2 + a1*ac2]
        """
        # Make copies of the internal dictionaries
        dl = dict(left[2]._monomial)
        dr = dict(right[0]._monomial)

        # If there is nothing to commute
        if not dl and not dr:
            return self.monomial((left[0], left[1] * right[1], right[2]))

        R = self.base_ring()
        I = self._cartan_type.index_set()
        P = PolynomialRing(R, 'x', len(I))
        G = P.gens()
        gens_dict = {a: G[i] for i, a in enumerate(I)}
        Q = RootSystem(self._cartan_type).root_lattice()
        alpha = Q.simple_roots()
        alphacheck = Q.simple_coroots()

        def commute_w_hd(w, al):  # al is given as a dictionary
            ret = P.one()
            for k in al:
                x = sum(c * gens_dict[i] for i, c in alpha[k].weyl_action(w))
                ret *= x**al[k]
            ret = ret.dict()
            for k in ret:
                yield (self._hd({I[i]: e
                                 for i, e in enumerate(k) if e != 0}), ret[k])

        # Do Lac Ra if they are both non-trivial
        if dl and dr:
            il = next(iter(dl.keys()))
            ir = next(iter(dr.keys()))

            # Compute the commutator
            terms = self._product_coroot_root(il, ir)

            # remove the generator from the elements
            dl[il] -= 1
            if dl[il] == 0:
                del dl[il]
            dr[ir] -= 1
            if dr[ir] == 0:
                del dr[ir]

            # We now commute right roots past the left reflections: s Ra = Ra' s
            cur = self._from_dict({(hd, s * right[1], right[2]): c * cc
                                   for s, c in terms
                                   for hd, cc in commute_w_hd(s, dr)})
            cur = self.monomial((left[0], left[1], self._h(dl))) * cur

            # Add back in the commuted h and hd elements
            rem = self.monomial((left[0], left[1], self._h(dl)))
            rem = rem * self.monomial(
                (self._hd({ir: 1}), self._weyl.one(), self._h({il: 1})))
            rem = rem * self.monomial((self._hd(dr), right[1], right[2]))

            return cur + rem

        if dl:
            # We have La Ls Lac Rs Rac,
            #   so we must commute Lac Rs = Rs Lac'
            #   and obtain La (Ls Rs) (Lac' Rac)
            ret = P.one()
            for k in dl:
                x = sum(c * gens_dict[i] for i, c in alphacheck[k].weyl_action(
                    right[1].reduced_word(), inverse=True))
                ret *= x**dl[k]
            ret = ret.dict()
            w = left[1] * right[1]
            return self._from_dict({
                (left[0], w,
                 self._h({I[i]: e
                          for i, e in enumerate(k) if e != 0}) * right[2]):
                ret[k]
                for k in ret
            })

        # Otherwise dr is non-trivial and we have La Ls Ra Rs Rac,
        #   so we must commute Ls Ra = Ra' Ls
        w = left[1] * right[1]
        return self._from_dict({(left[0] * hd, w, right[2]): c
                                for hd, c in commute_w_hd(left[1], dr)})

    @cached_method
    def _product_coroot_root(self, i, j):
        r"""
        Return the product `\alpha^{\vee}_i \alpha_j`.

        EXAMPLES::

            sage: k = QQ['c,t']
            sage: R = algebras.RationalCherednik(['A',3], k.gen(0), k.gen(1))
            sage: sorted(R._product_coroot_root(1, 1))
            [(s1, 2*c),
             (s1*s2*s1, 1/2*c),
             (s1*s2*s3*s2*s1, 1/2*c),
             (1, 2*t),
             (s3, 0),
             (s2, 1/2*c),
             (s2*s3*s2, 1/2*c)]

            sage: sorted(R._product_coroot_root(1, 2))
            [(s1, -c),
             (s1*s2*s1, 1/2*c),
             (s1*s2*s3*s2*s1, 0),
             (1, -t),
             (s3, 0),
             (s2, -c),
             (s2*s3*s2, -1/2*c)]

            sage: sorted(R._product_coroot_root(1, 3))
            [(s1, 0),
             (s1*s2*s1, -1/2*c),
             (s1*s2*s3*s2*s1, 1/2*c),
             (1, 0),
             (s3, 0),
             (s2, 1/2*c),
             (s2*s3*s2, -1/2*c)]
        """
        Q = RootSystem(self._cartan_type).root_lattice()
        ac = Q.simple_coroot(i)
        al = Q.simple_root(j)

        R = self.base_ring()
        terms = [(self._weyl.one(), self._t * R(ac.scalar(al)))]
        for s in self._reflections:
            # p[0] is the root, p[1] is the coroot, p[2] the value c_s
            pr, pc, c = self._reflections[s]
            terms.append(
                (s, c * R(ac.scalar(pr) * pc.scalar(al) / pc.scalar(pr))))
        return tuple(terms)

    def degree_on_basis(self, m):
        """
        Return the degree on the monomial indexed by ``m``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: [R.degree_on_basis(g.leading_support())
            ....:  for g in R.algebra_generators()]
            [1, 1, 0, 0, -1, -1]
        """
        return m[0].length() - m[2].length()

    @cached_method
    def trivial_idempotent(self):
        r"""
        Return the trivial idempotent of ``self``.

        Let `e = |W|^{-1} \sum_{w \in W} w` is the trivial idempotent.
        Thus `e^2 = e` and `eW = We`. The trivial idempotent is used
        in the construction of the spherical Cherednik algebra from
        the rational Cherednik algebra by `U_{c,t}(W) = e H_{c,t}(W) e`.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.trivial_idempotent()
            1/6*I + 1/6*s1 + 1/6*s2 + 1/6*s2*s1 + 1/6*s1*s2 + 1/6*s1*s2*s1
        """
        coeff = self.base_ring()(~self._weyl.cardinality())
        hd_one = self._hd.one()  # root - a
        h_one = self._h.one()  # coroot - ac
        return self._from_dict({(hd_one, w, h_one): coeff
                                for w in self._weyl},
                               remove_zeros=False)

    @cached_method
    def deformed_euler(self):
        """
        Return the element `eu_k`.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.deformed_euler()
            2*I + 2/3*a1*ac1 + 1/3*a1*ac2 + 1/3*a2*ac1 + 2/3*a2*ac2
             + s1 + s2 + s1*s2*s1
        """
        I = self._cartan_type.index_set()
        G = self.algebra_generators()
        cm = ~CartanMatrix(self._cartan_type)
        n = len(I)
        ac = [G['ac' + str(i)] for i in I]
        la = [
            sum(cm[i, j] * G['a' + str(I[i])] for i in range(n))
            for j in range(n)
        ]
        return self.sum(ac[i] * la[i] for i in range(n))

    @cached_method
    def an_element(self):
        """
        Return an element of ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.an_element()
            3*ac1 + 2*s1 + a1
        """
        G = self.algebra_generators()
        i = str(self._cartan_type.index_set()[0])
        return G['a' + i] + 2 * G['s' + i] + 3 * G['ac' + i]

    def some_elements(self):
        """
        Return some elements of ``self``.

        EXAMPLES::

            sage: R = algebras.RationalCherednik(['A',2], 1, 1, QQ)
            sage: R.some_elements()
            [0, I, 3*ac1 + 2*s1 + a1, a1, a2, s1, s2, ac1, ac2]
        """
        ret = [self.zero(), self.one(), self.an_element()]
        ret += list(self.algebra_generators())
        return ret
Пример #20
0
class CrystalOfLSPaths(UniqueRepresentation, Parent):
    r"""
    Crystal graph of LS paths generated from the straight-line path to a given weight.

    INPUT:

    - ``cartan_type`` -- the Cartan type of a finite or affine root system
    - ``starting_weight`` -- a weight given as a list of coefficients of the fundamental weights

    The crystal class of piecewise linear paths in the weight space,
    generated from a straight-line path from the origin to a given
    element of the weight lattice.

    OUTPUT: - a tuple of weights defining the directions of the piecewise linear segments

    EXAMPLES::

        sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1]); C
        The crystal of LS paths of type ['A', 2, 1] and weight (-1, 0, 1)
        sage: c = C.module_generators[0]; c
        (-Lambda[0] + Lambda[2],)
        sage: [c.f(i) for i in C.index_set()]
        [None, None, (Lambda[1] - Lambda[2],)]

        sage: R = C.R; R
        Root system of type ['A', 2, 1]
        sage: Lambda = R.weight_space().basis(); Lambda
        Finite family {0: Lambda[0], 1: Lambda[1], 2: Lambda[2]}
        sage: b=C(tuple([-Lambda[0]+Lambda[2]]))
        sage: b==c
        True
        sage: b.f(2)
        (Lambda[1] - Lambda[2],)

    For classical highest weight crystals we can also compare the results with the tableaux implementation::

        sage: C = CrystalOfLSPaths(['A',2],[1,1])
        sage: list(set(C.list()))
        [(-Lambda[1] - Lambda[2],), (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2]), (-Lambda[1] + 2*Lambda[2],),
        (1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2]), (Lambda[1] - 2*Lambda[2],), (-2*Lambda[1] + Lambda[2],),
        (2*Lambda[1] - Lambda[2],), (Lambda[1] + Lambda[2],)]
        sage: C.cardinality()
        8
        sage: B = CrystalOfTableaux(['A',2],shape=[2,1])
        sage: B.cardinality()
        8
        sage: B.digraph().is_isomorphic(C.digraph())
        True

    TESTS::

        sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1])
        sage: TestSuite(C).run(skip=['_test_elements', '_test_elements_eq', '_test_enumerated_set_contains', '_test_some_elements'])
        sage: C = CrystalOfLSPaths(['E',6],[1,0,0,0,0,0])
        sage: TestSuite(C).run()

    REFERENCES::

        .. [L] P. Littelmann, Paths and root operators in representation theory. Ann. of Math. (2) 142 (1995), no. 3, 499-525.
    """

    @staticmethod
    def __classcall__(cls, cartan_type, starting_weight):
        """
        cartan_type and starting_weight are lists, which are mutable. The class
        UniqueRepresentation requires immutable inputs. The following code
        fixes this problem.

        TESTS::

            sage: CrystalOfLSPaths.__classcall__(CrystalOfLSPaths,['A',2,1],[-1,0,1])
            The crystal of LS paths of type ['A', 2, 1] and weight (-1, 0, 1)
        """
        cartan_type = CartanType(cartan_type)
        starting_weight = tuple(starting_weight)
        return super(CrystalOfLSPaths, cls).__classcall__(cls, cartan_type, starting_weight)

    def __init__(self, cartan_type, starting_weight):
        """
        EXAMPLES::

            sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1]); C
            The crystal of LS paths of type ['A', 2, 1] and weight (-1, 0, 1)
            sage: C.R
            Root system of type ['A', 2, 1]
            sage: C.weight
            -Lambda[0] + Lambda[2]
            sage: C.weight.parent()
            Extended weight space over the Rational Field of the Root system of type ['A', 2, 1]
            sage: C.module_generators
            [(-Lambda[0] + Lambda[2],)]
        """
        self._cartan_type = CartanType(cartan_type)
        self.R = RootSystem(cartan_type)

        self._name = "The crystal of LS paths of type %s and weight %s"%(cartan_type,starting_weight)

        if self._cartan_type.is_affine():
            self.extended = True
            if all(i>=0 for i in starting_weight):
                Parent.__init__(self, category = HighestWeightCrystals())
            else:
                Parent.__init__(self, category = Crystals())
        else:
            self.extended = False
            Parent.__init__(self, category = FiniteCrystals())

        Lambda = self.R.weight_space(extended = self.extended).basis()
        offset = self.R.index_set()[Integer(0)]

        zero_weight = self.R.weight_space(extended = self.extended).zero()
        self.weight = sum([zero_weight]+[starting_weight[j-offset]*Lambda[j] for j in self.R.index_set()])

        if self.weight == zero_weight:
            initial_element = self(tuple([]))
        else:
            initial_element = self(tuple([self.weight]))
        self.module_generators = [initial_element]

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

            sage: CrystalOfLSPaths(['B',3],[1,1,0]) # indirect doctest
            The crystal of LS paths of type ['B', 3] and weight (1, 1, 0)
        """
        return self._name

    class Element(ElementWrapper):
        """
        TESTS::

            sage: C = CrystalOfLSPaths(['E',6],[1,0,0,0,0,0])
            sage: c=C.an_element()
            sage: TestSuite(c).run()
        """

        def endpoint(self):
            r"""
            Computes the endpoint of the path.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: b = C.module_generators[0]
                sage: b.endpoint()
                Lambda[1] + Lambda[2]
                sage: b.f_string([1,2,2,1])
                (-Lambda[1] - Lambda[2],)
                sage: b.f_string([1,2,2,1]).endpoint()
                -Lambda[1] - Lambda[2]
                sage: b.f_string([1,2])
                (1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])
                sage: b.f_string([1,2]).endpoint()
                0
                sage: b = C([])
                sage: b.endpoint()
                0
            """
            if len(self.value) > 0:
                return sum(self.value)
            return self.parent().R.weight_space(extended = self.parent().extended).zero()

        def compress(self):
            r"""
            Merges consecutive positively parallel steps present in the path.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: Lambda = C.R.weight_space().fundamental_weights(); Lambda
                Finite family {1: Lambda[1], 2: Lambda[2]}
                sage: c = C(tuple([1/2*Lambda[1]+1/2*Lambda[2], 1/2*Lambda[1]+1/2*Lambda[2]]))
                sage: c.compress()
                (Lambda[1] + Lambda[2],)
            """
            def positively_parallel_weights(v, w):
                """
                Checks whether the vectors ``v`` and ``w`` are positive scalar multiples of each other.
                """
                supp = v.support()
                if len(supp) > 0:
                    i = supp[0]
                    if v[i]*w[i] > 0 and v[i]*w == w[i]*v:
                        return True
                return False

            if len(self.value) == 0:
                return self
            q = []
            curr = self.value[0]
            for i in range(1,len(self.value)):
                if positively_parallel_weights(curr,self.value[i]):
                    curr = curr + self.value[i]
                else:
                    q.append(curr)
                    curr = self.value[i]
            q.append(curr)
            return self.parent()(tuple(q))

        def split_step(self, which_step, r):
            r"""
            Splits indicated step into two parallel steps of relative lengths `r` and `1-r`.

            INPUT:
            - ``which_step`` -- a position in the tuple ``self``
            - ``r`` -- a rational number between 0 and 1

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: b = C.module_generators[0]
                sage: b.split_step(0,1/3)
                (1/3*Lambda[1] + 1/3*Lambda[2], 2/3*Lambda[1] + 2/3*Lambda[2])
            """
            assert which_step in range(len(self.value))
            v = self.value[which_step]
            return self.parent()(self.value[:which_step]+tuple([r*v,(1-r)*v])+self.value[which_step+1:])

        def reflect_step(self, which_step, i):
            r"""
            Apply the `i`-th simple reflection to the indicated step in ``self``.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: b = C.module_generators[0]
                sage: b.reflect_step(0,1)
                (-Lambda[1] + 2*Lambda[2],)
                sage: b.reflect_step(0,2)
                (2*Lambda[1] - Lambda[2],)
            """
            assert i in self.index_set()
            assert which_step in range(len(self.value))
            return self.parent()(self.value[:which_step]+tuple([self.value[which_step].simple_reflection(i)])+self.value[which_step+1:])

        def _string_data(self, i):
            r"""
            Computes the `i`-string data of ``self``.

            TESTS::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: b = C.module_generators[0]
                sage: b._string_data(1)
                ()
                sage: b._string_data(2)
                ()
                sage: b.f(1)._string_data(1)
                ((0, -1, -1),)
                sage: b.f(1).f(2)._string_data(2)
                ((0, -1, -1),)
            """
            if len(self.value) == 0:
                return ()
            # get the i-th simple coroot
            alv = self.value[0].parent().alphacheck()[i]
            # Compute the i-heights of the steps of vs
            steps = [v.scalar(alv) for v in self.value]
            # Get the wet step data
            minima_pos = []
            ps = 0
            psmin = 0
            for ix in range(len(steps)):
                ps = ps + steps[ix]
                if ps < psmin:
                    minima_pos.append((ix,ps,steps[ix]))
                    psmin = ps
            return tuple(minima_pos)

        def epsilon(self, i):
            r"""
            Returns the distance to the beginning of the `i`-string.

            This method overrides the generic implementation in the category of crystals
            since this computation is more efficient.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: [c.epsilon(1) for c in C]
                [0, 1, 0, 0, 1, 0, 1, 2]
                sage: [c.epsilon(2) for c in C]
                [0, 0, 1, 2, 1, 1, 0, 0]
            """
            return self.e(i,length_only=True)

        def phi(self, i):
            r"""
            Returns the distance to the end of the `i`-string.

            This method overrides the generic implementation in the category of crystals
            since this computation is more efficient.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: [c.phi(1) for c in C]
                [1, 0, 0, 1, 0, 2, 1, 0]
                sage: [c.phi(2) for c in C]
                [1, 2, 1, 0, 0, 0, 0, 1]
            """
            return self.f(i,length_only=True)

        def e(self, i, power=1, to_string_end=False, length_only=False):
            r"""
            Returns the `i`-th crystal raising operator on ``self``.

            INPUT:

            - ``i`` -- element of the index set of the underlying root system
            - ``power`` -- positive integer; specifies the power of the raising operator
               to be applied (default: 1)
            - ``to_string_end`` -- boolean; if set to True, returns the dominant end of the
              `i`-string of ``self``. (default: False)
            - ``length_only`` -- boolean; if set to True, returns the distance to the dominant
               end of the `i`-string of ``self``.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: c = C[2]; c
                (1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])
                sage: c.e(1)
                sage: c.e(2)
                (-Lambda[1] + 2*Lambda[2],)
                sage: c.e(2,to_string_end=True)
                (-Lambda[1] + 2*Lambda[2],)
                sage: c.e(1,to_string_end=True)
                (1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])
                sage: c.e(1,length_only=True)
                0
            """
            assert i in self.index_set()
            data = self._string_data(i)
            # compute the minimum i-height M on the path
            if len(data) == 0:
                M = 0
            else:
                M = data[-1][1]
            max_raisings = floor(-M)
            if length_only:
                return max_raisings
            # set the power of e_i to apply
            if to_string_end:
                p = max_raisings
            else:
                p = power
            if p > max_raisings:
                return None

            # copy the vector sequence into a working vector sequence ws
            #!!! ws only needs to be the actual vector sequence, not some
            #!!! fancy crystal graph element
            ws = self.parent()(self.value)

            ix = len(data)-1
            while ix >= 0 and data[ix][1] < M + p:
            # get the index of the current step to be processed
                j = data[ix][0]
                # find the i-height where the current step might need to be split
                if ix == 0:
                    prev_ht = M + p
                else:
                    prev_ht = min(data[ix-1][1],M+p)
                # if necessary split the step. Then reflect the wet part.
                if data[ix][1] - data[ix][2] > prev_ht:
                    ws = ws.split_step(j,1-(prev_ht-data[ix][1])/(-data[ix][2]))
                    ws = ws.reflect_step(j+1,i)
                else:
                    ws = ws.reflect_step(j,i)
                ix = ix - 1
            #!!! at this point we should return the fancy crystal graph element
            #!!! corresponding to the humble vector sequence ws
            return self.parent()(ws.compress())

        def dualize(self):
            r"""
            Returns dualized path.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: for c in C:
                ...     print c, c.dualize()
                ...    
                (Lambda[1] + Lambda[2],) (-Lambda[1] - Lambda[2],)
                (-Lambda[1] + 2*Lambda[2],) (Lambda[1] - 2*Lambda[2],)
                (1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2]) (1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])
                (Lambda[1] - 2*Lambda[2],) (-Lambda[1] + 2*Lambda[2],)
                (-Lambda[1] - Lambda[2],) (Lambda[1] + Lambda[2],)
                (2*Lambda[1] - Lambda[2],) (-2*Lambda[1] + Lambda[2],)
                (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2]) (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2])
                (-2*Lambda[1] + Lambda[2],) (2*Lambda[1] - Lambda[2],)
            """
            if len(self.value) == 0:
                return self
            dual_path = [-v for v in self.value]
            dual_path.reverse()
            return self.parent()(tuple(dual_path))

        def f(self, i, power=1, to_string_end=False, length_only=False):
            r"""
            Returns the `i`-th crystal lowering operator on ``self``.

            INPUT:

            - ``i`` -- element of the index set of the underlying root system
            - ``power`` -- positive integer; specifies the power of the lowering operator
               to be applied (default: 1)
            - ``to_string_end`` -- boolean; if set to True, returns the anti-dominant end of the
              `i`-string of ``self``. (default: False)
            - ``length_only`` -- boolean; if set to True, returns the distance to the anti-dominant
               end of the `i`-string of ``self``.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: c = C.module_generators[0]
                sage: c.f(1)
                (-Lambda[1] + 2*Lambda[2],)
                sage: c.f(1,power=2)
                sage: c.f(2)
                (2*Lambda[1] - Lambda[2],)
                sage: c.f(2,to_string_end=True)
                (2*Lambda[1] - Lambda[2],)
                sage: c.f(2,length_only=True)
                1

                sage: C = CrystalOfLSPaths(['A',2,1],[-1,-1,2])
                sage: c = C.module_generators[0]
                sage: c.f(2,power=2)
                (Lambda[0] + Lambda[1] - 2*Lambda[2],)
            """
            dual_path = self.dualize()
            dual_path = dual_path.e(i, power, to_string_end, length_only)
            if length_only:
                return dual_path
            if dual_path == None:
                return None
            return dual_path.dualize()

        def s(self, i):
            r"""
            Computes the reflection of ``self`` along the `i`-string.

            This method is more efficient than the generic implementation since it uses
            powers of `e` and `f` in the Littelmann model directly.

            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: c = C.module_generators[0]
                sage: c.s(1)
                (-Lambda[1] + 2*Lambda[2],)
                sage: c.s(2)
                (2*Lambda[1] - Lambda[2],)

                sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1])
                sage: c = C.module_generators[0]; c
                (-Lambda[0] + Lambda[2],)
                sage: c.s(2)
                (Lambda[1] - Lambda[2],)
                sage: c.s(1)
                (-Lambda[0] + Lambda[2],)
                sage: c.f(2).s(1)
                (Lambda[0] - Lambda[1],)
            """
            ph = self.phi(i)
            ep = self.epsilon(i)
            diff = ph - ep
            if diff >= 0:
                return self.f(i, power=diff)
            else:
                return self.e(i, power=-diff)

        def _latex_(self):
            r"""
            Latex method for ``self``.
           
            EXAMPLES::

                sage: C = CrystalOfLSPaths(['A',2],[1,1])
                sage: c = C.module_generators[0]
                sage: c._latex_()
                [\Lambda_{1} + \Lambda_{2}]
            """
            return [latex(p) for p in self.value]
Пример #21
0
class ClassicalCrystalOfAlcovePaths(UniqueRepresentation, Parent):
    r"""
    Implementation of crystal of alcove paths of the given classical type with
    given highest weight, based on the Lenart--Postnikov model [LP2008].

    These are highest weight crystals for classical types `A_n`, `B_n`, `C_n`,
    `D_n` and the exceptional types `F_4`, `G_2`, `E_6`, `E_7`, `E_8`.

    INPUT:

        - ``cartan_type`` is the Cartan type of a classical Dynkin diagram
        - ``highest_weight`` is a dominant weight as a list of coefficients of
          the fundamental weights `Lambda_i`

    In this model, a chain of roots is associated to the given highest_weight,
    and then the elements of the crystal are indexed by "admissible subsets"
    which indicate "folding positions" along the chain of roots.  See [LP2008]
    for details.

    TODO:

    - Resolve speed issues;  `E_6(\Lambda_1)` takes just under 4 minutes to list().
      To construct the highest-weight node takes 15 sec for `E_6(\Lambda_4)`.
      The initial chain has 42 roots.

    TESTS:

    The following example appears in Figure 2 of [LP2008]::

        sage: C = ClassicalCrystalOfAlcovePaths(['G',2],[0,1]);
        sage: G = C.digraph()
        sage: GG= DiGraph({
        ...       ()        : {(0)         : 2 },
        ...       (0)       : {(0,8)       : 1 },
        ...       (0,1)     : {(0,1,7)     : 2 },
        ...       (0,1,2)   : {(0,1,2,9)   : 1 },
        ...       (0,1,2,3) : {(0,1,2,3,4) : 2 },
        ...       (0,1,2,6) : {(0,1,2,3)   : 1 },
        ...       (0,1,2,9) : {(0,1,2,6)   : 1 },
        ...       (0,1,7)   : {(0,1,2)     : 2 },
        ...       (0,1,7,9) : {(0,1,2,9)   : 2 },
        ...       (0,5)     : {(0,1)       : 1, (0,5,7) : 2 },
        ...       (0,5,7)   : {(0,5,7,9)   : 1 },
        ...       (0,5,7,9) : {(0,1,7,9)   : 1 },
        ...       (0,8)     : {(0,5)       : 1 },
        ...       })
        sage: G.is_isomorphic(GG)
        True

        sage: for edge in sorted([(u.value, v.value, i) for (u,v,i) in G.edges()]):
        ...       print edge
        ([], [0], 2)
        ([0], [0, 8], 1)
        ([0, 1], [0, 1, 7], 2)
        ([0, 1, 2], [0, 1, 2, 9], 1)
        ([0, 1, 2, 3], [0, 1, 2, 3, 4], 2)
        ([0, 1, 2, 6], [0, 1, 2, 3], 1)
        ([0, 1, 2, 9], [0, 1, 2, 6], 1)
        ([0, 1, 7], [0, 1, 2], 2)
        ([0, 1, 7, 9], [0, 1, 2, 9], 2)
        ([0, 5], [0, 1], 1)
        ([0, 5], [0, 5, 7], 2)
        ([0, 5, 7], [0, 5, 7, 9], 1)
        ([0, 5, 7, 9], [0, 1, 7, 9], 1)
        ([0, 8], [0, 5], 1)

    REFERENCES:

        .. [LP2008]  C. Lenart and A. Postnikov. A combinatorial model for crystals of Kac-Moody algebras. Trans. Amer. Math. Soc.  360  (2008), 4349-4381. 
    """
    @staticmethod
    def __classcall__(cls, cartan_type, highest_weight):
        """
        cartan_type and heighest_weight are lists, which are mutable, this
        causes a problem for class UniqueRepresentation, the following code
        fixes this problem.

        EXAMPLES::
            sage: ClassicalCrystalOfAlcovePaths.__classcall__(ClassicalCrystalOfAlcovePaths,['A',3],[0,1,0])
            <class 'sage.combinat.crystals.alcove_path.ClassicalCrystalOfAlcovePaths_with_category'>
        """
        cartan_type = CartanType(cartan_type)
        highest_weight = tuple(highest_weight)
        return super(ClassicalCrystalOfAlcovePaths,
                     cls).__classcall__(cls, cartan_type, highest_weight)

    def __init__(self, cartan_type, highest_weight):
        """
        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[1,0,0])
            sage: C.list()
            [[], [0], [0, 1], [0, 1, 2]]
            sage: TestSuite(C).run()
        """
        Parent.__init__(self, category=ClassicalCrystals())
        self._cartan_type = CartanType(cartan_type)
        self._name = "The crystal of alcove paths for type %s" % cartan_type
        self.chain_cache = {}
        self.endweight_cache = {}

        self.R = RootSystem(cartan_type)
        alpha = self.R.root_space().simple_roots()
        Lambda = self.R.weight_space().basis()

        self.positive_roots = sorted(self.R.root_space().positive_roots())

        self.weight = Lambda[Integer(1)] - Lambda[Integer(1)]
        offset = self.R.index_set()[Integer(0)]
        for j in self.R.index_set():
            self.weight = self.weight + highest_weight[j - offset] * Lambda[j]

        self.initial_element = self([])

        self.initial_element.chain = self.get_initial_chain(self.weight)
        rho = (Integer(1) / Integer(2)) * sum(self.positive_roots)
        self.initial_element.endweight = rho

        self.chain_cache[str([])] = self.initial_element.chain
        self.endweight_cache[str([])] = self.initial_element.endweight

        self.module_generators = [self.initial_element]

        self._list = super(ClassicalCrystalOfAlcovePaths, self).list()
        self._digraph = super(ClassicalCrystalOfAlcovePaths, self).digraph()
        self._digraph_closure = self.digraph().transitive_closure()

    def get_initial_chain(self, highest_weight):
        """
        Called internally by __init__() to construct the chain of roots
        associated to the highest weight element.

        EXAMPLES::
            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[0,1,0])
            sage: C.get_initial_chain(RootSystem(['A',3]).weight_space().basis()[1])
            [[alpha[1], alpha[1]], [alpha[1] + alpha[2], alpha[1] + alpha[2]], [alpha[1] + alpha[2] + alpha[3], alpha[1] + alpha[2] + alpha[3]]]
        """
        pos_roots = self.positive_roots
        tis = self.R.index_set()
        tis.reverse()
        cv_to_pos_root = {}
        to_sort = []
        for r in pos_roots:
            j = highest_weight.scalar(r.associated_coroot())
            if (int(math.floor(j)) - j == Integer(0)):
                j = j - Integer(1)
            j = int(math.floor(j))
            for k in (ellipsis_range(Integer(0), Ellipsis, j)):
                cv = []
                cv.append(
                    (Integer(1) / highest_weight.scalar(r.associated_coroot()))
                    * k)
                for i in tis:
                    cv.append((Integer(1) /
                               highest_weight.scalar(r.associated_coroot())) *
                              r.associated_coroot().coefficient(i))
                cv_to_pos_root[str(cv)] = r
                to_sort.append(cv)

        to_sort.sort(
        )  # Note:  Python sorts nested lists lexicographically by default.
        lambda_chain = []
        for t in to_sort:
            lambda_chain.append(
                [cv_to_pos_root[str(t)], cv_to_pos_root[str(t)]])
        return lambda_chain

    def _element_constructor_(self, value):
        """
        Coerces value into self.

        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[1,0,0])
            sage: C.module_generators
            [[]]
            sage: C([]).e(1)
            sage: C([]).f(1)
            [0]
        """
        return self.element_class(self, value)

    def list(self):
        """
        Returns a list of the elements of self.

        .. warning::

           This can be slow!

        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[0,1,0])
            sage: C.list()
            [[], [0], [0, 1], [0, 2], [0, 1, 2], [0, 1, 2, 3]]
        """
        return self._list

    def digraph(self):
        """
        Returns the directed graph associated to self.

        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[0,1,0])
            sage: C.digraph().vertices()
            [[], [0], [0, 1], [0, 2], [0, 1, 2], [0, 1, 2, 3]]
            sage: C.digraph().edges()
            [([], [0], 2), ([0], [0, 1], 1), ([0], [0, 2], 3), ([0, 1], [0, 1, 2], 3), ([0, 2], [0, 1, 2], 1), ([0, 1, 2], [0, 1, 2, 3], 2)]
        """
        return self._digraph

    def lt_elements(self, x, y):
        r"""
        Returns True if and only if there is a path
        from x to y in the crystal graph.

        Because the crystal graph is classical, it is a directed
        acyclic graph which can be interpreted as a poset. This
        function implements the comparison function of this poset.

        EXAMPLES::

            sage: C = ClassicalCrystalOfAlcovePaths(['A',3],[0,1,0])
            sage: x = C([])
            sage: y = C([0,2])
            sage: C.lt_elements(x,y)
            True
            sage: C.lt_elements(y,x)
            False
            sage: C.lt_elements(x,x)
            False
        """
        assert x.parent() == self and y.parent() == self
        if self._digraph_closure.has_edge(x, y):
            return True
        return False