Ticket #8911: trac_8911_categorification_crystals-as.patch

File trac_8911_categorification_crystals-as.patch, 182.6 KB (added by aschilling, 10 years ago)
  • doc/en/reference/categories.rst

    # HG changeset patch
    # User Anne Schilling <anne@math.ucdavis.edu>
    # Date 1275952702 25200
    # Node ID 0964e28531dba713cfad44ee97057d62063a150f
    # Parent  3c17cc1f5cfccba604f1220e9b5bc26a3f03029d
    Trac # 8911: Use category framework for crystal code
    (depends on #8881, #9178)
    
    New crystal categories:
    
    - crystals
    - finite_crystals
    - highest_weight_crystals
    - classical_crystals
    
    together with a template in
    categories/example/crystals
    on how to implement a new crystal.
    
    The files
    
    combinat/crystals/letters.py and
    combinat/crystals/tensor_product.py
    combinat/crystals/spins.py
    combinat/crystals/fast_crystals.py
    combinat/crystals/highest_weight_crystals.py
    combinat/crystals/direct_sum.py
    combinat/crystals/affine.py
    combinat/crystals/kirillov_reshetikhin.py
    
    have been categorified. What was before in
    
    combinat/crystals
    
    is now mostly in the various categories except for the BackTracker class and the documentation about crystals.
    
    This patch breaks old crystal pickles. Well, those were actually silently broken since #7978 four months ago,
    and no-one voted for against this on sage-combinat-devel.
    
    Depends on #8881 and #9178. Requires updating Sage's pickle jar.
    
    diff --git a/doc/en/reference/categories.rst b/doc/en/reference/categories.rst
    a b Categories 
    4646   sage/categories/bialgebras
    4747   sage/categories/bialgebras_with_basis
    4848   sage/categories/bimodules
     49   sage/categories/classical_crystals
    4950   sage/categories/coalgebras
    5051   sage/categories/coalgebras_with_basis
    5152   sage/categories/commutative_additive_groups
    Categories 
    5657   sage/categories/commutative_ring_ideals
    5758   sage/categories/commutative_rings
    5859   sage/categories/coxeter_groups
     60   sage/categories/crystals
    5961   sage/categories/division_rings
    6062   sage/categories/domains
    6163   sage/categories/enumerated_sets
    6264   sage/categories/euclidean_domains
    6365   sage/categories/fields
     66   sage/categories/finite_crystals
    6467   sage/categories/finite_coxeter_groups
    6568   sage/categories/finite_dimensional_algebras_with_basis
    6669   sage/categories/finite_dimensional_bialgebras_with_basis
    Categories 
    8891   sage/categories/groups
    8992   sage/categories/g_sets
    9093   sage/categories/hecke_modules
     94   sage/categories/highest_weight_crystals
    9195   sage/categories/hopf_algebras
    9296   sage/categories/hopf_algebras_with_basis
    9397   sage/categories/infinite_enumerated_sets
  • doc/en/reference/combinat/crystals.rst

    diff --git a/doc/en/reference/combinat/crystals.rst b/doc/en/reference/combinat/crystals.rst
    a b Crystals 
    44.. toctree::
    55   :maxdepth: 2
    66
     7   ../sage/combinat/crystals/affine
    78   ../sage/combinat/crystals/crystals
     9   ../sage/combinat/crystals/direct_sum
    810   ../sage/combinat/crystals/letters
     11   ../sage/combinat/crystals/fast_crystals
     12   ../sage/combinat/crystals/highest_weight_crystals
     13   ../sage/combinat/crystals/kirillov_reshetikhin
    914   ../sage/combinat/crystals/spins
    1015   ../sage/combinat/crystals/tensor_product
    11    ../sage/combinat/crystals/fast_crystals
    12  No newline at end of file
  • sage/categories/all.py

    diff --git a/sage/categories/all.py b/sage/categories/all.py
    a b from finite_coxeter_groups import Finite 
    114114from weyl_groups import WeylGroups
    115115from finite_weyl_groups import FiniteWeylGroups
    116116from affine_weyl_groups import AffineWeylGroups
     117
     118# crystal bases
     119from crystals import Crystals
     120from highest_weight_crystals import HighestWeightCrystals
     121from finite_crystals import FiniteCrystals
     122from classical_crystals import ClassicalCrystals
  • new file sage/categories/classical_crystals.py

    diff --git a/sage/categories/classical_crystals.py b/sage/categories/classical_crystals.py
    new file mode 100644
    - +  
     1r"""
     2Classical Crystals
     3"""
     4#*****************************************************************************
     5#  Copyright (C) 2010    Anne Schilling <anne at math.ucdavis.edu>
     6#
     7#  Distributed under the terms of the GNU General Public License (GPL)
     8#                  http://www.gnu.org/licenses/
     9#******************************************************************************
     10
     11from sage.misc.cachefunc import cached_method
     12from sage.categories.category import Category
     13from sage.categories.finite_crystals import FiniteCrystals
     14from sage.categories.highest_weight_crystals import HighestWeightCrystals
     15
     16class ClassicalCrystals(Category):
     17    """
     18    The category of classical crystals, that is crystals of finite Cartan type.
     19
     20    EXAMPLES::
     21
     22        sage: C = ClassicalCrystals()
     23        sage: C
     24        Category of classical crystals
     25        sage: C.super_categories()
     26        [Category of finite crystals, Category of highest weight crystals]
     27        sage: C.example()
     28        Highest weight crystal of type A_3 of highest weight omega_1
     29
     30    TESTS::
     31
     32        sage: TestSuite(C).run()
     33        sage: B = FiniteCrystals().example()
     34        sage: TestSuite(B).run(verbose = True)
     35        running ._test_an_element() . . . pass
     36        running ._test_category() . . . pass
     37        running ._test_elements() . . .
     38          Running the test suite of self.an_element()
     39          running ._test_category() . . . pass
     40          running ._test_eq() . . . pass
     41          running ._test_not_implemented_methods() . . . pass
     42          running ._test_pickling() . . . pass
     43          pass
     44        running ._test_elements_eq() . . . pass
     45        running ._test_enumerated_set_contains() . . . pass
     46        running ._test_enumerated_set_iter_cardinality() . . . pass
     47        running ._test_enumerated_set_iter_list() . . . pass
     48        running ._test_eq() . . . pass
     49        running ._test_fast_iter() . . . pass
     50        running ._test_not_implemented_methods() . . . pass
     51        running ._test_pickling() . . . pass
     52        running ._test_some_elements() . . . pass
     53    """
     54
     55    @cached_method
     56    def super_categories(self):
     57        r"""
     58        EXAMPLES::
     59
     60            sage: ClassicalCrystals().super_categories()
     61            [Category of finite crystals, Category of highest weight crystals]
     62        """
     63        return [FiniteCrystals(), HighestWeightCrystals()]
     64
     65    def example(self, n = 3):
     66        """
     67        Returns an example of highest weight crystals, as per
     68        :meth:`Category.example`.
     69
     70        EXAMPLES::
     71
     72            sage: B = ClassicalCrystals().example(); B
     73            Highest weight crystal of type A_3 of highest weight omega_1
     74        """
     75        from sage.categories.crystals import Crystals
     76        return Crystals().example(n)
     77
     78    class ParentMethods:
     79
     80        def demazure_character(self, weight, reduced_word = False):
     81            r"""
     82            Returns the Demazure character associated to the specified
     83            weight in the ambient weight lattice.
     84
     85            INPUT:
     86
     87                - ``weight`` -- an element of the weight lattice
     88                  realization of the crystal, or a reduced word
     89                - ``reduced_word`` -- a boolean (default: ``False``)
     90                  whether ``weight`` is given as a reduced word
     91
     92            This is currently only supported for crystals whose
     93            underlying weight space is the ambient space.
     94
     95            EXAMPLES::
     96
     97                sage: T = CrystalOfTableaux(['A',2], shape = [2,1])
     98                sage: e = T.weight_lattice_realization().basis()
     99                sage: weight = e[0] + 2*e[2]
     100                sage: weight.reduced_word()
     101                [2, 1]
     102                sage: T.demazure_character(weight)
     103                x1^2*x2 + x1^2*x3 + x1*x2^2 + x1*x2*x3 + x1*x3^2
     104
     105                sage: T = CrystalOfTableaux(['A',3],shape=[2,1])
     106                sage: T.demazure_character([1,2,3], reduced_word = True)
     107                x1^2*x2 + x1^2*x3 + x1*x2^2 + x1*x2*x3 + x2^2*x3
     108
     109                sage: T = CrystalOfTableaux(['B',2], shape = [2])
     110                sage: e = T.weight_lattice_realization().basis()
     111                sage: weight = -2*e[1]
     112                sage: T.demazure_character(weight)
     113                x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1
     114
     115            TODO: detect automatically if weight is a reduced word,
     116            and remove the (untested!) ``reduced_word`` option.
     117
     118            REFERENCES::
     119
     120                .. [D1974] M. Demazure, Desingularisation des varietes de Schubert,
     121                   Ann. E. N. S., Vol. 6, (1974), p. 163-172
     122
     123                .. [M2009] Sarah Mason, An Explicit Construction of Type A Demazure Atoms,
     124                   Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313
     125                   (arXiv:0707.4267)
     126
     127            """
     128            from sage.misc.misc_c import prod
     129            from sage.rings.rational_field import QQ
     130            from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
     131            if reduced_word:
     132                word = weight
     133            else:
     134                word = weight.reduced_word()
     135            n = self.weight_lattice_realization().n
     136            u = list( self.module_generators )
     137            for i in reversed(word):
     138                u = u + sum((x.demazure_operator(i, truncated = True) for x in u), [])
     139            x = ['x%s'%i for i in range(1,n+1)]
     140            P = PolynomialRing(QQ, x)
     141            u = [b.weight() for b in u]
     142            return sum((prod((x[i]**(la[i]) for i in range(n)), P.one()) for la in u), P.zero())
     143
     144        def character(self, R):
     145            """
     146            Returns the character of the crystal.
     147
     148            INPUT:
     149
     150              - ``R`` -- a ``WeylCharacterRing``
     151
     152            EXAMPLES::
     153
     154                sage: C = CrystalOfLetters(['A',2])
     155                sage: T = TensorProductOfCrystals(C, C)
     156                sage: A2 = WeylCharacterRing(C.cartan_type()); A2
     157                The Weyl Character Ring of Type ['A', 2] with Integer Ring coefficients
     158                sage: chi = T.character(A2); chi
     159                A2(1,1,0) + A2(2,0,0)
     160                sage: chi.check(verbose = true)
     161                [9, 9]
     162
     163            TODO: add default value for ``R``
     164            """
     165            from sage.combinat.root_system.weyl_characters import WeylCharacter
     166            if not R.cartan_type() == self.cartan_type():
     167                raise ValueError, "ring does not have the right Cartan type"
     168            hlist = {}
     169            mlist = {}
     170
     171            for x in self.highest_weight_vectors():
     172                k = x.weight()
     173                if k in hlist:
     174                    hlist[k] += 1
     175                else:
     176                    hlist[k] = 1
     177            for x in self.list():
     178                k = x.weight()
     179                if k in mlist:
     180                    mlist[k] += 1
     181                else:
     182                    mlist[k] = 1
     183            return WeylCharacter(R, hlist, mlist)
     184
     185        def list(self):
     186            r"""
     187            Returns the list of the elements of ``self``, as per
     188            :meth:`FiniteEnumeratedSets.ParentMethods.list`
     189
     190            EXAMPLES::
     191
     192                sage: C = CrystalOfLetters(['D',4])
     193                sage: C.list()
     194                [1, 2, 3, 4, -4, -3, -2, -1]
     195
     196            FIXME: this is just there to reinstate the default
     197            implementation of :meth:`.list` from :meth:`.__iter__`
     198            which is overriden in :class:`Crystals`.
     199            """
     200            return self._list_from_iterator()
     201
     202        def __iter__(self):
     203            r"""
     204            Returns an iterator over the elements of this crystal.
     205
     206            This iterator uses little memory, storing only one element
     207            of the crystal at a time. For details on the complexity, see
     208            :class:`sage.combinat.crystals.crystals.CrystalBacktracker`.
     209
     210            EXAMPLES::
     211
     212                sage: C = CrystalOfLetters(['A',5])
     213                sage: [x for x in C]
     214                [1, 2, 3, 4, 5, 6]
     215
     216            TESTS::
     217
     218                sage: C = CrystalOfLetters(['D',4])
     219                sage: D = CrystalOfSpinsPlus(['D',4])
     220                sage: E = CrystalOfSpinsMinus(['D',4])
     221                sage: T=TensorProductOfCrystals(D,E,generators=[[D.list()[0],E.list()[0]]])
     222                sage: U=TensorProductOfCrystals(C,E,generators=[[C(1),E.list()[0]]])
     223                sage: T.cardinality()
     224                56
     225                sage: TestSuite(T).run(verbose = True)
     226                running ._test_an_element() . . . pass
     227                running ._test_category() . . . pass
     228                running ._test_elements() . . .
     229                  Running the test suite of self.an_element()
     230                  running ._test_category() . . . pass
     231                  running ._test_eq() . . . pass
     232                  running ._test_not_implemented_methods() . . . pass
     233                  running ._test_pickling() . . . pass
     234                  pass
     235                running ._test_elements_eq() . . . pass
     236                running ._test_enumerated_set_contains() . . . pass
     237                running ._test_enumerated_set_iter_cardinality() . . . pass
     238                running ._test_enumerated_set_iter_list() . . . pass
     239                running ._test_eq() . . . pass
     240                running ._test_fast_iter() . . . pass
     241                running ._test_not_implemented_methods() . . . pass
     242                running ._test_pickling() . . . pass
     243                running ._test_some_elements() . . . pass
     244                sage: TestSuite(U).run(verbose = True)
     245                running ._test_an_element() . . . pass
     246                running ._test_category() . . . pass
     247                running ._test_elements() . . .
     248                  Running the test suite of self.an_element()
     249                  running ._test_category() . . . pass
     250                  running ._test_eq() . . . pass
     251                  running ._test_not_implemented_methods() . . . pass
     252                  running ._test_pickling() . . . pass
     253                  pass
     254                running ._test_elements_eq() . . . pass
     255                running ._test_enumerated_set_contains() . . . pass
     256                running ._test_enumerated_set_iter_cardinality() . . . pass
     257                running ._test_enumerated_set_iter_list() . . . pass
     258                running ._test_eq() . . . pass
     259                running ._test_fast_iter() . . . pass
     260                running ._test_not_implemented_methods() . . . pass
     261                running ._test_pickling() . . . pass
     262                running ._test_some_elements() . . . pass
     263
     264            Bump's systematic tests::
     265
     266                sage: fa3 = lambda a,b,c: CrystalOfTableaux(['A',3],shape=[a+b+c,b+c,c])
     267                sage: fb3 = lambda a,b,c: CrystalOfTableaux(['B',3],shape=[a+b+c,b+c,c])
     268                sage: fc3 = lambda a,b,c: CrystalOfTableaux(['C',3],shape=[a+b+c,b+c,c])
     269                sage: fb4 = lambda a,b,c,d: CrystalOfTableaux(['B',4],shape=[a+b+c+d,b+c+d,c+d,d])
     270                sage: fd4 = lambda a,b,c,d: CrystalOfTableaux(['D',4],shape=[a+b+c+d,b+c+d,c+d,d])
     271                sage: fd5 = lambda a,b,c,d,e: CrystalOfTableaux(['D',5],shape=[a+b+c+d+e,b+c+d+e,c+d+e,d+e,e])
     272                sage: def fd4spinplus(a,b,c,d):\
     273                     C = CrystalOfTableaux(['D',4],shape=[a+b+c+d,b+c+d,c+d,d]);\
     274                     D = CrystalOfSpinsPlus(['D',4]);\
     275                     return TensorProductOfCrystals(C,D,generators=[[C[0],D[0]]])
     276                sage: def fb3spin(a,b,c):\
     277                     C = CrystalOfTableaux(['B',3],shape=[a+b+c,b+c,c]);\
     278                     D = CrystalOfSpins(['B',3]);\
     279                     return TensorProductOfCrystals(C,D,generators=[[C[0],D[0]]])
     280
     281            TODO: choose a good panel of values for a,b,c ... both for
     282            basic systematic tests and for conditionally run,
     283            computationally involved tests.
     284
     285            ::
     286
     287                sage: TestSuite(fb4(1,0,1,0)).run(verbose = True)
     288                running ._test_an_element() . . . pass
     289                running ._test_category() . . . pass
     290                running ._test_elements() . . .
     291                  Running the test suite of self.an_element()
     292                  running ._test_category() . . . pass
     293                  running ._test_eq() . . . pass
     294                  running ._test_not_implemented_methods() . . . pass
     295                  running ._test_pickling() . . . pass
     296                  pass
     297                running ._test_elements_eq() . . . pass
     298                running ._test_enumerated_set_contains() . . . pass
     299                running ._test_enumerated_set_iter_cardinality() . . . pass
     300                running ._test_enumerated_set_iter_list() . . .Enumerated set too big; skipping test; see ``self.max_test_enumerated_set_loop``
     301                pass
     302                running ._test_eq() . . . pass
     303                running ._test_fast_iter() . . . pass
     304                running ._test_not_implemented_methods() . . . pass
     305                running ._test_pickling() . . . pass
     306                running ._test_some_elements() . . . pass
     307
     308            ::
     309
     310                #sage: fb4(1,1,1,1).check() # expensive: the crystal is of size 297297
     311                #True
     312            """
     313            from sage.combinat.crystals.crystals import CrystalBacktracker
     314            return iter(CrystalBacktracker(self))
     315
     316        def _test_fast_iter(self, **options):
     317            r"""
     318            Tests whether the elements returned by :meth:`.__iter__`
     319            and ``Crystal.list(self)`` are the same (the two
     320            algorithms are different).
     321
     322            EXAMPLES::
     323
     324                sage: C = CrystalOfLetters(['A', 5])
     325                sage: C._test_fast_iter()
     326            """
     327            tester = self._tester(**options)
     328            S = self._list_brute_force()
     329            SS = set(S)
     330            tester.assert_( len(S) == len(SS) )
     331            tester.assert_( set(self) == set(SS) )
     332
     333        def cardinality(self):
     334            r"""
     335            Returns the number of elements of the crystal, using Weyl's
     336            dimension formula on each connected component.
     337
     338            EXAMPLES::
     339
     340                sage: C = ClassicalCrystals().example(5)
     341                sage: C.cardinality()
     342                6
     343            """
     344            return sum(self.weight_lattice_realization().weyl_dimension(x.weight())
     345                       for x in self.highest_weight_vectors())
  • new file sage/categories/crystals.py

    diff --git a/sage/categories/crystals.py b/sage/categories/crystals.py
    new file mode 100644
    - +  
     1r"""
     2Crystals
     3"""
     4#*****************************************************************************
     5#  Copyright (C) 2010    Anne Schilling <anne at math.ucdavis.edu>
     6#
     7#  Distributed under the terms of the GNU General Public License (GPL)
     8#                  http://www.gnu.org/licenses/
     9#******************************************************************************
     10
     11from sage.misc.cachefunc import cached_method
     12from sage.misc.cachefunc import CachedFunction
     13from sage.misc.abstract_method import abstract_method
     14from sage.categories.category import Category
     15from sage.categories.enumerated_sets import EnumeratedSets
     16from sage.misc.latex import latex
     17from sage.combinat import ranker
     18
     19class Crystals(Category):
     20    """
     21    The category of crystals
     22
     23    See :mod:`sage.combinat.crystals` for an introduction to crystals.
     24
     25    EXAMPLES::
     26
     27        sage: C = Crystals()
     28        sage: C
     29        Category of crystals
     30        sage: C.super_categories()
     31        [Category of... enumerated sets]
     32        sage: C.example()
     33        Highest weight crystal of type A_3 of highest weight omega_1
     34
     35    Parents in this category should implement the following methods:
     36
     37     - either a method ``cartan_type`` or an attribute ``_cartan_type``
     38
     39     - ``module_generators``: a list (or container) of distinct elements
     40       which generate the crystal using `f_i`
     41
     42    Furthermore, their elements should implement the following methods:
     43
     44     - x.e(i) (returning `e_i(x)`)
     45
     46     - x.f(i) (returning `f_i(x)`)
     47
     48    EXAMPLES::
     49
     50        sage: from sage.misc.abstract_method import abstract_methods_of_class
     51        sage: abstract_methods_of_class(Crystals().element_class)
     52        {'required': ['e', 'f'], 'optional': []}
     53
     54    TESTS::
     55
     56        sage: TestSuite(C).run()
     57        sage: B = Crystals().example()
     58        sage: TestSuite(B).run(verbose = True)
     59        running ._test_an_element() . . . pass
     60        running ._test_category() . . . pass
     61        running ._test_elements() . . .
     62          Running the test suite of self.an_element()
     63          running ._test_category() . . . pass
     64          running ._test_eq() . . . pass
     65          running ._test_not_implemented_methods() . . . pass
     66          running ._test_pickling() . . . pass
     67          pass
     68        running ._test_elements_eq() . . . pass
     69        running ._test_enumerated_set_contains() . . . pass
     70        running ._test_enumerated_set_iter_cardinality() . . . pass
     71        running ._test_enumerated_set_iter_list() . . . pass
     72        running ._test_eq() . . . pass
     73        running ._test_fast_iter() . . . pass
     74        running ._test_not_implemented_methods() . . . pass
     75        running ._test_pickling() . . . pass
     76        running ._test_some_elements() . . . pass
     77    """
     78
     79    @cached_method
     80    def super_categories(self):
     81        r"""
     82        EXAMPLES::
     83
     84            sage: Crystals().super_categories()
     85            [Category of enumerated sets]
     86        """
     87        return [EnumeratedSets()]
     88
     89    class ParentMethods:
     90
     91        #TODO: implement tests for the TestSuite to check whether the crystal satisfies the Stembridge rules
     92
     93        def _an_element_(self):
     94            """
     95            Returns an element of ``self``
     96
     97                sage: C = CrystalOfLetters(['A', 5])
     98                sage: C.an_element()
     99                1
     100            """
     101            return self.first()
     102
     103        def weight_lattice_realization(self):
     104            """
     105            Returns the weight lattice realization for the root system
     106            associated to ``self``. This default implementation uses
     107            the ambient space of the root system.
     108
     109            EXAMPLES::
     110
     111                sage: C = CrystalOfLetters(['A', 5])
     112                sage: C.weight_lattice_realization()
     113                Ambient space of the Root system of type ['A', 5]
     114                sage: K = KirillovReshetikhinCrystal(['A',2,1], 1, 1)
     115                sage: K.weight_lattice_realization()
     116                Weight lattice of the Root system of type ['A', 2, 1]
     117            """
     118            F = self.cartan_type().root_system()
     119            if F.ambient_space() is None:
     120                return F.weight_lattice()
     121            else:
     122                return F.ambient_space()
     123
     124        def cartan_type(self):
     125            """
     126            Returns the Cartan type of the crystal
     127
     128            EXAMPLES::
     129                sage: C = CrystalOfLetters(['A',2])
     130                sage: C.cartan_type()
     131                ['A', 2]
     132            """
     133            return self._cartan_type
     134
     135        def index_set(self):
     136            """
     137            Returns the index set of the Dynkin diagram underlying the crystal
     138
     139            EXAMPLES:
     140                sage: C = CrystalOfLetters(['A', 5])
     141                sage: C.index_set()
     142                [1, 2, 3, 4, 5]
     143            """
     144            return self.cartan_type().index_set()
     145
     146        def Lambda(self):
     147            """
     148            Returns the fundamentals weights in the weight lattice
     149            realization for the root system associated the crystal
     150
     151            EXAMPLES::
     152
     153                sage: C = CrystalOfLetters(['A', 5])
     154                sage: C.Lambda()
     155                Finite family {1: (1, 0, 0, 0, 0, 0), 2: (1, 1, 0, 0, 0, 0), 3: (1, 1, 1, 0, 0, 0), 4: (1, 1, 1, 1, 0, 0), 5: (1, 1, 1, 1, 1, 0)}
     156            """
     157            return self.weight_lattice_realization().fundamental_weights()
     158
     159        def crystal_morphism(self, g, index_set = None, automorphism = lambda i : i, direction = 'down', direction_image = 'down',
     160                             similarity_factor = None, similarity_factor_domain = None, cached = False, acyclic = True):
     161            """
     162            Constructs a morphism from the crystal ``self`` to another crystal.
     163            The input `g` can either be a function of a (sub)set of elements of self to
     164            element in another crystal or a dictionary between certain elements.
     165            Usually one would map highest weight elements or crystal generators to each
     166            other using g.
     167            Specifying index_set gives the opportunity to define the morphism as `I`-crystals
     168            where `I =` index_set. If index_set is not specified, the index set of self is used.
     169            It is also possible to define twisted-morphisms by specifying an automorphism on the
     170            nodes in te Dynkin diagram (or the index_set).
     171            The option direction and direction_image indicate whether to use `f_i` or `e_i` in
     172            self or the image crystal to construct the morphism, depending on whether the direction
     173            is set to 'down' or 'up'.
     174            It is also possible to set a similarity_factor. This should be a dictionary between
     175            the elements in the index set and positive integers. The crystal operator `f_i` then gets
     176            mapped to `f_i^{m_i}` where `m_i =` similarity_factor[i].
     177            Setting similarity_factor_domain to a dictionary between the index set and positive integers
     178            has the effect that `f_i^{m_i}` gets mapped to `f_i` where `m_i =` similarity_factor_domain[i].
     179            Finally, it is possible to set the option `acyclic = False`. This calculates an isomorphism
     180            for cyclic crystals (for example finite affine crystals). In this case the input function `g`
     181            is supposed to be given as a dictionary.
     182
     183            EXAMPLES::
     184
     185                sage: C2 = CrystalOfLetters(['A',2])
     186                sage: C3 = CrystalOfLetters(['A',3])
     187                sage: g = {C2.module_generators[0] : C3.module_generators[0]}
     188                sage: g_full = C2.crystal_morphism(g)
     189                sage: g_full(C2(1))
     190                1
     191                sage: g_full(C2(2))
     192                2
     193                sage: g = {C2(1) : C2(3)}
     194                sage: g_full = C2.crystal_morphism(g, automorphism = lambda i : 3-i, direction_image = 'up')
     195                sage: [g_full(b) for b in C2]
     196                [3, 2, 1]
     197                sage: T = CrystalOfTableaux(['A',2], shape = [2])
     198                sage: g = {C2(1) : T(rows=[[1,1]])}
     199                sage: g_full = C2.crystal_morphism(g, similarity_factor = {1:2, 2:2})
     200                sage: [g_full(b) for b in C2]
     201                [[[1, 1]], [[2, 2]], [[3, 3]]]
     202                sage: g = {T(rows=[[1,1]]) : C2(1)}
     203                sage: g_full = T.crystal_morphism(g, similarity_factor_domain = {1:2, 2:2})
     204                sage: g_full(T(rows=[[2,2]]))
     205                2
     206
     207                sage: B1=KirillovReshetikhinCrystal(['A',2,1],1,1)
     208                sage: B2=KirillovReshetikhinCrystal(['A',2,1],1,2)
     209                sage: T=TensorProductOfCrystals(B1,B2)
     210                sage: T1=TensorProductOfCrystals(B2,B1)
     211                sage: La = T.weight_lattice_realization().fundamental_weights()
     212                sage: t = [b for b in T if b.weight() == -3*La[0] + 3*La[1]][0]
     213                sage: t1 = [b for b in T1 if b.weight() == -3*La[0] + 3*La[1]][0]
     214                sage: g={t:t1}
     215                sage: f=T.crystal_morphism(g,acyclic = False)
     216                sage: [[b,f(b)] for b in T]
     217                [[[[[1]], [[1, 1]]], [[[1, 1]], [[1]]]],
     218                [[[[1]], [[1, 2]]], [[[1, 1]], [[2]]]],
     219                [[[[1]], [[2, 2]]], [[[1, 2]], [[2]]]],
     220                [[[[1]], [[1, 3]]], [[[1, 1]], [[3]]]],
     221                [[[[1]], [[2, 3]]], [[[1, 2]], [[3]]]],
     222                [[[[1]], [[3, 3]]], [[[1, 3]], [[3]]]],
     223                [[[[2]], [[1, 1]]], [[[1, 2]], [[1]]]],
     224                [[[[2]], [[1, 2]]], [[[2, 2]], [[1]]]],
     225                [[[[2]], [[2, 2]]], [[[2, 2]], [[2]]]],
     226                [[[[2]], [[1, 3]]], [[[2, 3]], [[1]]]],
     227                [[[[2]], [[2, 3]]], [[[2, 2]], [[3]]]],
     228                [[[[2]], [[3, 3]]], [[[2, 3]], [[3]]]],
     229                [[[[3]], [[1, 1]]], [[[1, 3]], [[1]]]],
     230                [[[[3]], [[1, 2]]], [[[1, 3]], [[2]]]],
     231                [[[[3]], [[2, 2]]], [[[2, 3]], [[2]]]],
     232                [[[[3]], [[1, 3]]], [[[3, 3]], [[1]]]],
     233                [[[[3]], [[2, 3]]], [[[3, 3]], [[2]]]],
     234                [[[[3]], [[3, 3]]], [[[3, 3]], [[3]]]]]
     235            """
     236            if index_set is None:
     237                index_set = self.index_set()
     238            if similarity_factor is None:
     239                similarity_factor = dict( (i,1) for i in index_set )
     240            if similarity_factor_domain is None:
     241                similarity_factor_domain = dict( (i,1) for i in index_set )
     242            if direction == 'down':
     243                e_string = 'e_string'
     244            else:
     245                e_string = 'f_string'
     246            if direction_image == 'down':
     247                f_string = 'f_string'
     248            else:
     249                f_string = 'e_string'
     250
     251            if acyclic:
     252                if type(g) == dict:
     253                    g = g.__getitem__
     254
     255                def morphism(b):
     256                    for i in index_set:
     257                        c = getattr(b, e_string)([i for k in range(similarity_factor_domain[i])])
     258                        if c is not None:
     259                            d = getattr(morphism(c), f_string)([automorphism(i) for k in range(similarity_factor[i])])
     260                            if d is not None:
     261                                return d
     262                            else:
     263                                raise ValueError, "This is not a morphism!"
     264                            #now we know that b is hw
     265                    return g(b)
     266
     267            else:
     268                import copy
     269                morphism = copy.copy(g)
     270                known = set( g.keys() )
     271                todo = copy.copy(known)
     272                images = set( [g[x] for x in known] )
     273                # Invariants:
     274                # - images contains all known morphism(x)
     275                # - known contains all elements x for which we know morphism(x)
     276                # - todo  contains all elements x for which we haven't propagated to each child
     277                while todo <> set( [] ):
     278                    x = todo.pop()
     279                    for i in index_set:
     280                        eix  = getattr(x, f_string)([i for k in range(similarity_factor_domain[i])])
     281                        eigx = getattr(morphism[x], f_string)([automorphism(i) for k in range(similarity_factor[i])])
     282                        if bool(eix is None) <> bool(eigx is None):
     283                            # This is not a crystal morphism!
     284                            raise ValueError, "This is not a morphism!" #, print("x="x,"g(x)="g(x),"i="i)
     285                        if (eix is not None) and (eix not in known):
     286                            todo.add(eix)
     287                            known.add(eix)
     288                            morphism[eix] = eigx
     289                            images.add(eigx)
     290                # Check that the module generators are indeed module generators
     291                assert(len(known) == self.cardinality())
     292                # We may not want to systematically run those tests,
     293                # to allow for non bijective crystal morphism
     294                # Add an option CheckBijective?
     295                if not ( len(known) == len(images) and len(images) == images.pop().parent().cardinality() ):
     296                    return(None)
     297                return morphism.__getitem__
     298
     299            if cached:
     300                return morphism
     301            else:
     302                return CachedFunction(morphism)
     303
     304        def digraph(self):
     305            """
     306            Returns the DiGraph associated to self.
     307
     308            EXAMPLES::
     309
     310                sage: C = Crystals().example(5)
     311                sage: C.digraph()
     312                Digraph on 6 vertices
     313
     314            TODO: add more tests
     315            """
     316            from sage.graphs.all import DiGraph
     317            d = {}
     318            for x in self:
     319                d[x] = {}
     320                for i in self.index_set():
     321                    child = x.f(i)
     322                    if child is None:
     323                        continue
     324                    d[x][child]=i
     325            return DiGraph(d)
     326
     327        def latex_file(self, filename):
     328            r"""
     329            Exports a file, suitable for pdflatex, to 'filename'. This requires
     330            a proper installation of ``dot2tex`` in sage-python. For more
     331            information see the documentation for ``self.latex()``.
     332
     333            EXAMPLES::
     334
     335                sage: C = CrystalOfLetters(['A', 5])
     336                sage: C.latex_file('/tmp/test.tex') #optional requires dot2tex
     337            """
     338            header = r"""\documentclass{article}
     339            \usepackage[x11names, rgb]{xcolor}
     340            \usepackage[utf8]{inputenc}
     341            \usepackage{tikz}
     342            \usetikzlibrary{snakes,arrows,shapes}
     343            \usepackage{amsmath}
     344            \usepackage[active,tightpage]{preview}
     345            \newenvironment{bla}{}{}
     346            \PreviewEnvironment{bla}
     347
     348            \begin{document}
     349            \begin{bla}"""
     350
     351            footer = r"""\end{bla}
     352            \end{document}"""
     353
     354            f = open(filename, 'w')
     355            f.write(header + self.latex() + footer)
     356            f.close()
     357
     358        def latex(self):
     359            r"""
     360            Returns the crystal graph as a latex string. This can be exported
     361            to a file with self.latex_file('filename').
     362
     363            See :meth:`Graph.layout_graphviz` for further documentation on which
     364            packages are required.
     365
     366            EXAMPLES::
     367
     368                sage: C = CrystalOfLetters(['A', 5])
     369                sage: C.latex()         #optional requires dot2tex
     370                ...
     371                sage: view(C, pdflatex = True, tightpage = True) # optional
     372            """
     373            try:
     374                from dot2tex.dot2tex import Dot2TikZConv
     375            except ImportError:
     376                print "dot2tex not available.  Install after running \'sage -sh\'"
     377                return
     378
     379            # In MuPAD, 'autosize' is an option, but this doesn't seem to work here.
     380            options = {'format':'tikz', 'crop':True, 'usepdflatex':True, 'figonly':True}
     381
     382            content = (Dot2TikZConv(options)).convert(self.dot_tex())
     383
     384            return content
     385
     386        _latex_ = latex
     387
     388        def metapost(self, filename, thicklines=False, labels=True, scaling_factor=1.0, tallness=1.0):
     389            r"""
     390            Use C.metapost("filename.mp",[options]), where options can be:
     391
     392            thicklines = True (for thicker edges) labels = False (to suppress
     393            labeling of the vertices) scaling_factor=value, where value is a
     394            floating point number, 1.0 by default. Increasing or decreasing the
     395            scaling factor changes the size of the image. tallness=1.0.
     396            Increasing makes the image taller without increasing the width.
     397
     398            Root operators e(1) or f(1) move along red lines, e(2) or f(2)
     399            along green. The highest weight is in the lower left. Vertices with
     400            the same weight are kept close together. The concise labels on the
     401            nodes are strings introduced by Berenstein and Zelevinsky and
     402            Littelmann; see Littelmann's paper Cones, Crystals, Patterns,
     403            sections 5 and 6.
     404
     405            For Cartan types B2 or C2, the pattern has the form
     406
     407            a2 a3 a4 a1
     408
     409            where c\*a2 = a3 = 2\*a4 =0 and a1=0, with c=2 for B2, c=1 for C2.
     410            Applying e(2) a1 times, e(1) a2 times, e(2) a3 times, e(1) a4 times
     411            returns to the highest weight. (Observe that Littelmann writes the
     412            roots in opposite of the usual order, so our e(1) is his e(2) for
     413            these Cartan types.) For type A2, the pattern has the form
     414
     415            a3 a2 a1
     416
     417            where applying e(1) a1 times, e(2) a2 times then e(3) a1 times
     418            returns to the highest weight. These data determine the vertex and
     419            may be translated into a Gelfand-Tsetlin pattern or tableau.
     420
     421            EXAMPLES::
     422
     423                sage: C = CrystalOfLetters(['A', 2])
     424                sage: C.metapost('/tmp/test.mp') #optional
     425
     426            ::
     427
     428                sage: C = CrystalOfLetters(['A', 5])
     429                sage: C.metapost('/tmp/test.mp')
     430                Traceback (most recent call last):
     431                ...
     432                NotImplementedError
     433            """
     434            # FIXME: those tests are not robust
     435            # Should use instead self.cartan_type() == CartanType(['B',2])
     436            if self.cartan_type()[0] == 'B' and self.cartan_type()[1] == 2:
     437                word = [2,1,2,1]
     438            elif self.cartan_type()[0] == 'C' and self.cartan_type()[1] == 2:
     439                word = [2,1,2,1]
     440            elif self.cartan_type()[0] == 'A' and self.cartan_type()[1] == 2:
     441                word = [1,2,1]
     442            else:
     443                raise NotImplementedError
     444            size = self.cardinality()
     445            string_data = []
     446            for i in range(size):
     447                turtle = self.list()[i]
     448                string_datum = []
     449                for j in word:
     450                    turtlewalk = 0
     451                    while not turtle.e(j) == None:
     452                        turtle = turtle.e(j)
     453                        turtlewalk += 1
     454                    string_datum.append(turtlewalk)
     455                string_data.append(string_datum)
     456
     457            if self.cartan_type()[0] == 'A':
     458                if labels:
     459                    c0 = int(55*scaling_factor)
     460                    c1 = int(-25*scaling_factor)
     461                    c2 = int(45*tallness*scaling_factor)
     462                    c3 = int(-12*scaling_factor)
     463                    c4 = int(-12*scaling_factor)
     464                else:
     465                    c0 = int(45*scaling_factor)
     466                    c1 = int(-20*scaling_factor)
     467                    c2 = int(35*tallness*scaling_factor)
     468                    c3 = int(12*scaling_factor)
     469                    c4 = int(-12*scaling_factor)
     470                outstring = "verbatimtex\n\\magnification=600\netex\n\nbeginfig(-1);\nsx:=35; sy:=30;\n\nz1000=(%d,0);\nz1001=(%d,%d);\nz1002=(%d,%d);\nz2001=(-3,3);\nz2002=(3,3);\nz2003=(0,-3);\nz2004=(7,0);\nz2005=(0,7);\nz2006=(-7,0);\nz2007=(0,7);\n\n"%(c0,c1,c2,c3,c4)
     471            else:
     472                if labels:
     473                    outstring = "verbatimtex\n\\magnification=600\netex\n\nbeginfig(-1);\n\nsx := %d;\nsy=%d;\n\nz1000=(2*sx,0);\nz1001=(-sx,sy);\nz1002=(-16,-10);\n\nz2001=(0,-3);\nz2002=(-5,3);\nz2003=(0,3);\nz2004=(5,3);\nz2005=(10,1);\nz2006=(0,10);\nz2007=(-10,1);\nz2008=(0,-8);\n\n"%(int(scaling_factor*40),int(tallness*scaling_factor*40))
     474                else:
     475                    outstring = "beginfig(-1);\n\nsx := %d;\nsy := %d;\n\nz1000=(2*sx,0);\nz1001=(-sx,sy);\nz1002=(-5,-5);\n\nz1003=(10,10);\n\n"%(int(scaling_factor*35),int(tallness*scaling_factor*35))
     476            for i in range(size):
     477                if self.cartan_type()[0] == 'A':
     478                    [a1,a2,a3] = string_data[i]
     479                else:
     480                    [a1,a2,a3,a4] = string_data[i]
     481                shift = 0
     482                for j in range(i):
     483                    if self.cartan_type()[0] == 'A':
     484                        [b1,b2,b3] = string_data[j]
     485                        if b1+b3 == a1+a3 and b2 == a2:
     486                            shift += 1
     487                    else:
     488                        [b1,b2,b3,b4] = string_data[j]
     489                        if b1+b3 == a1+a3 and b2+b4 == a2+a4:
     490                            shift += 1
     491                if self.cartan_type()[0] == 'A':
     492                    outstring = outstring +"z%d=%d*z1000+%d*z1001+%d*z1002;\n"%(i,a1+a3,a2,shift)
     493                else:
     494                    outstring = outstring +"z%d=%d*z1000+%d*z1001+%d*z1002;\n"%(i,a1+a3,a2+a4,shift)
     495            outstring = outstring + "\n"
     496            if thicklines:
     497                outstring = outstring +"pickup pencircle scaled 2\n\n"
     498            for i in range(size):
     499                for j in range(1,3):
     500                    dest = self.list()[i].f(j)
     501                    if not dest == None:
     502                        dest = self.list().index(dest)
     503                        if j == 1:
     504                            col = "red;"
     505                        else:
     506                            col = "green;  "
     507                        if self.cartan_type()[0] == 'A':
     508                            [a1,a2,a3] = string_data[i] # included to facilitate hand editing of the .mp file
     509                            outstring = outstring+"draw z%d--z%d withcolor %s   %% %d %d %d\n"%(i,dest,col,a1,a2,a3)
     510                        else:
     511                            [a1,a2,a3,a4] = string_data[i]
     512                            outstring = outstring+"draw z%d--z%d withcolor %s   %% %d %d %d %d\n"%(i,dest,col,a1,a2,a3,a4)
     513            outstring += "\npickup pencircle scaled 3;\n\n"
     514            for i in range(self.cardinality()):
     515                if labels:
     516                    if self.cartan_type()[0] == 'A':
     517                        outstring = outstring+"pickup pencircle scaled 15;\nfill z%d+z2004..z%d+z2006..z%d+z2006..z%d+z2007..cycle withcolor white;\nlabel(btex %d etex, z%d+z2001);\nlabel(btex %d etex, z%d+z2002);\nlabel(btex %d etex, z%d+z2003);\npickup pencircle scaled .5;\ndraw z%d+z2004..z%d+z2006..z%d+z2006..z%d+z2007..cycle;\n"%(i,i,i,i,string_data[i][2],i,string_data[i][1],i,string_data[i][0],i,i,i,i,i)
     518                    else:
     519                        outstring = outstring+"%%%d %d %d %d\npickup pencircle scaled 1;\nfill z%d+z2005..z%d+z2006..z%d+z2007..z%d+z2008..cycle withcolor white;\nlabel(btex %d etex, z%d+z2001);\nlabel(btex %d etex, z%d+z2002);\nlabel(btex %d etex, z%d+z2003);\nlabel(btex %d etex, z%d+z2004);\npickup pencircle scaled .5;\ndraw z%d+z2005..z%d+z2006..z%d+z2007..z%d+z2008..cycle;\n\n"%(string_data[i][0],string_data[i][1],string_data[i][2],string_data[i][3],i,i,i,i,string_data[i][0],i,string_data[i][1],i,string_data[i][2],i,string_data[i][3],i,i,i,i,i)
     520                else:
     521                    outstring += "drawdot z%d;\n"%i
     522            outstring += "\nendfig;\n\nend;\n\n"
     523
     524            f = open(filename, 'w')
     525            f.write(outstring)
     526            f.close()
     527
     528        def dot_tex(self):
     529            r"""
     530            Returns a dot_tex string representation of ``self``.
     531
     532            EXAMPLES::
     533
     534                sage: C = CrystalOfLetters(['A',2])
     535                sage: C.dot_tex()
     536                'digraph G { \n  node [ shape=plaintext ];\n  N_0 [ label = " ", texlbl = "$1$" ];\n  N_1 [ label = " ", texlbl = "$2$" ];\n  N_2 [ label = " ", texlbl = "$3$" ];\n  N_0 -> N_1 [ label = " ", texlbl = "1" ];\n  N_1 -> N_2 [ label = " ", texlbl = "2" ];\n}'
     537            """
     538            import re
     539            rank = ranker.from_list(self.list())[0]
     540            vertex_key = lambda x: "N_"+str(rank(x))
     541
     542            # To do: check the regular expression
     543            # Removing %-style comments, newlines, quotes
     544            # This should probably be moved to sage.misc.latex
     545            quoted_latex = lambda x: re.sub("\"|\r|(%[^\n]*)?\n","", latex(x))
     546
     547            result = "digraph G { \n  node [ shape=plaintext ];\n"
     548
     549            for x in self:
     550                result += "  " + vertex_key(x) + " [ label = \" \", texlbl = \"$"+quoted_latex(x)+"$\" ];\n"
     551            for x in self:
     552                for i in self.index_set():
     553                    child = x.f(i)
     554                    if child is None:
     555                        continue
     556    #                result += "  " + vertex_key(x) + " -> "+vertex_key(child)+ " [ label = \" \", texlbl = \""+quoted_latex(i)+"\" ];\n"
     557                    if i == 0:
     558                        option = "dir = back, "
     559                        (source, target) = (child, x)
     560                    else:
     561                        option = ""
     562                        (source, target) = (x, child)
     563                    result += "  " + vertex_key(source) + " -> "+vertex_key(target)+ " [ "+option+"label = \" \", texlbl = \""+quoted_latex(i)+"\" ];\n"
     564            result+="}"
     565            return result
     566
     567        def plot(self, **options):
     568            """
     569            Returns the plot of self as a directed graph.
     570
     571            EXAMPLES::
     572
     573                sage: C = CrystalOfLetters(['A', 5])
     574                sage: show_default(False) #do not show the plot by default
     575                sage: C.plot()
     576                Graphics object consisting of 17 graphics primitives
     577            """
     578            return self.digraph().plot(edge_labels=True,vertex_size=0,**options)
     579
     580    class ElementMethods:
     581
     582        def index_set(self):
     583            """
     584            EXAMPLES::
     585
     586                sage: C = CrystalOfLetters(['A',5])
     587                sage: C(1).index_set()
     588                [1, 2, 3, 4, 5]
     589            """
     590            return self.parent().index_set()
     591
     592        def cartan_type(self):
     593            """
     594            Returns the cartan type associated to ``self``
     595
     596            EXAMPLES::
     597
     598                sage: C = CrystalOfLetters(['A', 5])
     599                sage: C(1).cartan_type()
     600                ['A', 5]
     601            """
     602            return self.parent().cartan_type()
     603
     604        def weight(self):
     605            """
     606            Returns the weight of this crystal element
     607
     608            EXAMPLES::
     609
     610                sage: C = CrystalOfLetters(['A',5])
     611                sage: C(1).weight()
     612                (1, 0, 0, 0, 0, 0)
     613            """
     614            return self.Phi() - self.Epsilon()
     615
     616        @abstract_method
     617        def e(self, i):
     618            r"""
     619            Returns `e_i(x)` if it exists or ``None`` otherwise.
     620
     621            This method should be implemented by the element class of
     622            the crystal.
     623
     624            EXAMPLES::
     625
     626                sage: C = Crystals().example(5)
     627                sage: x = C[2]; x
     628                3
     629                sage: x.e(1), x.e(2), x.e(3)
     630                (None, 2, None)
     631            """
     632
     633        @abstract_method
     634        def f(self, i):
     635            r"""
     636            Returns `f_i(x)` if it exists or ``None`` otherwise.
     637
     638            This method should be implemented by the element class of
     639            the crystal.
     640
     641            EXAMPLES::
     642
     643                sage: C = Crystals().example(5)
     644                sage: x = C[1]; x
     645                2
     646                sage: x.f(1), x.f(2), x.f(3)
     647                (None, 3, None)
     648            """
     649
     650        def epsilon(self, i):
     651            r"""
     652            EXAMPLES::
     653
     654                sage: C = CrystalOfLetters(['A',5])
     655                sage: C(1).epsilon(1)
     656                0
     657                sage: C(2).epsilon(1)
     658                1
     659            """
     660            assert i in self.index_set()
     661            x = self
     662            eps = 0
     663            while True:
     664                x = x.e(i)
     665                if x is None:
     666                    break
     667                eps = eps+1
     668            return eps
     669
     670        def phi(self, i):
     671            r"""
     672            EXAMPLES::
     673
     674                sage: C = CrystalOfLetters(['A',5])
     675                sage: C(1).phi(1)
     676                1
     677                sage: C(2).phi(1)
     678                0
     679            """
     680            assert i in self.index_set()
     681            x = self
     682            phi = 0
     683            while True:
     684                x = x.f(i)
     685                if x is None:
     686                    break
     687                phi = phi+1
     688            return phi
     689
     690        def phi_minus_epsilon(self, i):
     691            """
     692            Returns `\phi_i - \epsilon_i` of self. There are sometimes
     693            better implementations using the weight for this. It is used
     694            for reflections along a string.
     695
     696            EXAMPLES::
     697
     698                sage: C = CrystalOfLetters(['A',5])
     699                sage: C(1).phi_minus_epsilon(1)
     700                1
     701            """
     702            return self.phi(i) - self.epsilon(i)
     703
     704        def Epsilon(self):
     705            """
     706            EXAMPLES::
     707
     708                sage: C = CrystalOfLetters(['A',5])
     709                sage: C(0).Epsilon()
     710                (0, 0, 0, 0, 0, 0)
     711                sage: C(1).Epsilon()
     712                (0, 0, 0, 0, 0, 0)
     713                sage: C(2).Epsilon()
     714                (1, 0, 0, 0, 0, 0)
     715            """
     716            Lambda = self.parent().Lambda()
     717            return sum(self.epsilon(i) * Lambda[i] for i in self.index_set())
     718
     719        def Phi(self):
     720            """
     721            EXAMPLES::
     722
     723                sage: C = CrystalOfLetters(['A',5])
     724                sage: C(0).Phi()
     725                (0, 0, 0, 0, 0, 0)
     726                sage: C(1).Phi()
     727                (1, 0, 0, 0, 0, 0)
     728                sage: C(2).Phi()
     729                (1, 1, 0, 0, 0, 0)
     730            """
     731            Lambda = self.parent().Lambda()
     732            return sum(self.phi(i) * Lambda[i] for i in self.index_set())
     733
     734        def f_string(self, list):
     735            r"""
     736            Applies `f_{i_r} ... f_{i_1}` to self for `list = [i_1, ..., i_r]`
     737
     738            EXAMPLES::
     739
     740                sage: C = CrystalOfLetters(['A',3])
     741                sage: b = C(1)
     742                sage: b.f_string([1,2])
     743                3
     744                sage: b.f_string([2,1])
     745
     746            """
     747            b = self
     748            for i in list:
     749                b = b.f(i)
     750                if b is None:
     751                    return None
     752            return b
     753
     754        def e_string(self, list):
     755            r"""
     756            Applies `e_{i_r} ... e_{i_1}` to self for `list = [i_1, ..., i_r]`
     757
     758            EXAMPLES::
     759
     760                sage: C = CrystalOfLetters(['A',3])
     761                sage: b = C(3)
     762                sage: b.e_string([2,1])
     763                1
     764                sage: b.e_string([1,2])
     765
     766            """
     767            b = self
     768            for i in list:
     769                b = b.e(i)
     770                if b is None:
     771                    return None
     772            return b
     773
     774        def s(self, i):
     775            r"""
     776            Returns the reflection of ``self`` along its `i`-string
     777
     778            EXAMPLES::
     779
     780                sage: C = CrystalOfTableaux(['A',2], shape=[2,1])
     781                sage: b=C(rows=[[1,1],[3]])
     782                sage: b.s(1)
     783                [[2, 2], [3]]
     784                sage: b=C(rows=[[1,2],[3]])
     785                sage: b.s(2)
     786                [[1, 2], [3]]
     787                sage: T=CrystalOfTableaux(['A',2],shape=[4])
     788                sage: t=T(rows=[[1,2,2,2]])
     789                sage: t.s(1)
     790                [[1, 1, 1, 2]]
     791            """
     792            d = self.phi_minus_epsilon(i)
     793            b = self
     794            if d > 0:
     795                for j in range(d):
     796                    b = b.f(i)
     797            else:
     798                for j in range(-d):
     799                    b = b.e(i)
     800            return b
     801
     802        def demazure_operator(self, i, truncated = False):
     803            r"""
     804            Returns the list of the elements one can obtain from
     805            ``self`` by application of `f_i`.  If the option
     806            "truncated" is set to True, then ``self`` is not included
     807            in the list.
     808
     809            REFERENCES:
     810
     811                .. [L1995] Peter Littelmann, Crystal graphs and Young tableaux,
     812                   J. Algebra 175 (1995), no. 1, 65--87.
     813
     814                .. [K1993] Masaki Kashiwara, The crystal base and Littelmann's refined Demazure character formula,
     815                   Duke Math. J. 71 (1993), no. 3, 839--858.
     816
     817            EXAMPLES::
     818
     819                sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
     820                sage: t=T(rows=[[1,2],[2]])
     821                sage: t.demazure_operator(2)
     822                [[[1, 2], [2]], [[1, 3], [2]], [[1, 3], [3]]]
     823                sage: t.demazure_operator(2, truncated = True)
     824                [[[1, 3], [2]], [[1, 3], [3]]]
     825                sage: t.demazure_operator(1, truncated = True)
     826                []
     827                sage: t.demazure_operator(1)
     828                [[[1, 2], [2]]]
     829
     830                sage: K = KirillovReshetikhinCrystal(['A',2,1],2,1)
     831                sage: t = K(rows=[[3],[2]])
     832                sage: t.demazure_operator(0)
     833                [[[2, 3]], [[1, 2]]]
     834            """
     835            if truncated:
     836                l = []
     837            else:
     838                l = [self]
     839            for k in range(self.phi(i)):
     840                l.append(self.f_string([i for j in range(k+1)]))
     841            return(l)
     842
     843        def is_highest_weight(self, index_set = None):
     844            r"""
     845            Returns ``True`` if ``self`` is a highest weight.
     846            Specifying the option ``index_set`` to be a subset `I` of the
     847            index set of the underlying crystal, finds all highest
     848            weight vectors for arrows in `I`.
     849
     850            EXAMPLES::
     851
     852                sage: C = CrystalOfLetters(['A',5])
     853                sage: C(1).is_highest_weight()
     854                True
     855                sage: C(2).is_highest_weight()
     856                False
     857                sage: C(2).is_highest_weight(index_set = [2,3,4,5])
     858                True
     859            """
     860            if index_set is None:
     861                index_set = self.index_set()
     862            return all(self.e(i) is None for i in index_set)
  • new file sage/categories/examples/crystals.py

    diff --git a/sage/categories/examples/crystals.py b/sage/categories/examples/crystals.py
    new file mode 100644
    - +  
     1r"""
     2Example of a crystal
     3"""
     4#*****************************************************************************
     5#  Copyright (C) 2010 Anne Schilling <anne at math.ucdavis.edu>
     6#
     7#  Distributed under the terms of the GNU General Public License (GPL)
     8#                  http://www.gnu.org/licenses/
     9#******************************************************************************
     10
     11from sage.structure.parent import Parent
     12from sage.structure.element_wrapper import ElementWrapper
     13from sage.structure.unique_representation import UniqueRepresentation
     14from sage.categories.classical_crystals import ClassicalCrystals
     15from sage.categories.enumerated_sets import EnumeratedSets
     16from sage.combinat.root_system.cartan_type import CartanType
     17
     18class HighestWeightCrystalOfTypeA(UniqueRepresentation, Parent):
     19    r"""
     20    An example of a crystal: the highest weight crystal of type `A_n`
     21    of highest weight `\omega_1`.
     22
     23    The purpose of this class is to provide a minimal template for
     24    implementing crystals. See
     25    :class:`~sage.combinat.crystals.letters.CrystalOfLetters` for a
     26    full featured and optimized implementation.
     27
     28    EXAMPLES::
     29
     30        sage: C = Crystals().example()
     31        sage: C
     32        Highest weight crystal of type A_3 of highest weight omega_1
     33        sage: C.category()
     34        Category of classical crystals
     35
     36    The elements of this crystal are in the set `\{1,\ldots,n+1\}`::
     37
     38        sage: C.list()
     39        [1, 2, 3,  4]
     40        sage: C.module_generators[0]
     41        1
     42
     43    The crystal operators themselves correspond to the elementary
     44    transpositions::
     45
     46        sage: b = C.module_generators[0]
     47        sage: b.f(1)
     48        2
     49        sage: b.f(1).e(1) == b
     50        True
     51
     52    TESTS::
     53
     54        sage: TestSuite(C).run(verbose = True)
     55        running ._test_an_element() . . . pass
     56        running ._test_category() . . . pass
     57        running ._test_elements() . . .
     58          Running the test suite of self.an_element()
     59          running ._test_category() . . . pass
     60          running ._test_eq() . . . pass
     61          running ._test_not_implemented_methods() . . . pass
     62          running ._test_pickling() . . . pass
     63          pass
     64        running ._test_elements_eq() . . . pass
     65        running ._test_enumerated_set_contains() . . . pass
     66        running ._test_enumerated_set_iter_cardinality() . . . pass
     67        running ._test_enumerated_set_iter_list() . . . pass
     68        running ._test_eq() . . . pass
     69        running ._test_fast_iter() . . . pass
     70        running ._test_not_implemented_methods() . . . pass
     71        running ._test_pickling() . . . pass
     72        running ._test_some_elements() . . . pass
     73
     74    Only the following basic operations are implemented:
     75     - :meth:`.cartan_type` or an attribute _cartan_type
     76     - an attribute `module_generators`
     77     - :meth:`.Element.e`
     78     - :meth:`.Element.f`
     79     - :meth:`.Element.weight`
     80
     81    All the other usual crystal operations are inherited from the
     82    categories; for example::
     83
     84        sage: C.cardinality()
     85        4
     86    """
     87
     88    def __init__(self, n = 3):
     89        """
     90        EXAMPLES::
     91
     92            sage: C = sage.categories.examples.crystals.HighestWeightCrystalOfTypeA(4)
     93            sage: C == Crystals().example(4)
     94            True
     95        """
     96        Parent.__init__(self, category = ClassicalCrystals())
     97        self.n = n
     98        self._cartan_type = CartanType(['A',n])
     99        self.module_generators = [ self(1) ]
     100
     101    def _repr_(self):
     102        """
     103        EXAMPLES::
     104
     105            sage: Crystals().example()
     106            Highest weight crystal of type A_3 of highest weight omega_1
     107        """
     108        return "Highest weight crystal of type A_%s of highest weight omega_1"%(self.n)
     109
     110    # temporary woraround while an_element is overriden by Parent
     111    _an_element_ = EnumeratedSets.ParentMethods._an_element_
     112
     113    class Element(ElementWrapper):
     114
     115        def e(self, i):
     116            r"""
     117            Returns the action of `e_i` on ``self``.
     118
     119            EXAMPLES::
     120
     121                sage: C = Crystals().example(4)
     122                sage: [[c,i,c.e(i)] for i in C.index_set() for c in C if c.e(i) is not None]
     123                [[2, 1, 1], [3, 2, 2], [4, 3, 3], [5, 4, 4]]
     124            """
     125            assert i in self.index_set()
     126            if self.value == i+1:
     127                return self.parent()(self.value-1)
     128            else:
     129                return None
     130
     131        def f(self, i):
     132            r"""
     133            Returns the action of `f_i` on ``self``.
     134
     135            EXAMPLES::
     136
     137                sage: C = Crystals().example(4)
     138                sage: [[c,i,c.f(i)] for i in C.index_set() for c in C if c.f(i) is not None]
     139                [[1, 1, 2], [2, 2, 3], [3, 3, 4], [4, 4, 5]]
     140            """
     141            assert i in self.index_set()
     142            if self.value == i:
     143                return self.parent()(self.value+1)
     144            else:
     145                return None
     146
     147Example = HighestWeightCrystalOfTypeA
  • new file sage/categories/finite_crystals.py

    diff --git a/sage/categories/finite_crystals.py b/sage/categories/finite_crystals.py
    new file mode 100644
    - +  
     1r"""
     2Finite Crystals
     3"""
     4#*****************************************************************************
     5#  Copyright (C) 2010    Anne Schilling <anne at math.ucdavis.edu>
     6#
     7#  Distributed under the terms of the GNU General Public License (GPL)
     8#                  http://www.gnu.org/licenses/
     9#******************************************************************************
     10
     11from sage.misc.cachefunc import cached_method
     12from sage.categories.category import Category
     13from sage.categories.crystals import Crystals
     14from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     15
     16class FiniteCrystals(Category):
     17    """
     18    The category of finite crystals.
     19
     20    EXAMPLES::
     21
     22        sage: C = FiniteCrystals()
     23        sage: C
     24        Category of finite crystals
     25        sage: C.super_categories()
     26        [Category of crystals, Category of finite enumerated sets]
     27        sage: C.example()
     28        Highest weight crystal of type A_3 of highest weight omega_1
     29
     30    TESTS::
     31
     32        sage: TestSuite(C).run()
     33        sage: B = FiniteCrystals().example()
     34        sage: TestSuite(B).run(verbose = True)
     35        running ._test_an_element() . . . pass
     36        running ._test_category() . . . pass
     37        running ._test_elements() . . .
     38          Running the test suite of self.an_element()
     39          running ._test_category() . . . pass
     40          running ._test_eq() . . . pass
     41          running ._test_not_implemented_methods() . . . pass
     42          running ._test_pickling() . . . pass
     43          pass
     44        running ._test_elements_eq() . . . pass
     45        running ._test_enumerated_set_contains() . . . pass
     46        running ._test_enumerated_set_iter_cardinality() . . . pass
     47        running ._test_enumerated_set_iter_list() . . . pass
     48        running ._test_eq() . . . pass
     49        running ._test_fast_iter() . . . pass
     50        running ._test_not_implemented_methods() . . . pass
     51        running ._test_pickling() . . . pass
     52        running ._test_some_elements() . . . pass
     53    """
     54
     55    @cached_method
     56    def super_categories(self):
     57        r"""
     58        EXAMPLES::
     59
     60            sage: FiniteCrystals().super_categories()
     61            [Category of crystals, Category of finite enumerated sets]
     62        """
     63        return [Crystals(), FiniteEnumeratedSets()]
     64
     65    def example(self, n = 3):
     66        """
     67        Returns an example of highest weight crystals, as per
     68        :meth:`Category.example`.
     69
     70        EXAMPLES::
     71
     72            sage: B = FiniteCrystals().example(); B
     73            Highest weight crystal of type A_3 of highest weight omega_1
     74        """
     75        from sage.categories.crystals import Crystals
     76        return Crystals().example(n)
     77
     78    class ParentMethods:
     79
     80        def list(self):
     81            """
     82            Returns a list of the elements of ``self`` obtained by
     83            repeatedly applying the `f_i` operators to the module
     84            generators of ``self``.
     85
     86            EXAMPLES::
     87
     88                sage: C = FiniteCrystals().example(5)
     89                sage: l = C._list_brute_force()
     90                sage: l.sort(); l
     91                [1, 2, 3, 4, 5, 6]
     92            """
     93            # Should use transitiveIdeal
     94            # should be transformed to __iter__ instead of list
     95            # To be moved in a super category CombinatorialModule
     96            result = set(self.module_generators)
     97            todo = result.copy()
     98            while len(todo) > 0:
     99                x = todo.pop()
     100                for i in self.index_set():
     101                    y = x.f(i)
     102                    if y == None or y in result:
     103                        continue
     104                    todo.add(y)
     105                    result.add(y)
     106            return list(result)
     107
     108        _list_brute_force = list
  • new file sage/categories/highest_weight_crystals.py

    diff --git a/sage/categories/highest_weight_crystals.py b/sage/categories/highest_weight_crystals.py
    new file mode 100644
    - +  
     1r"""
     2Highest Weight Crystals
     3"""
     4#*****************************************************************************
     5#  Copyright (C) 2010    Anne Schilling <anne at math.ucdavis.edu>
     6#
     7#  Distributed under the terms of the GNU General Public License (GPL)
     8#                  http://www.gnu.org/licenses/
     9#******************************************************************************
     10
     11from sage.misc.cachefunc import cached_method
     12from sage.categories.category import Category
     13from sage.categories.crystals import Crystals
     14
     15class HighestWeightCrystals(Category):
     16    """
     17    The category of highest weight crystals.
     18
     19    A crystal is highest weight if it is acyclic; in particular, every
     20    connected component has a unique highest weight element, and that
     21    element generate the component.
     22
     23    EXAMPLES::
     24
     25        sage: C = HighestWeightCrystals()
     26        sage: C
     27        Category of highest weight crystals
     28        sage: C.super_categories()
     29        [Category of crystals]
     30        sage: C.example()
     31        Highest weight crystal of type A_3 of highest weight omega_1
     32
     33    TESTS::
     34
     35        sage: TestSuite(C).run()
     36        sage: B = HighestWeightCrystals().example()
     37        sage: TestSuite(B).run(verbose = True)
     38        running ._test_an_element() . . . pass
     39        running ._test_category() . . . pass
     40        running ._test_elements() . . .
     41          Running the test suite of self.an_element()
     42          running ._test_category() . . . pass
     43          running ._test_eq() . . . pass
     44          running ._test_not_implemented_methods() . . . pass
     45          running ._test_pickling() . . . pass
     46          pass
     47        running ._test_elements_eq() . . . pass
     48        running ._test_enumerated_set_contains() . . . pass
     49        running ._test_enumerated_set_iter_cardinality() . . . pass
     50        running ._test_enumerated_set_iter_list() . . . pass
     51        running ._test_eq() . . . pass
     52        running ._test_fast_iter() . . . pass
     53        running ._test_not_implemented_methods() . . . pass
     54        running ._test_pickling() . . . pass
     55        running ._test_some_elements() . . . pass
     56    """
     57
     58    @cached_method
     59    def super_categories(self):
     60        r"""
     61        EXAMPLES::
     62
     63            sage: HighestWeightCrystals().super_categories()
     64            [Category of crystals]
     65        """
     66        return [Crystals()]
     67
     68    def example(self):
     69        """
     70        Returns an example of highest weight crystals, as per
     71        :meth:`Category.example`.
     72
     73        EXAMPLES::
     74
     75            sage: B = HighestWeightCrystals().example(); B
     76            Highest weight crystal of type A_3 of highest weight omega_1
     77        """
     78        from sage.categories.crystals import Crystals
     79        return Crystals().example()
     80
     81    class ParentMethods:
     82
     83        @cached_method
     84        def highest_weight_vectors(self):
     85            r"""
     86            Returns the highest weight vectors of ``self``
     87
     88            This default implementation selects among the module
     89            generators those that are highest weight, and cache the
     90            result.
     91
     92            EXAMPLES::
     93
     94                sage: C = CrystalOfLetters(['A',5])
     95                sage: C.highest_weight_vectors()
     96                [1]
     97
     98            ::
     99
     100                sage: C = CrystalOfLetters(['A',2])
     101                sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)],[C(1),C(2),C(1)]])
     102                sage: T.highest_weight_vectors()
     103                [[2, 1, 1], [1, 2, 1]]
     104            """
     105            return [g for g in self.module_generators if g.is_highest_weight()]
     106
     107        def highest_weight_vector(self):
     108            r"""
     109            Returns the highest weight vector if there is a single one;
     110            otherwise, raises an error.
     111
     112            Caveat: this assumes that :meth:`.highest_weight_vector`
     113            returns a list or tuple.
     114
     115            EXAMPLES::
     116
     117                sage: C = CrystalOfLetters(['A',5])
     118                sage: C.highest_weight_vector()
     119                1
     120            """
     121            hw = self.highest_weight_vectors();
     122            if len(hw) == 1:
     123                return hw[0]
     124            else:
     125                raise RuntimeError("The crystal does not have exactly one highest weight vector")
     126
     127
     128    class ElementMethods:
     129
     130        def to_highest_weight(self, list = [], index_set = None):
     131            r"""
     132            Yields the highest weight element `u` and a list `[i_1,...,i_k]`
     133            such that `self = f_{i_1} ... f_{i_k} u`, where `i_1,...,i_k` are
     134            elements in `index_set`. By default the index set is assumed to be
     135            the full index set of self.
     136
     137            EXAMPLES::
     138
     139                sage: T = CrystalOfTableaux(['A',3], shape = [1])
     140                sage: t = T(rows = [[3]])
     141                sage: t.to_highest_weight()
     142                [[[1]], [2, 1]]
     143                sage: t.to_highest_weight()
     144                [[[1]], [2, 1]]
     145                sage: T = CrystalOfTableaux(['A',3], shape = [2,1])
     146                sage: t = T(rows = [[1,2],[4]])
     147                sage: t.to_highest_weight()
     148                [[[1, 1], [2]], [1, 3, 2]]
     149                sage: t.to_highest_weight(index_set = [3])
     150                [[[1, 2], [3]], [3]]
     151            """
     152            if index_set is None:
     153                index_set = self.index_set()
     154            for i in index_set:
     155                if self.epsilon(i) <> 0:
     156                    self = self.e(i)
     157                    return self.to_highest_weight(list = list + [i], index_set = index_set)
     158            return [self, list]
  • sage/combinat/combinat.py

    diff --git a/sage/combinat/combinat.py b/sage/combinat/combinat.py
    a b class CombinatorialObject(SageObject): 
    625625        """
    626626        return str(self._list)
    627627
    628     def __repr__(self):
     628    def _repr_(self):
    629629        """
    630630        EXAMPLES::
    631631       
  • sage/combinat/crystals/affine.py

    diff --git a/sage/combinat/crystals/affine.py b/sage/combinat/crystals/affine.py
    a b  
    11r"""
    2 Affine crystals
     2Affine Crystals
    33"""
    4 
    54#*****************************************************************************
    65#       Copyright (C) 2008 Brant Jones <brant at math.ucdavis.edu>
    76#                          Anne Schilling <anne at math.ucdavis.edu>
    87#
    98#  Distributed under the terms of the GNU General Public License (GPL)
    10 #
    11 #    This code is distributed in the hope that it will be useful,
    12 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
    13 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    14 #    General Public License for more details.
    15 #
    16 #  The full text of the GPL is available at:
    17 #
    189#                  http://www.gnu.org/licenses/
    1910#****************************************************************************
    2011# Acknowledgment: most of the design and implementation of this
    Affine crystals 
    2213#****************************************************************************
    2314
    2415from sage.misc.abstract_method import abstract_method
    25 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     16from sage.categories.finite_crystals import FiniteCrystals
     17from sage.structure.parent import Parent
    2618from sage.structure.unique_representation import UniqueRepresentation
    2719from sage.structure.element_wrapper import ElementWrapper
    28 from sage.combinat.crystals.crystals import Crystal, CrystalElement
    2920from sage.combinat.root_system.cartan_type import CartanType
    30 
    31 class AffineCrystal(Crystal):
    32     r"""
    33     The abstract class of affine crystals
    34     """
    3521 
    36 class AffineCrystalFromClassical(Crystal, UniqueRepresentation):
     22class AffineCrystalFromClassical(UniqueRepresentation, Parent):
    3723    r"""
    3824    This abstract class can be used for affine crystals that are constructed from a classical crystal.
    3925    The zero arrows can be implemented using different methods (for example using a Dynkin diagram
    4026    automorphisms or virtual crystals).
    4127
    4228    This is a helper class, mostly used to implement Kirillov-Reshetikhin crystals
    43     (see: :function:`sage.combinat.crystals.kirillov_reshetikhin.KirillovReshetikhin`).
     29    (see: :func:`sage.combinat.crystals.kirillov_reshetikhin.KirillovReshetikhin`).
    4430
    4531    For general information about crystals see :mod:`sage.combinat.crystals`.
    4632
    class AffineCrystalFromClassical(Crystal 
    5238
    5339    EXAMPLES::
    5440
    55         sage: n=2 
     41        sage: n=2
    5642        sage: C=CrystalOfTableaux(['A',n],shape=[1])
    57         sage: pr=lambda x : C(x.to_tableau().promotion(n))
    58         sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     43        sage: pr = attrcall("promotion")
     44        sage: pr_inverse = attrcall("promotion_inverse")
    5945        sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    6046        sage: A.list()
    6147        [[[1]], [[2]], [[3]]]
    class AffineCrystalFromClassical(Crystal 
    7662
    7763    @staticmethod
    7864    def __classcall__(cls, cartan_type, *args, **options):
     65        """
     66        TESTS::
     67
     68            sage: n = 1
     69            sage: C = CrystalOfTableaux(['A',n],shape=[1])
     70            sage: pr = attrcall("promotion")
     71            sage: pr_inverse = attrcall("promotion_inverse")
     72            sage: A = AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1) # indirect doctest
     73            sage: B = AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1) # indirect doctest
     74            sage: A is B
     75            True
     76        """
    7977        ct = CartanType(cartan_type)
    8078        return super(AffineCrystalFromClassical, cls).__classcall__(cls, ct, *args, **options)
    8179
    class AffineCrystalFromClassical(Crystal 
    8684
    8785        EXAMPLES::
    8886
    89             sage: n=1
    90             sage: C=CrystalOfTableaux(['A',n],shape=[1])
    91             sage: def pr(x): return C(x.to_tableau().promotion(n))
    92             sage: def pr_inverse(x): return C(x.to_tableau().promotion_inverse(n))
    93             sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1) # indirect doctest
     87            sage: n = 1
     88            sage: C = CrystalOfTableaux(['A',n],shape=[1])
     89            sage: pr = attrcall("promotion")
     90            sage: pr_inverse = attrcall("promotion_inverse")
     91            sage: A = AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1) # indirect doctest
    9492            sage: A.list()
    9593            [[[1]], [[2]]]
    9694            sage: A.cartan_type()
    class AffineCrystalFromClassical(Crystal 
    9896            sage: A.index_set()
    9997            [0, 1]
    10098
     99        Note: AffineCrystalFromClassical is an abstract class, so we
     100        can't test it directly.
     101
    101102        TESTS::
    102103
    103             sage: import __main__; __main__.pr = pr; __main__.pr_inverse = pr_inverse # standard pickling hack
    104104            sage: TestSuite(A).run()
    105105        """
    106106        if category is None:
    107             category = FiniteEnumeratedSets()
     107            category = FiniteCrystals()
    108108        self._cartan_type = cartan_type
    109         super(AffineCrystalFromClassical, self).__init__(category = category)
     109        Parent.__init__(self, category = category)
    110110        self.classical_crystal = classical_crystal;
    111111        self.module_generators = map( self.retract, self.classical_crystal.module_generators )
    112112        self.element_class._latex_ = lambda x: x.lift()._latex_()
    class AffineCrystalFromClassical(Crystal 
    117117
    118118            sage: n=1
    119119            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    120             sage: def pr(x): return C(x.to_tableau().promotion(n))
    121             sage: def pr_inverse(x): return C(x.to_tableau().promotion_inverse(n))
     120            sage: pr = attrcall("promotion")
     121            sage: pr_inverse = attrcall("promotion_inverse")
    122122            sage: AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1) # indirect doctest
    123123            An affine crystal for type ['A', 1, 1]
    124124        """
    class AffineCrystalFromClassical(Crystal 
    133133
    134134            sage: n=1
    135135            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    136             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    137             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     136            sage: pr = attrcall("promotion")
     137            sage: pr_inverse = attrcall("promotion_inverse")
    138138            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1) # indirect doctest
    139139            sage: A.list() # indirect doctest
    140140            [[[1]], [[2]]]
    class AffineCrystalFromClassical(Crystal 
    151151
    152152            sage: n=2
    153153            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    154             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    155             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     154            sage: pr = attrcall("promotion")
     155            sage: pr_inverse = attrcall("promotion_inverse")
    156156            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    157157            sage: A.list()
    158158            [[[1]], [[2]], [[3]]]
    class AffineCrystalFromClassical(Crystal 
    167167
    168168            sage: n=2
    169169            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    170             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    171             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     170            sage: pr = attrcall("promotion")
     171            sage: pr_inverse = attrcall("promotion_inverse")
    172172            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    173173            sage: b=A.list()[0]
    174174            sage: A.lift(b)
    class AffineCrystalFromClassical(Crystal 
    186186
    187187            sage: n=2
    188188            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    189             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    190             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     189            sage: pr = attrcall("promotion")
     190            sage: pr_inverse = attrcall("promotion_inverse")
    191191            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    192192            sage: t=C(rows=[[1]])
    193193            sage: t.parent()
    class AffineCrystalFromClassical(Crystal 
    207207
    208208            sage: n=2
    209209            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    210             sage: def pr(x): return C(x.to_tableau().promotion(n))
    211             sage: def pr_inverse(x): return C(x.to_tableau().promotion_inverse(n))
     210            sage: pr = attrcall("promotion")
     211            sage: pr_inverse = attrcall("promotion_inverse")
    212212            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    213213            sage: b=A(rows=[[1]])
    214214            sage: b
    class AffineCrystalFromClassical(Crystal 
    231231
    232232            sage: n=2
    233233            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    234             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    235             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     234            sage: pr = attrcall("promotion")
     235            sage: pr_inverse = attrcall("promotion_inverse")
    236236            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    237237            sage: b=A(rows=[[1]])
    238238            sage: A.__contains__(b)
    class AffineCrystalFromClassical(Crystal 
    241241        return x.parent() is self
    242242
    243243
    244 class AffineCrystalFromClassicalElement(ElementWrapper, CrystalElement):
     244class AffineCrystalFromClassicalElement(ElementWrapper):
    245245    r"""
    246246    Elements of crystals that are constructed from a classical crystal.
    247247    The elements inherit many of their methods from the classical crystal
    class AffineCrystalFromClassicalElement( 
    255255
    256256        sage: n=2
    257257        sage: C=CrystalOfTableaux(['A',n],shape=[1])
    258         sage: pr=lambda x : C(x.to_tableau().promotion(n))
    259         sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     258        sage: pr = attrcall("promotion")
     259        sage: pr_inverse = attrcall("promotion_inverse")
    260260        sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    261261        sage: b=A(rows=[[1]])
    262         sage: b.__repr__()
     262        sage: b._repr_()
    263263        '[[1]]'
    264264    """
    265265
    class AffineCrystalFromClassicalElement( 
    271271
    272272            sage: n=2
    273273            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    274             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    275             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     274            sage: pr = attrcall("promotion")
     275            sage: pr_inverse = attrcall("promotion_inverse")
    276276            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    277277            sage: b=A(rows=[[1]])
    278278            sage: b.classical_weight()
    class AffineCrystalFromClassicalElement( 
    288288
    289289            sage: n=2
    290290            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    291             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    292             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     291            sage: pr = attrcall("promotion")
     292            sage: pr_inverse = attrcall("promotion_inverse")
    293293            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    294294            sage: b=A.list()[0]
    295295            sage: b.lift()
    class AffineCrystalFromClassicalElement( 
    332332
    333333            sage: n=2
    334334            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    335             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    336             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     335            sage: pr = attrcall("promotion")
     336            sage: pr_inverse = attrcall("promotion_inverse")
    337337            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    338338            sage: b=A(rows=[[1]])
    339339            sage: b.e(0)
    class AffineCrystalFromClassicalElement( 
    357357
    358358            sage: n=2
    359359            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    360             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    361             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     360            sage: pr = attrcall("promotion")
     361            sage: pr_inverse = attrcall("promotion_inverse")
    362362            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    363363            sage: b=A(rows=[[3]])
    364364            sage: b.f(0)
    class AffineCrystalFromClassicalElement( 
    388388
    389389            sage: n=2
    390390            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    391             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    392             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     391            sage: pr = attrcall("promotion")
     392            sage: pr_inverse = attrcall("promotion_inverse")
    393393            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    394394            sage: [x.epsilon(0) for x in A.list()]
    395395            [1, 0, 0]
    class AffineCrystalFromClassicalElement( 
    415415
    416416            sage: n=2
    417417            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    418             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    419             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     418            sage: pr = attrcall("promotion")
     419            sage: pr_inverse = attrcall("promotion_inverse")
    420420            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    421421            sage: [x.phi(0) for x in A.list()]
    422422            [0, 0, 1]
    class AffineCrystalFromClassicalAndPromo 
    454454
    455455    EXAMPLES::
    456456
    457         sage: n=2 
     457        sage: n=2
    458458        sage: C=CrystalOfTableaux(['A',n],shape=[1])
    459         sage: pr=lambda x : C(x.to_tableau().promotion(n))
    460         sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     459        sage: pr = attrcall("promotion")
     460        sage: pr_inverse = attrcall("promotion_inverse")
    461461        sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    462462        sage: A.list()
    463463        [[[1]], [[2]], [[3]]]
    class AffineCrystalFromClassicalAndPromo 
    485485
    486486            sage: n=1
    487487            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    488             sage: def pr(x): return C(x.to_tableau().promotion(n))
    489             sage: def pr_inverse(x): return C(x.to_tableau().promotion_inverse(n))
     488            sage: pr = attrcall("promotion")
     489            sage: pr_inverse = attrcall("promotion_inverse")
    490490            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    491491            sage: A.list()
    492492            [[[1]], [[2]]]
    class AffineCrystalFromClassicalAndPromo 
    497497
    498498        TESTS::
    499499
    500             sage: import __main__; __main__.pr = pr; __main__.pr_inverse = pr_inverse # standard pickling hack
    501500            sage: TestSuite(A).run()
    502501        """
    503502        AffineCrystalFromClassical.__init__(self, cartan_type, classical_crystal)
    class AffineCrystalFromClassicalAndPromo 
    513512
    514513            sage: n=2
    515514            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    516             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    517             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     515            sage: pr = attrcall("promotion")
     516            sage: pr_inverse = attrcall("promotion_inverse")
    518517            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    519518            sage: b=A.list()[0]
    520519            sage: A.automorphism(b)
    class AffineCrystalFromClassicalAndPromo 
    530529
    531530            sage: n=2
    532531            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    533             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    534             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     532            sage: pr = attrcall("promotion")
     533            sage: pr_inverse = attrcall("promotion_inverse")
    535534            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    536535            sage: b=A.list()[0]
    537536            sage: A.inverse_automorphism(b)
    class AffineCrystalFromClassicalAndPromo 
    557556
    558557        sage: n=2
    559558        sage: C=CrystalOfTableaux(['A',n],shape=[1])
    560         sage: pr=lambda x : C(x.to_tableau().promotion(n))
    561         sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     559        sage: pr = attrcall("promotion")
     560        sage: pr_inverse = attrcall("promotion_inverse")
    562561        sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    563562        sage: b=A(rows=[[1]])
    564         sage: b.__repr__()
     563        sage: b._repr_()
    565564        '[[1]]'
    566565    """
    567566
    568567    def e0(self):
    569568        r"""
    570569        Implements `e_0` using the automorphism as
    571         `e_0 = \pr^{-1} e_{dynkin_node} \pr`
     570        `e_0 = \operatorname{pr}^{-1} e_{dynkin_node} \operatorname{pr}`
    572571
    573572        EXAMPLES::
    574573
    575574            sage: n=2
    576575            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    577             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    578             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     576            sage: pr = attrcall("promotion")
     577            sage: pr_inverse = attrcall("promotion_inverse")
    579578            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    580579            sage: b=A(rows=[[1]])
    581580            sage: b.e0()
    class AffineCrystalFromClassicalAndPromo 
    590589    def f0(self):
    591590        r"""
    592591        Implements `f_0` using the automorphism as
    593         `f_0 = \pr^{-1} f_{dynkin_node} \pr`
     592        `f_0 = \operatorname{pr}^{-1} f_{dynkin_node} \operatorname{pr}`
    594593
    595594        EXAMPLES::
    596595
    597596            sage: n=2
    598597            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    599             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    600             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     598            sage: pr = attrcall("promotion")
     599            sage: pr_inverse = attrcall("promotion_inverse")
    601600            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    602601            sage: b=A(rows=[[3]])
    603602            sage: b.f0()
    class AffineCrystalFromClassicalAndPromo 
    617616
    618617            sage: n=2
    619618            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    620             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    621             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     619            sage: pr = attrcall("promotion")
     620            sage: pr_inverse = attrcall("promotion_inverse")
    622621            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    623622            sage: [x.epsilon0() for x in A.list()]
    624623            [1, 0, 0]
    class AffineCrystalFromClassicalAndPromo 
    634633
    635634            sage: n=2
    636635            sage: C=CrystalOfTableaux(['A',n],shape=[1])
    637             sage: pr=lambda x : C(x.to_tableau().promotion(n))
    638             sage: pr_inverse = lambda x : C(x.to_tableau().promotion_inverse(n))
     636            sage: pr = attrcall("promotion")
     637            sage: pr_inverse = attrcall("promotion_inverse")
    639638            sage: A=AffineCrystalFromClassicalAndPromotion(['A',n,1],C,pr,pr_inverse,1)
    640639            sage: [x.phi0() for x in A.list()]
    641640            [0, 0, 1]
  • sage/combinat/crystals/all.py

    diff --git a/sage/combinat/crystals/all.py b/sage/combinat/crystals/all.py
    a b  
    1 from crystals import Crystal
    21from letters import CrystalOfLetters
    32from spins import CrystalOfSpins
    43from spins import CrystalOfSpinsPlus
  • sage/combinat/crystals/crystals.py

    diff --git a/sage/combinat/crystals/crystals.py b/sage/combinat/crystals/crystals.py
    a b of the type `T` weight lattice such that 
    3030
    3131
    3232This crystal actually models a representation of a Lie algebra if
    33 it satisfies some further local conditions due to Stembridge, see
    34 J. Stembridge, *A local characterization of simply-laced crystals*,
    35 Trans. Amer. Math. Soc. 355 (2003), no. 12, 4807-4823.
     33it satisfies some further local conditions due to Stembridge [St2003].
     34
     35REFERENCES:
     36
     37    .. [St2003] J. Stembridge, *A local characterization of simply-laced crystals*,
     38       Trans. Amer. Math. Soc. 355 (2003), no. 12, 4807-4823.
    3639
    3740EXAMPLES:
    3841
    39 We construct the type `A_5` crystal on letters (or in
    40 representation theoretic terms, the highest weight crystal of type
    41 `A_5` corresponding to the highest weight
    42 `\Lambda_1`)
    43 
    44 ::
     42We construct the type `A_5` crystal on letters (or in representation
     43theoretic terms, the highest weight crystal of type `A_5`
     44corresponding to the highest weight `\Lambda_1`)::
    4545
    4646    sage: C = CrystalOfLetters(['A',5]); C
    4747    The crystal of letters for type ['A', 5]
    One can get (currently) crude plotting v 
    8080For rank two crystals, there is an alternative method of getting
    8181metapost pictures. For more information see C.metapost?
    8282
     83See also the categories :class:`Crystals`, :class:`ClassicalCrystals`,
     84:class:`FiniteCrystals`, :class:`HighestWeightCrystals`.
     85
     86
    8387Caveat: this crystal library, although relatively featureful for
    8488classical crystals, is still in an early development stage, and the
    8589syntax details may be subject to changes.
    8690
    8791TODO:
    8892
    89 
    9093-  Vocabulary and conventions:
    9194
    92    
    9395   -  elements or vectors of a crystal?
    9496
    9597   -  For a classical crystal: connected / highest weight /
    TODO: 
    9799
    98100   -  ...
    99101
    100 
    101102-  More introductory doc explaining the mathematical background
    102103
    103104-  Layout instructions for plot() for rank 2 types
    TODO: 
    107108-  Littelmann paths and/or alcove paths (this would give us the
    108109   exceptional types)
    109110
    110 -  RestrictionOfCrystal / DirectSumOfCrystals
     111-  RestrictionOfCrystal
    111112
    112113
    113114Most of the above features (except Littelmann/alcove paths) are in
    inspiration. 
    134135# library is heavily inspired from MuPAD-Combinat.
    135136#****************************************************************************
    136137
    137 from sage.misc.misc_c import prod
    138 from sage.misc.latex import latex
    139 from sage.misc.cachefunc import CachedFunction
    140 from sage.structure.unique_representation import UniqueRepresentation
    141 from sage.structure.parent import Parent
    142 from sage.structure.element import Element
    143 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
    144 from sage.graphs.all import DiGraph
    145 from sage.combinat import ranker
    146 from sage.combinat.root_system.weyl_characters import WeylCharacter
     138#from sage.structure.unique_representation import UniqueRepresentation
     139#from sage.structure.parent import Parent
     140#from sage.structure.element import Element
     141#from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     142#from sage.graphs.all import DiGraph
     143#from sage.combinat import ranker
     144#from sage.combinat.root_system.weyl_characters import WeylCharacter
    147145from sage.combinat.backtrack import GenericBacktracker
    148 from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
    149 from sage.rings.rational_field import QQ
    150 
    151 ## MuPAD-Combinat's Cat::Crystal
    152 # FIXME: crystals, like most parent should have unique data representation
    153 # TODO: make into a category
    154 class Crystal(UniqueRepresentation, Parent):
    155     r"""
    156     The abstract class of crystals
    157    
    158     Derived subclasses should implement the following:
    159    
    160     -  either a method cartan_type or an attribute _cartan_type
    161      
    162     -  module_generators: a list (or container) of distinct elements
    163        which generate the crystal using `f_i`
    164 
    165     """
    166 
    167     def _an_element_(self):
    168         """
    169         Returns an element of self
    170 
    171             sage: C = CrystalOfLetters(['A', 5])
    172             sage: C.an_element()
    173             1
    174         """
    175         return self.first()
    176 
    177     def weight_lattice_realization(self):
    178         """
    179         Returns the weight lattice realization for the root system
    180         associated to self. This default implementation uses the
    181         ambient space of the root system.
    182        
    183         EXAMPLES::
    184        
    185             sage: C = CrystalOfLetters(['A', 5])
    186             sage: C.weight_lattice_realization()
    187             Ambient space of the Root system of type ['A', 5]
    188             sage: K = KirillovReshetikhinCrystal(['A',2,1], 1, 1)
    189             sage: K.weight_lattice_realization()
    190             Weight lattice of the Root system of type ['A', 2, 1]
    191         """
    192         F = self.cartan_type().root_system()
    193         if F.ambient_space() is None:
    194             return F.weight_lattice()
    195         else:
    196             return F.ambient_space()
    197 
    198     def cartan_type(self):
    199         """
    200         Returns the Cartan type of the associated crystal
    201 
    202         EXAMPLES::
    203             sage: C = CrystalOfLetters(['A',2])
    204             sage: C.cartan_type()
    205             ['A', 2]
    206         """
    207         return self._cartan_type
    208  
    209     def index_set(self):
    210         """
    211         Returns the index set of the Dynkin diagram underlying the given crystal
    212 
    213         EXAMPLES:
    214             sage: C = CrystalOfLetters(['A', 5])
    215             sage: C.index_set()
    216             [1, 2, 3, 4, 5]
    217         """
    218         return self.cartan_type().index_set()
    219 
    220     def Lambda(self):
    221         """
    222         Returns the fundamentals weights in the weight lattice realization
    223         for the root system associated to self.
    224        
    225         EXAMPLES::
    226        
    227             sage: C = CrystalOfLetters(['A', 5])
    228             sage: C.Lambda()
    229             Finite family {1: (1, 0, 0, 0, 0, 0), 2: (1, 1, 0, 0, 0, 0), 3: (1, 1, 1, 0, 0, 0), 4: (1, 1, 1, 1, 0, 0), 5: (1, 1, 1, 1, 1, 0)}
    230         """
    231         return self.weight_lattice_realization().fundamental_weights()
    232 
    233     def check(self):
    234         r"""
    235         Runs sanity checks on the crystal:
    236        
    237        
    238         -  Checks that count, list, and __iter__ are consistent. For a
    239            ClassicalCrystal, this in particular checks that the number of
    240            elements returned by the brute force listing and the iterator
    241            __iter__ are consistent with the Weyl dimension formula.
    242        
    243         -  Should check Stembridge's rules, etc.
    244        
    245         EXAMPLES::
    246        
    247             sage: C = CrystalOfLetters(['A', 5])
    248             sage: C.check()
    249             True
    250         """
    251         # Those tests could be lifted up to CombinatorialClass
    252         list1 = self.list()
    253         set1 = set(list1)
    254         list2 = [c for c in self]
    255         set2 = set(list2)
    256         list3 = Crystal.list(self)
    257         set3 = set(list3)
    258         return len(set1) == len(list1) \
    259                and len(set2) == len(list2) \
    260                and len(set3) == len(list3) \
    261                and len(set1) == self.cardinality() \
    262                and set1 == set2 \
    263                and set2 == set3
    264 
    265     def list(self):
    266         """
    267         Returns a list of the elements of self obtained by continually
    268         apply the `f_i` operators to the module generators of
    269         self.
    270        
    271         EXAMPLES::
    272        
    273             sage: from sage.combinat.crystals.crystals import Crystal
    274             sage: C = CrystalOfLetters(['A', 5])
    275             sage: l = Crystal.list(C)
    276             sage: l.sort(); l
    277             [1, 2, 3, 4, 5, 6]
    278         """
    279         # Should use transitiveIdeal
    280         # should be transformed to __iter__ instead of list
    281         # To be moved in a super category CombinatorialModule
    282         result = set(self.module_generators)
    283         todo = result.copy()
    284         while len(todo) > 0:
    285             x = todo.pop()
    286             for i in self.index_set():
    287                 y = x.f(i)
    288                 if y == None or y in result:
    289                     continue
    290                 todo.add(y)
    291                 result.add(y)
    292         return list(result)
    293 
    294     def crystal_morphism(self, g, index_set = None, automorphism = lambda i : i, direction = 'down', direction_image = 'down',
    295                          similarity_factor = None, similarity_factor_domain = None, cached = False, acyclic = True):
    296         """
    297         Constructs a morphism from the crystal self to another crystal.
    298         The input `g` can either be a function of a (sub)set of elements of self to
    299         element in another crystal or a dictionary between certain elements.
    300         Usually one would map highest weight elements or crystal generators to each
    301         other using g.
    302         Specifying index_set gives the opportunity to define the morphism as `I`-crystals
    303         where `I =` index_set. If index_set is not specified, the index set of self is used.
    304         It is also possible to define twisted-morphisms by specifying an automorphism on the
    305         nodes in te Dynkin diagram (or the index_set).
    306         The option direction and direction_image indicate whether to use `f_i` or `e_i` in
    307         self or the image crystal to construct the morphism, depending on whether the direction
    308         is set to 'down' or 'up'.
    309         It is also possible to set a similarity_factor. This should be a dictionary between
    310         the elements in the index set and positive integers. The crystal operator `f_i` then gets
    311         mapped to `f_i^{m_i}` where `m_i =` similarity_factor[i].
    312         Setting similarity_factor_domain to a dictionary between the index set and positive integers
    313         has the effect that `f_i^{m_i}` gets mapped to `f_i` where `m_i =` similarity_factor_domain[i].
    314         Finally, it is possible to set the option `acyclic = False`. This calculates an isomorphism
    315         for cyclic crystals (for example finite affine crystals). In this case the input function `g`
    316         is supposed to be given as a dictionary.
    317 
    318         EXAMPLES::
    319 
    320             sage: C2 = CrystalOfLetters(['A',2])
    321             sage: C3 = CrystalOfLetters(['A',3])
    322             sage: g = {C2.module_generators[0] : C3.module_generators[0]}
    323             sage: g_full = C2.crystal_morphism(g)
    324             sage: g_full(C2(1))
    325             1
    326             sage: g_full(C2(2))
    327             2
    328             sage: g = {C2(1) : C2(3)}
    329             sage: g_full = C2.crystal_morphism(g, automorphism = lambda i : 3-i, direction_image = 'up')
    330             sage: [g_full(b) for b in C2]
    331             [3, 2, 1]
    332             sage: T = CrystalOfTableaux(['A',2], shape = [2])
    333             sage: g = {C2(1) : T(rows=[[1,1]])}
    334             sage: g_full = C2.crystal_morphism(g, similarity_factor = {1:2, 2:2})
    335             sage: [g_full(b) for b in C2]
    336             [[[1, 1]], [[2, 2]], [[3, 3]]]
    337             sage: g = {T(rows=[[1,1]]) : C2(1)}
    338             sage: g_full = T.crystal_morphism(g, similarity_factor_domain = {1:2, 2:2})
    339             sage: g_full(T(rows=[[2,2]]))
    340             2
    341 
    342             sage: B1=KirillovReshetikhinCrystal(['A',2,1],1,1)
    343             sage: B2=KirillovReshetikhinCrystal(['A',2,1],1,2)
    344             sage: T=TensorProductOfCrystals(B1,B2)
    345             sage: T1=TensorProductOfCrystals(B2,B1)
    346             sage: La = T.weight_lattice_realization().fundamental_weights()
    347             sage: t = [b for b in T if b.weight() == -3*La[0] + 3*La[1]][0]
    348             sage: t1 = [b for b in T1 if b.weight() == -3*La[0] + 3*La[1]][0]
    349             sage: g={t:t1}
    350             sage: f=T.crystal_morphism(g,acyclic = False)
    351             sage: [[b,f(b)] for b in T]
    352             [[[[[1]], [[1, 1]]], [[[1, 1]], [[1]]]],
    353             [[[[1]], [[1, 2]]], [[[1, 1]], [[2]]]],
    354             [[[[1]], [[2, 2]]], [[[1, 2]], [[2]]]],
    355             [[[[1]], [[1, 3]]], [[[1, 1]], [[3]]]],
    356             [[[[1]], [[2, 3]]], [[[1, 2]], [[3]]]],
    357             [[[[1]], [[3, 3]]], [[[1, 3]], [[3]]]],
    358             [[[[2]], [[1, 1]]], [[[1, 2]], [[1]]]],
    359             [[[[2]], [[1, 2]]], [[[2, 2]], [[1]]]],
    360             [[[[2]], [[2, 2]]], [[[2, 2]], [[2]]]],
    361             [[[[2]], [[1, 3]]], [[[2, 3]], [[1]]]],
    362             [[[[2]], [[2, 3]]], [[[2, 2]], [[3]]]],
    363             [[[[2]], [[3, 3]]], [[[2, 3]], [[3]]]],
    364             [[[[3]], [[1, 1]]], [[[1, 3]], [[1]]]],
    365             [[[[3]], [[1, 2]]], [[[1, 3]], [[2]]]],
    366             [[[[3]], [[2, 2]]], [[[2, 3]], [[2]]]],
    367             [[[[3]], [[1, 3]]], [[[3, 3]], [[1]]]],
    368             [[[[3]], [[2, 3]]], [[[3, 3]], [[2]]]],
    369             [[[[3]], [[3, 3]]], [[[3, 3]], [[3]]]]]
    370         """
    371         if index_set is None:
    372             index_set = self.index_set()
    373         if similarity_factor is None:
    374             similarity_factor = dict( (i,1) for i in index_set )
    375         if similarity_factor_domain is None:
    376             similarity_factor_domain = dict( (i,1) for i in index_set )
    377         if direction == 'down':
    378             e_string = 'e_string'
    379         else:
    380             e_string = 'f_string'
    381         if direction_image == 'down':
    382             f_string = 'f_string'
    383         else:
    384             f_string = 'e_string'
    385 
    386         if acyclic:
    387             if type(g) == dict:
    388                 g = g.__getitem__
    389 
    390             def morphism(b):
    391                 for i in index_set:
    392                     c = getattr(b, e_string)([i for k in range(similarity_factor_domain[i])])
    393                     if c is not None:
    394                         d = getattr(morphism(c), f_string)([automorphism(i) for k in range(similarity_factor[i])])
    395                         if d is not None:
    396                             return d
    397                         else:
    398                             raise ValueError, "This is not a morphism!"
    399                         #now we know that b is hw
    400                 return g(b)
    401 
    402         else:
    403             import copy
    404             morphism = copy.copy(g)
    405             known = set( g.keys() )
    406             todo = copy.copy(known)
    407             images = set( [g[x] for x in known] )
    408             # Invariants:
    409             # - images contains all known morphism(x)
    410             # - known contains all elements x for which we know morphism(x)
    411             # - todo  contains all elements x for which we haven't propagated to each child
    412             while todo <> set( [] ):
    413                 x = todo.pop()
    414                 for i in index_set:
    415                     eix  = getattr(x, f_string)([i for k in range(similarity_factor_domain[i])])
    416                     eigx = getattr(morphism[x], f_string)([automorphism(i) for k in range(similarity_factor[i])])
    417                     if bool(eix is None) <> bool(eigx is None):
    418                         # This is not a crystal morphism!
    419                         raise ValueError, "This is not a morphism!" #, print("x="x,"g(x)="g(x),"i="i)
    420                     if (eix is not None) and (eix not in known):
    421                         todo.add(eix)
    422                         known.add(eix)
    423                         morphism[eix] = eigx
    424                         images.add(eigx)
    425             # Check that the module generators are indeed module generators
    426             assert(len(known) == self.cardinality())
    427             # We may not want to systematically run those tests,
    428             # to allow for non bijective crystal morphism
    429             # Add an option CheckBijective?
    430             if not ( len(known) == len(images) and len(images) == images.pop().parent().cardinality() ):
    431                 return(None)
    432             return morphism.__getitem__
    433 
    434         if cached:
    435             return morphism
    436         else:
    437             return CachedFunction(morphism)
    438 
    439     def demazure_character(self, weight, reduced_word = False):
    440         r"""
    441         Calculates the Demazure character associated to the specified weight in the
    442         ambient weight lattice.
    443 
    444         INPUT:
    445             - weight in the weight lattice realization of the crystal (default input)
    446             - when the option reduced_word is set to True, a reduced word can be inputted
    447 
    448         This is currently only supported for crystals whose underlying weight space is
    449         the ambient space.
    450 
    451         References:
    452 
    453         M. Demazure, "Desingularisation des varietes de Schubert",
    454         Ann. E. N. S., Vol. 6, (1974), p. 163-172
    455 
    456         Sarah Mason, "An Explicit Construction of Type A Demazure Atoms",
    457         Journal of Algebraic Combinatorics, Vol. 29, (2009), No. 3, p.295-313
    458         (arXiv:0707.4267)
    459 
    460         EXAMPLES::
    461 
    462             sage: T = CrystalOfTableaux(['A',2], shape = [2,1])
    463             sage: e = T.weight_lattice_realization().basis()
    464             sage: weight = e[0] + 2*e[2]
    465             sage: weight.reduced_word()
    466             [2, 1]
    467             sage: T.demazure_character(weight)
    468             x1^2*x2 + x1^2*x3 + x1*x2^2 + x1*x2*x3 + x1*x3^2
    469 
    470             sage: T = CrystalOfTableaux(['A',3],shape=[2,1])
    471             sage: T.demazure_character([1,2,3], reduced_word = True)
    472             x1^2*x2 + x1^2*x3 + x1*x2^2 + x1*x2*x3 + x2^2*x3
    473 
    474             sage: T = CrystalOfTableaux(['B',2], shape = [2])
    475             sage: e = T.weight_lattice_realization().basis()
    476             sage: weight = -2*e[1]
    477             sage: T.demazure_character(weight)
    478             x1^2 + x1*x2 + x2^2 + x1 + x2 + x1/x2 + 1/x2 + 1/x2^2 + 1
    479         """
    480         if reduced_word:
    481             word = weight
    482         else:
    483             word = weight.reduced_word()
    484         n = self.weight_lattice_realization().n
    485         u = list( self.module_generators )
    486         for i in reversed(word):
    487             u = u + sum((x.demazure_operator(i, truncated = True) for x in u), [])
    488         x = ['x%s'%i for i in range(1,n+1)]
    489         P = PolynomialRing(QQ, x)
    490         u = [b.weight() for b in u]
    491         return sum((prod((x[i]**(la[i]) for i in range(n)), P.one()) for la in u), P.zero()) 
    492 
    493     def digraph(self):
    494         """
    495         Returns the DiGraph associated to self.
    496        
    497         EXAMPLES::
    498        
    499             sage: from sage.combinat.crystals.crystals import Crystal
    500             sage: C = CrystalOfLetters(['A', 5])
    501             sage: Crystal.digraph(C)
    502             Digraph on 6 vertices
    503         """
    504         d = {}
    505         for x in self:
    506             d[x] = {}
    507             for i in self.index_set():
    508                 child = x.f(i)
    509                 if child is None:
    510                     continue
    511                 d[x][child]=i
    512         return DiGraph(d)
    513 
    514     def character(self, R):
    515         """
    516         INPUT: R, a WeylCharacterRing. Produces the character of the
    517         crystal.
    518        
    519         EXAMPLES::
    520        
    521             sage: C = CrystalOfLetters(['A',2])
    522             sage: T = TensorProductOfCrystals(C, C)
    523             sage: A2 = WeylCharacterRing(C.cartan_type()); A2
    524             The Weyl Character Ring of Type ['A', 2] with Integer Ring coefficients
    525             sage: chi = T.character(A2); chi
    526             A2(1,1,0) + A2(2,0,0)
    527             sage: chi.check(verbose = true)
    528             [9, 9]
    529         """
    530         if not R.cartan_type() == self.cartan_type():
    531             raise ValueError, "ring does not have the right Cartan type"
    532         hlist = {}
    533         mlist = {}
    534 
    535         for x in self.highest_weight_vectors():
    536             k = x.weight()
    537             if k in hlist:
    538                 hlist[k] += 1
    539             else:
    540                 hlist[k] = 1
    541         for x in self.list():
    542             k = x.weight()
    543             if k in mlist:
    544                 mlist[k] += 1
    545             else:
    546                 mlist[k] = 1
    547         return WeylCharacter(R, hlist, mlist)
    548 
    549     def latex_file(self, filename):
    550         r"""
    551         Exports a file, suitable for pdflatex, to 'filename'. This requires
    552         a proper installation of dot2tex in sage-python. For more
    553         information see the documentation for self.latex().
    554        
    555         EXAMPLES::
    556        
    557             sage: C = CrystalOfLetters(['A', 5])
    558             sage: C.latex_file('/tmp/test.tex') #optional requires dot2tex
    559         """
    560         header = r"""\documentclass{article}
    561         \usepackage[x11names, rgb]{xcolor}
    562         \usepackage[utf8]{inputenc}
    563         \usepackage{tikz}
    564         \usetikzlibrary{snakes,arrows,shapes}
    565         \usepackage{amsmath}
    566         \usepackage[active,tightpage]{preview}
    567         \newenvironment{bla}{}{}
    568         \PreviewEnvironment{bla}
    569 
    570         \begin{document}
    571         \begin{bla}"""
    572 
    573         footer = r"""\end{bla}
    574         \end{document}"""
    575 
    576         f = open(filename, 'w')
    577         f.write(header + self.latex() + footer)
    578         f.close()
    579  
    580     def latex(self):
    581         r"""
    582         Returns the crystal graph as a bit of latex. This can be exported
    583         to a file with self.latex_file('filename').
    584 
    585         This requires the dot2tex spkg. Here some tips for installation:
    586 
    587         -  Install graphviz = 2.14
    588 
    589         -  Download dot2tex-0.?.spkg from http://wiki.sagemath.org/combinat/FPSAC09/projects
    590 
    591         -  Install pgf-2.00 inside your latex tree. In short:
    592 
    593            -  untaring in /usr/share/texmf/tex/generic
    594 
    595            -  clean out remaining pgf files from older version
    596 
    597            -  run texhash
    598 
    599 
    600         In case LaTeX complains about tikzpicture not being defined,
    601         you may need to further run::
    602 
    603            sage: sage.misc.latex.LATEX_HEADER+=r"\\usepackage{tikz}"
    604 
    605 
    606         EXAMPLES::
    607 
    608             sage: C = CrystalOfLetters(['A', 5])
    609             sage: C.latex()         #optional requires dot2tex
    610             ...
    611             sage: view(C, pdflatex = True, tightpage = True) # optional
    612         """
    613 
    614         try:
    615             from dot2tex.dot2tex import Dot2TikZConv
    616         except ImportError:
    617             print "dot2tex not available.  Install after running \'sage -sh\'"
    618             return
    619 
    620         # In MuPAD, 'autosize' is an option, but this doesn't seem to work here.
    621         options = {'format':'tikz', 'crop':True, 'usepdflatex':True, 'figonly':True}
    622 
    623         content = (Dot2TikZConv(options)).convert(self.dot_tex())
    624 
    625         return content
    626 
    627     _latex_ = latex
    628 
    629     def metapost(self, filename, thicklines=False, labels=True, scaling_factor=1.0, tallness=1.0):
    630         """Use C.metapost("filename.mp",[options])
    631         where options can be:
    632        
    633         thicklines = True (for thicker edges) labels = False (to suppress
    634         labeling of the vertices) scaling_factor=value, where value is a
    635         floating point number, 1.0 by default. Increasing or decreasing the
    636         scaling factor changes the size of the image. tallness=1.0.
    637         Increasing makes the image taller without increasing the width.
    638        
    639         Root operators e(1) or f(1) move along red lines, e(2) or f(2)
    640         along green. The highest weight is in the lower left. Vertices with
    641         the same weight are kept close together. The concise labels on the
    642         nodes are strings introduced by Berenstein and Zelevinsky and
    643         Littelmann; see Littelmann's paper Cones, Crystals, Patterns,
    644         sections 5 and 6.
    645        
    646         For Cartan types B2 or C2, the pattern has the form
    647        
    648         a2 a3 a4 a1
    649        
    650         where c\*a2 = a3 = 2\*a4 =0 and a1=0, with c=2 for B2, c=1 for C2.
    651         Applying e(2) a1 times, e(1) a2 times, e(2) a3 times, e(1) a4 times
    652         returns to the highest weight. (Observe that Littelmann writes the
    653         roots in opposite of the usual order, so our e(1) is his e(2) for
    654         these Cartan types.) For type A2, the pattern has the form
    655        
    656         a3 a2 a1
    657        
    658         where applying e(1) a1 times, e(2) a2 times then e(3) a1 times
    659         returns to the highest weight. These data determine the vertex and
    660         may be translated into a Gelfand-Tsetlin pattern or tableau.
    661        
    662         EXAMPLES::
    663        
    664             sage: C = CrystalOfLetters(['A', 2])
    665             sage: C.metapost('/tmp/test.mp') #optional
    666        
    667         ::
    668        
    669             sage: C = CrystalOfLetters(['A', 5])
    670             sage: C.metapost('/tmp/test.mp')
    671             Traceback (most recent call last):
    672             ...
    673             NotImplementedError
    674         """
    675         # FIXME: those tests are not robust
    676         # Should use instead self.cartan_type() == CartanType(['B',2])
    677         if self.cartan_type()[0] == 'B' and self.cartan_type()[1] == 2:
    678             word = [2,1,2,1]
    679         elif self.cartan_type()[0] == 'C' and self.cartan_type()[1] == 2:
    680             word = [2,1,2,1]
    681         elif self.cartan_type()[0] == 'A' and self.cartan_type()[1] == 2:
    682             word = [1,2,1]
    683         else:
    684             raise NotImplementedError
    685         size = self.cardinality()
    686         string_data = []
    687         for i in range(size):
    688             turtle = self.list()[i]
    689             string_datum = []
    690             for j in word:
    691                 turtlewalk = 0
    692                 while not turtle.e(j) == None:
    693                     turtle = turtle.e(j)
    694                     turtlewalk += 1
    695                 string_datum.append(turtlewalk)
    696             string_data.append(string_datum)
    697 
    698         if self.cartan_type()[0] == 'A':
    699             if labels:
    700                 c0 = int(55*scaling_factor)
    701                 c1 = int(-25*scaling_factor)
    702                 c2 = int(45*tallness*scaling_factor)
    703                 c3 = int(-12*scaling_factor)
    704                 c4 = int(-12*scaling_factor)
    705             else:
    706                 c0 = int(45*scaling_factor)
    707                 c1 = int(-20*scaling_factor)
    708                 c2 = int(35*tallness*scaling_factor)
    709                 c3 = int(12*scaling_factor)
    710                 c4 = int(-12*scaling_factor)
    711             outstring = "verbatimtex\n\\magnification=600\netex\n\nbeginfig(-1);\nsx:=35; sy:=30;\n\nz1000=(%d,0);\nz1001=(%d,%d);\nz1002=(%d,%d);\nz2001=(-3,3);\nz2002=(3,3);\nz2003=(0,-3);\nz2004=(7,0);\nz2005=(0,7);\nz2006=(-7,0);\nz2007=(0,7);\n\n"%(c0,c1,c2,c3,c4)
    712         else:
    713             if labels:
    714                 outstring = "verbatimtex\n\\magnification=600\netex\n\nbeginfig(-1);\n\nsx := %d;\nsy=%d;\n\nz1000=(2*sx,0);\nz1001=(-sx,sy);\nz1002=(-16,-10);\n\nz2001=(0,-3);\nz2002=(-5,3);\nz2003=(0,3);\nz2004=(5,3);\nz2005=(10,1);\nz2006=(0,10);\nz2007=(-10,1);\nz2008=(0,-8);\n\n"%(int(scaling_factor*40),int(tallness*scaling_factor*40))
    715             else:
    716                 outstring = "beginfig(-1);\n\nsx := %d;\nsy := %d;\n\nz1000=(2*sx,0);\nz1001=(-sx,sy);\nz1002=(-5,-5);\n\nz1003=(10,10);\n\n"%(int(scaling_factor*35),int(tallness*scaling_factor*35))
    717         for i in range(size):
    718             if self.cartan_type()[0] == 'A':
    719                 [a1,a2,a3] = string_data[i]
    720             else:
    721                 [a1,a2,a3,a4] = string_data[i]
    722             shift = 0
    723             for j in range(i):
    724                 if self.cartan_type()[0] == 'A':
    725                     [b1,b2,b3] = string_data[j]
    726                     if b1+b3 == a1+a3 and b2 == a2:
    727                         shift += 1
    728                 else:
    729                     [b1,b2,b3,b4] = string_data[j]
    730                     if b1+b3 == a1+a3 and b2+b4 == a2+a4:
    731                         shift += 1
    732             if self.cartan_type()[0] == 'A':
    733                 outstring = outstring +"z%d=%d*z1000+%d*z1001+%d*z1002;\n"%(i,a1+a3,a2,shift)
    734             else:
    735                 outstring = outstring +"z%d=%d*z1000+%d*z1001+%d*z1002;\n"%(i,a1+a3,a2+a4,shift)
    736         outstring = outstring + "\n"   
    737         if thicklines:
    738             outstring = outstring +"pickup pencircle scaled 2\n\n"
    739         for i in range(size):
    740             for j in range(1,3):
    741                 dest = self.list()[i].f(j)
    742                 if not dest == None:
    743                     dest = self.list().index(dest)
    744                     if j == 1:
    745                         col = "red;"
    746                     else:
    747                         col = "green;  "
    748                     if self.cartan_type()[0] == 'A':
    749                         [a1,a2,a3] = string_data[i] # included to facilitate hand editing of the .mp file
    750                         outstring = outstring+"draw z%d--z%d withcolor %s   %% %d %d %d\n"%(i,dest,col,a1,a2,a3)
    751                     else:
    752                         [a1,a2,a3,a4] = string_data[i]
    753                         outstring = outstring+"draw z%d--z%d withcolor %s   %% %d %d %d %d\n"%(i,dest,col,a1,a2,a3,a4)
    754         outstring += "\npickup pencircle scaled 3;\n\n"
    755         for i in range(self.cardinality()):
    756             if labels:
    757                 if self.cartan_type()[0] == 'A':
    758                     outstring = outstring+"pickup pencircle scaled 15;\nfill z%d+z2004..z%d+z2006..z%d+z2006..z%d+z2007..cycle withcolor white;\nlabel(btex %d etex, z%d+z2001);\nlabel(btex %d etex, z%d+z2002);\nlabel(btex %d etex, z%d+z2003);\npickup pencircle scaled .5;\ndraw z%d+z2004..z%d+z2006..z%d+z2006..z%d+z2007..cycle;\n"%(i,i,i,i,string_data[i][2],i,string_data[i][1],i,string_data[i][0],i,i,i,i,i)
    759                 else:
    760                     outstring = outstring+"%%%d %d %d %d\npickup pencircle scaled 1;\nfill z%d+z2005..z%d+z2006..z%d+z2007..z%d+z2008..cycle withcolor white;\nlabel(btex %d etex, z%d+z2001);\nlabel(btex %d etex, z%d+z2002);\nlabel(btex %d etex, z%d+z2003);\nlabel(btex %d etex, z%d+z2004);\npickup pencircle scaled .5;\ndraw z%d+z2005..z%d+z2006..z%d+z2007..z%d+z2008..cycle;\n\n"%(string_data[i][0],string_data[i][1],string_data[i][2],string_data[i][3],i,i,i,i,string_data[i][0],i,string_data[i][1],i,string_data[i][2],i,string_data[i][3],i,i,i,i,i)
    761             else:           
    762                 outstring += "drawdot z%d;\n"%i
    763         outstring += "\nendfig;\n\nend;\n\n"
    764 
    765         f = open(filename, 'w')
    766         f.write(outstring)
    767         f.close()
    768 
    769     def dot_tex(self):
    770         r"""
    771         Returns a dot_tex version of self.
    772        
    773         EXAMPLES::
    774        
    775             sage: C = CrystalOfLetters(['A',2])
    776             sage: C.dot_tex()
    777             'digraph G { \n  node [ shape=plaintext ];\n  N_0 [ label = " ", texlbl = "$1$" ];\n  N_1 [ label = " ", texlbl = "$2$" ];\n  N_2 [ label = " ", texlbl = "$3$" ];\n  N_0 -> N_1 [ label = " ", texlbl = "1" ];\n  N_1 -> N_2 [ label = " ", texlbl = "2" ];\n}'
    778         """
    779         import re
    780         rank = ranker.from_list(self.list())[0]
    781         vertex_key = lambda x: "N_"+str(rank(x))
    782 
    783         # To do: check the regular expression
    784         # Removing %-style comments, newlines, quotes
    785         # This should probably be moved to sage.misc.latex
    786         quoted_latex = lambda x: re.sub("\"|\r|(%[^\n]*)?\n","", latex(x))
    787        
    788         result = "digraph G { \n  node [ shape=plaintext ];\n"
    789 
    790         for x in self:
    791             result += "  " + vertex_key(x) + " [ label = \" \", texlbl = \"$"+quoted_latex(x)+"$\" ];\n"
    792         for x in self:
    793             for i in self.index_set():
    794                 child = x.f(i)
    795                 if child is None:
    796                     continue
    797 #                result += "  " + vertex_key(x) + " -> "+vertex_key(child)+ " [ label = \" \", texlbl = \""+quoted_latex(i)+"\" ];\n"
    798                 if i == 0:
    799                     option = "dir = back, "
    800                     (source, target) = (child, x)
    801                 else:
    802                     option = ""
    803                     (source, target) = (x, child)
    804                 result += "  " + vertex_key(source) + " -> "+vertex_key(target)+ " [ "+option+"label = \" \", texlbl = \""+quoted_latex(i)+"\" ];\n"
    805         result+="}"
    806         return result
    807    
    808     def plot(self, **options):
    809         """
    810         Returns the plot of self as a directed graph.
    811        
    812         EXAMPLES::
    813        
    814             sage: C = CrystalOfLetters(['A', 5])
    815             sage: show_default(False) #do not show the plot by default
    816             sage: C.plot()
    817             Graphics object consisting of 17 graphics primitives
    818         """
    819         return self.digraph().plot(edge_labels=True,vertex_size=0,**options)
    820 
    821 class CrystalElement(Element):
    822     r"""
    823     The abstract class of crystal elements
    824    
    825     Sub classes should implement at least:
    826        
    827     -  x.e(i) (returning `e_i(x)`)
    828    
    829     -  x.f(i) (returning `f_i(x)`)
    830    
    831     -  x.weight()
    832     """
    833 
    834     def index_set(self):
    835         """
    836         EXAMPLES::
    837        
    838             sage: C = CrystalOfLetters(['A',5])
    839             sage: C(1).index_set()
    840             [1, 2, 3, 4, 5]
    841         """
    842         return self.parent().index_set()
    843 
    844     def cartan_type(self):
    845         """
    846         Returns the cartan type associated to self
    847 
    848         EXAMPLES::
    849             sage: C = CrystalOfLetters(['A', 5])
    850             sage: C(1).cartan_type()
    851             ['A', 5]
    852         """
    853         return self.parent().cartan_type()
    854 
    855     def weight(self):
    856         """
    857         EXAMPLES::
    858        
    859             sage: C = CrystalOfLetters(['A',5])
    860             sage: C(1).weight()
    861             (1, 0, 0, 0, 0, 0)
    862         """
    863         return self.Phi() - self.Epsilon()
    864 
    865     def e(self, i):
    866         r"""
    867         Returns `e_i(x)` if it exists or None otherwise. This is
    868         to be implemented by subclasses of CrystalElement.
    869        
    870         TESTS::
    871        
    872             sage: from sage.combinat.crystals.crystals import CrystalElement
    873             sage: C = CrystalOfLetters(['A',5])
    874             sage: CrystalElement.e(C(1), 1)
    875             Traceback (most recent call last):
    876             ...
    877             NotImplementedError
    878         """
    879         raise NotImplementedError
    880 
    881     def f(self, i):
    882         r"""
    883         Returns `f_i(x)` if it exists or None otherwise. This is
    884         to be implemented by subclasses of CrystalElement.
    885        
    886         TESTS::
    887        
    888             sage: from sage.combinat.crystals.crystals import CrystalElement
    889             sage: C = CrystalOfLetters(['A',5])
    890             sage: CrystalElement.f(C(1), 1)
    891             Traceback (most recent call last):
    892             ...
    893             NotImplementedError
    894         """
    895         raise NotImplementedError
    896    
    897     def epsilon(self, i):
    898         r"""
    899         EXAMPLES::
    900        
    901             sage: C = CrystalOfLetters(['A',5])
    902             sage: C(1).epsilon(1)
    903             0
    904             sage: C(2).epsilon(1)
    905             1
    906         """
    907         assert i in self.index_set()
    908         x = self
    909         eps = 0
    910         while True:
    911             x = x.e(i)
    912             if x is None:
    913                 break
    914             eps = eps+1
    915         return eps
    916 
    917     def phi(self, i):
    918         r"""
    919         EXAMPLES::
    920        
    921             sage: C = CrystalOfLetters(['A',5])
    922             sage: C(1).phi(1)
    923             1
    924             sage: C(2).phi(1)
    925             0
    926         """
    927         assert i in self.index_set()
    928         x = self
    929         phi = 0
    930         while True:
    931             x = x.f(i)
    932             if x is None:
    933                 break
    934             phi = phi+1
    935         return phi
    936 
    937     def phi_minus_epsilon(self, i):
    938         """
    939         Returns `\phi_i - \epsilon_i` of self. There are sometimes
    940         better implementations using the weight for this. It is used
    941         for reflections along a string.
    942 
    943         EXAMPLES::
    944 
    945             sage: C = CrystalOfLetters(['A',5])
    946             sage: C(1).phi_minus_epsilon(1)
    947             1
    948         """
    949         return self.phi(i) - self.epsilon(i)
    950 
    951     def Epsilon(self):
    952         """
    953         EXAMPLES::
    954        
    955             sage: C = CrystalOfLetters(['A',5])
    956             sage: C(0).Epsilon()
    957             (0, 0, 0, 0, 0, 0)
    958             sage: C(1).Epsilon()
    959             (0, 0, 0, 0, 0, 0)
    960             sage: C(2).Epsilon()
    961             (1, 0, 0, 0, 0, 0)
    962         """
    963         Lambda = self.parent().Lambda()
    964         return sum(self.epsilon(i) * Lambda[i] for i in self.index_set())
    965 
    966     def Phi(self):
    967         """
    968         EXAMPLES::
    969        
    970             sage: C = CrystalOfLetters(['A',5])
    971             sage: C(0).Phi()
    972             (0, 0, 0, 0, 0, 0)
    973             sage: C(1).Phi()
    974             (1, 0, 0, 0, 0, 0)
    975             sage: C(2).Phi()
    976             (1, 1, 0, 0, 0, 0)
    977         """
    978         Lambda = self.parent().Lambda()
    979         return sum(self.phi(i) * Lambda[i] for i in self.index_set())
    980 
    981     def f_string(self, list):
    982         r"""
    983         Applies `f_{i_r} ... f_{i_1}` to self for `list = [i_1, ..., i_r]`
    984 
    985         EXAMPLES::
    986 
    987             sage: C = CrystalOfLetters(['A',3])
    988             sage: b = C(1)
    989             sage: b.f_string([1,2])
    990             3
    991             sage: b.f_string([2,1])
    992 
    993         """
    994         b = self
    995         for i in list:
    996             b = b.f(i)
    997             if b is None:
    998                 return None
    999         return b
    1000 
    1001     def e_string(self, list):
    1002         r"""
    1003         Applies `e_{i_r} ... e_{i_1}` to self for `list = [i_1, ..., i_r]`
    1004 
    1005         EXAMPLES::
    1006 
    1007             sage: C = CrystalOfLetters(['A',3])
    1008             sage: b = C(3)
    1009             sage: b.e_string([2,1])
    1010             1
    1011             sage: b.e_string([1,2])
    1012 
    1013         """
    1014         b = self
    1015         for i in list:
    1016             b = b.e(i)
    1017             if b is None:
    1018                 return None
    1019         return b
    1020 
    1021     def s(self, i):
    1022         r"""
    1023         Returns the reflection of self along its `i`-string
    1024        
    1025         EXAMPLES::
    1026        
    1027             sage: C = CrystalOfTableaux(['A',2], shape=[2,1])
    1028             sage: b=C(rows=[[1,1],[3]])
    1029             sage: b.s(1)
    1030             [[2, 2], [3]]
    1031             sage: b=C(rows=[[1,2],[3]])
    1032             sage: b.s(2)
    1033             [[1, 2], [3]]
    1034             sage: T=CrystalOfTableaux(['A',2],shape=[4])
    1035             sage: t=T(rows=[[1,2,2,2]])
    1036             sage: t.s(1)
    1037             [[1, 1, 1, 2]]
    1038         """
    1039         d = self.phi_minus_epsilon(i)
    1040         b = self
    1041         if d > 0:
    1042             for j in range(d):
    1043                 b = b.f(i)
    1044         else:
    1045             for j in range(-d):
    1046                 b = b.e(i)
    1047         return b
    1048 
    1049     def demazure_operator(self, i, truncated = False):
    1050         r"""
    1051         Yields all elements one can obtain from self by application of `f_i`.
    1052         If the option "truncated" is set to True, then `self` is not included in the list.
    1053 
    1054         References:
    1055        
    1056         Peter Littelmann, "Crystal graphs and Young tableaux",
    1057         J. Algebra 175 (1995), no. 1, 65--87.
    1058 
    1059         Masaki Kashiwara, "The crystal base and Littelmann's refined Demazure character formula",
    1060         Duke Math. J. 71 (1993), no. 3, 839--858.
    1061 
    1062         EXAMPLES::
    1063        
    1064             sage: T = CrystalOfTableaux(['A',2], shape=[2,1])
    1065             sage: t=T(rows=[[1,2],[2]])
    1066             sage: t.demazure_operator(2)
    1067             [[[1, 2], [2]], [[1, 3], [2]], [[1, 3], [3]]]
    1068             sage: t.demazure_operator(2, truncated = True)
    1069             [[[1, 3], [2]], [[1, 3], [3]]]
    1070             sage: t.demazure_operator(1, truncated = True)
    1071             []
    1072             sage: t.demazure_operator(1)
    1073             [[[1, 2], [2]]]
    1074 
    1075             sage: K = KirillovReshetikhinCrystal(['A',2,1],2,1)
    1076             sage: t = K(rows=[[3],[2]])
    1077             sage: t.demazure_operator(0)
    1078             [[[2, 3]], [[1, 2]]]
    1079         """
    1080         if truncated:
    1081             l = []
    1082         else:
    1083             l = [self]
    1084         for k in range(self.phi(i)):
    1085             l.append(self.f_string([i for j in range(k+1)]))
    1086         return(l)
    1087 
    1088     def is_highest_weight(self, index_set = None):
    1089         r"""
    1090         Returns True if self is a highest weight.
    1091         Specifying the option index_set to be a subset `I` of the index set
    1092         of the underlying crystal, finds all highest weight vectors for arrows in `I`.
    1093        
    1094         EXAMPLES::
    1095        
    1096             sage: C = CrystalOfLetters(['A',5])
    1097             sage: C(1).is_highest_weight()
    1098             True
    1099             sage: C(2).is_highest_weight()
    1100             False
    1101             sage: C(2).is_highest_weight(index_set = [2,3,4,5])
    1102             True
    1103         """
    1104         if index_set is None:
    1105             index_set = self.index_set()
    1106         return all(self.e(i) is None for i in index_set)
    1107 
    1108     def to_highest_weight(self, list = [], index_set = None):
    1109         r"""
    1110         Yields the highest weight element `u` and list `[i_1,...,i_k]`
    1111         such that `self = f_{i_1} ... f_{i_k} u`, where `i_1,...,i_k` are
    1112         elements in `index_set`. By default the index set is assumed to be
    1113         the full index set of self.
    1114 
    1115         EXAMPLES::
    1116            
    1117             sage: T = CrystalOfTableaux(['A',3], shape = [1])
    1118             sage: t = T(rows = [[3]])
    1119             sage: t.to_highest_weight()
    1120             [[[1]], [2, 1]]
    1121             sage: t.to_highest_weight()
    1122             [[[1]], [2, 1]]
    1123             sage: T = CrystalOfTableaux(['A',3], shape = [2,1])
    1124             sage: t = T(rows = [[1,2],[4]])
    1125             sage: t.to_highest_weight()
    1126             [[[1, 1], [2]], [1, 3, 2]]
    1127             sage: t.to_highest_weight(index_set = [3])
    1128             [[[1, 2], [3]], [3]]
    1129         """
    1130         #should assert that self.parent() restricted to index_set is a highest weight crystal
    1131         if index_set is None:
    1132             index_set = self.index_set()
    1133         for i in index_set:
    1134             if self.epsilon(i) <> 0:
    1135                 self = self.e(i)
    1136                 return self.to_highest_weight(list = list + [i], index_set = index_set)
    1137         return [self, list]
    1138 
    1139146
    1140147class CrystalBacktracker(GenericBacktracker):
    1141148    def __init__(self, crystal):
    class CrystalBacktracker(GenericBacktrac 
    1220227
    1221228            # yield y and all elements further below
    1222229            yield y, "n/a", True
    1223 
    1224 
    1225    
    1226 class ClassicalCrystal(Crystal):
    1227     r"""
    1228     The abstract class of classical crystals
    1229     """
    1230 
    1231     def list(self):
    1232         r"""
    1233         Returns the list of the elements of ``self``, as per
    1234         :meth:`FiniteEnumeratedSets.ParentMethods.list`
    1235 
    1236         EXAMPLES::
    1237 
    1238             sage: C = CrystalOfLetters(['D',4])
    1239             sage: C.list()
    1240             [1, 2, 3, 4, -4, -3, -2, -1]
    1241 
    1242         FIXME: this is just there to reinstate the default
    1243         implementation of list which is overriden in
    1244         :class:`Crystals`.
    1245         """
    1246         return self._list_from_iterator()
    1247 
    1248     def __iter__(self):
    1249         r"""
    1250         Returns an iterator over the elements of the crystal.
    1251        
    1252         EXAMPLES::
    1253        
    1254             sage: C = CrystalOfLetters(['A',5])
    1255             sage: [x for x in C]
    1256             [1, 2, 3, 4, 5, 6]
    1257        
    1258         TESTS::
    1259        
    1260             sage: C = CrystalOfLetters(['D',4])
    1261             sage: D = CrystalOfSpinsPlus(['D',4])
    1262             sage: E = CrystalOfSpinsMinus(['D',4])
    1263             sage: T=TensorProductOfCrystals(D,E,generators=[[D.list()[0],E.list()[0]]])
    1264             sage: U=TensorProductOfCrystals(C,E,generators=[[C(1),E.list()[0]]])
    1265             sage: T.cardinality() 
    1266             56
    1267             sage: T.check()
    1268             True
    1269             sage: U.check()
    1270             True
    1271        
    1272         Bump's systematic tests::
    1273        
    1274             sage: fa3 = lambda a,b,c: CrystalOfTableaux(['A',3],shape=[a+b+c,b+c,c])
    1275             sage: fb3 = lambda a,b,c: CrystalOfTableaux(['B',3],shape=[a+b+c,b+c,c])
    1276             sage: fc3 = lambda a,b,c: CrystalOfTableaux(['C',3],shape=[a+b+c,b+c,c])
    1277             sage: fb4 = lambda a,b,c,d: CrystalOfTableaux(['B',4],shape=[a+b+c+d,b+c+d,c+d,d])
    1278             sage: fd4 = lambda a,b,c,d: CrystalOfTableaux(['D',4],shape=[a+b+c+d,b+c+d,c+d,d])
    1279             sage: fd5 = lambda a,b,c,d,e: CrystalOfTableaux(['D',5],shape=[a+b+c+d+e,b+c+d+e,c+d+e,d+e,e])
    1280             sage: def fd4spinplus(a,b,c,d):\
    1281                  C = CrystalOfTableaux(['D',4],shape=[a+b+c+d,b+c+d,c+d,d]);\
    1282                  D = CrystalOfSpinsPlus(['D',4]);\
    1283                  return TensorProductOfCrystals(C,D,generators=[[C[0],D[0]]])
    1284             sage: def fb3spin(a,b,c):\
    1285                  C = CrystalOfTableaux(['B',3],shape=[a+b+c,b+c,c]);\
    1286                  D = CrystalOfSpins(['B',3]);\
    1287                  return TensorProductOfCrystals(C,D,generators=[[C[0],D[0]]])
    1288        
    1289         TODO: choose a good panel of values for a,b,c ... both for basic
    1290         systematic tests and for conditionally run more computationally
    1291         involved tests
    1292        
    1293         ::
    1294        
    1295             sage: fb4(1,0,1,0).check()
    1296             True
    1297        
    1298         ::
    1299        
    1300             #sage: fb4(1,1,1,1).check() # expensive: the crystal is of size 297297
    1301             #True
    1302         """
    1303         return iter(CrystalBacktracker(self))
    1304 
    1305     def _test_fast_iter(self, **options):
    1306         r"""
    1307         For classical crystals, tests whether the elements of list(self) and Crystal.list(self) are
    1308         the same (the two algorithms are different).
    1309 
    1310         EXAMPLES::
    1311        
    1312             sage: C = CrystalOfLetters(['A', 5])
    1313             sage: C._test_fast_iter()
    1314 
    1315         """
    1316         tester = self._tester(**options)
    1317         tester.assert_( list(self).sort() == Crystal.list(self).sort() )
    1318 
    1319     def highest_weight_vectors(self):
    1320         r"""
    1321         Returns a list of the highest weight vectors of self.
    1322        
    1323         EXAMPLES::
    1324        
    1325             sage: C = CrystalOfLetters(['A',5])
    1326             sage: C.highest_weight_vectors()
    1327             [1]
    1328        
    1329         ::
    1330        
    1331             sage: C = CrystalOfLetters(['A',2])
    1332             sage: T = TensorProductOfCrystals(C,C,C,generators=[[C(2),C(1),C(1)],[C(1),C(2),C(1)]])
    1333             sage: T.highest_weight_vectors()
    1334             [[2, 1, 1], [1, 2, 1]]
    1335         """
    1336         # Implementation: selects among the module generators those that are
    1337         # highest weight and cache the result
    1338         try:
    1339             return self._highest_weight_vectors
    1340         except AttributeError:
    1341             pass
    1342 
    1343         self._highest_weight_vectors = [g for g in self.module_generators if g.is_highest_weight()]
    1344         return self._highest_weight_vectors
    1345 
    1346     def highest_weight_vector(self):
    1347         r"""
    1348         Returns the highest weight vector if there is a single one;
    1349         otherwise, raises an error.
    1350        
    1351         EXAMPLES::
    1352        
    1353             sage: C = CrystalOfLetters(['A',5])
    1354             sage: C.highest_weight_vector()
    1355             1
    1356         """
    1357         hw = self.highest_weight_vectors();
    1358         if len(hw) == 1:
    1359             return hw[0]
    1360         else:
    1361             raise RuntimeError("The crystal does not have exactly one highest weight vector")
    1362 
    1363     def cardinality(self):
    1364         r"""
    1365         Returns the number of elements of the crystal, using Weyl's
    1366         dimension formula on each connected component
    1367        
    1368         EXAMPLES::
    1369        
    1370             sage: from sage.combinat.crystals.crystals import ClassicalCrystal
    1371             sage: C = CrystalOfLetters(['A', 5])
    1372             sage: ClassicalCrystal.cardinality(C)
    1373             6
    1374         """
    1375         return sum(self.weight_lattice_realization().weyl_dimension(x.weight())
    1376                    for x in self.highest_weight_vectors())
    1377 
  • sage/combinat/crystals/direct_sum.py

    diff --git a/sage/combinat/crystals/direct_sum.py b/sage/combinat/crystals/direct_sum.py
    a b Direct Sum of Crystals 
    1616#                  http://www.gnu.org/licenses/
    1717#****************************************************************************
    1818
    19 from crystals import CrystalElement, Crystal
     19from sage.structure.parent import Parent
     20from sage.categories.category import Category
    2021from sage.sets.disjoint_union_enumerated_sets import DisjointUnionEnumeratedSets
    2122from sage.structure.element_wrapper import ElementWrapper
    2223
    23 class DirectSumOfCrystals(Crystal, DisjointUnionEnumeratedSets):
     24class DirectSumOfCrystals(DisjointUnionEnumeratedSets):
    2425    r"""
    2526    Direct sum of crystals.
    2627
    class DirectSumOfCrystals(Crystal, Disjo 
    3233     - ``crystals``  -- a list of crystals of the same Cartan type
    3334     - ``keepkey``   -- a boolean
    3435
    35      The option keepkey is by default set to False, assuming that the crystals
    36      are all distinct. In this case the elements of the direct sum are just
    37      represented by the elements in the crystals `B_i`.
    38      If the crystals are not all distinct, one should set the keepkey option to True.
    39      In this case, the elements of the direct sum are represented as tuples
    40     `(i, b)` where `b \in B_i`.
     36    The option ``keepkey`` is by default set to ``False``, assuming
     37    that the crystals are all distinct. In this case the elements of
     38    the direct sum are just represented by the elements in the
     39    crystals `B_i`.  If the crystals are not all distinct, one should
     40    set the ``keepkey`` option to ``True``.  In this case, the
     41    elements of the direct sum are represented as tuples `(i, b)`
     42    where `b \in B_i`.
    4143
    4244    EXAMPLES::
    4345
    class DirectSumOfCrystals(Crystal, Disjo 
    4547        sage: C1 = CrystalOfTableaux(['A',2],shape=[1,1])
    4648        sage: B = DirectSumOfCrystals([C,C1])
    4749        sage: B.list()
    48         [1, 2, [[2], [3]], [[1], [2]], 3, [[1], [3]]]
    49         sage: [b.f(1) for b in B] 
     50        [1, 2, 3, [[1], [2]], [[1], [3]], [[2], [3]]]
     51        sage: [b.f(1) for b in B]
    5052        [2, None, None, None, [[2], [3]], None]
    5153        sage: B.module_generators
    5254        [1, [[1], [2]]]
    5355
     56    ::
     57
    5458        sage: B = DirectSumOfCrystals([C,C], keepkey=True)
    5559        sage: B.list()
    56         [(0, 1), (1, 2), (1, 3), (0, 2), (0, 3), (1, 1)]
     60        [(0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3)]
    5761        sage: B.module_generators
    5862        [(0, 1), (1, 1)]
    5963        sage: b = B( tuple([0,C(1)]) )
    class DirectSumOfCrystals(Crystal, Disjo 
    6367        (1, 0, 0)
    6468    """
    6569
    66     """
    67     Required, because ``DirectSumOfCrystals`` takes the same arguments
    68     as :cls:`DisjointUnionEnumeratedSets` (which see for details).
     70    r"""
     71    The following is required, because :class:`DirectSumOfCrystals`
     72    takes the same arguments as :class:`DisjointUnionEnumeratedSets`
     73    (which see for details).
    6974
    7075    TESTS::
    7176
    class DirectSumOfCrystals(Crystal, Disjo 
    102107            facade = False
    103108        else:
    104109            facade = True
     110        category = Category.meet([Category.join(crystal.categories()) for crystal in crystals])
     111        Parent.__init__(self, category = category)
    105112        DisjointUnionEnumeratedSets.__init__(self, crystals, keepkey = keepkey, facade = facade)
    106113        self.rename("Direct sum of the crystals %s"%(crystals,))
    107114        self._keepkey = keepkey
    class DirectSumOfCrystals(Crystal, Disjo 
    118125            self.module_generators = sum( (list(B.module_generators) for B in crystals), [])
    119126
    120127
    121 class DirectSumOfCrystalsElement(ElementWrapper, CrystalElement):
    122     r"""
    123     A class for elements of direct sums of crystals
    124     """
     128    class Element(ElementWrapper):
     129        r"""
     130        A class for elements of direct sums of crystals
     131        """
    125132
    126     def e(self, i):
    127         r"""
    128         Returns the action of `e_i` on self.
    129        
    130         EXAMPLES::
     133        def e(self, i):
     134            r"""
     135            Returns the action of `e_i` on self.
    131136
    132             sage: C = CrystalOfLetters(['A',2])
    133             sage: B = DirectSumOfCrystals([C,C], keepkey=True)
    134             sage: [[b, b.e(2)] for b in B]
    135             [[(0, 1), None], [(0, 2), None], [(0, 3), (0, 2)], [(1, 1), None], [(1, 2), None], [(1, 3), (1, 2)]]
    136         """
    137         v = self.value
    138         vn = v[1].e(i)
    139         if vn is None:
    140             return None
    141         else:
    142             return self.parent()(tuple([v[0],vn]))
     137            EXAMPLES::
    143138
    144     def f(self, i):
    145         r"""
    146         Returns the action of `f_i` on self.
    147        
    148         EXAMPLES::
     139                sage: C = CrystalOfLetters(['A',2])
     140                sage: B = DirectSumOfCrystals([C,C], keepkey=True)
     141                sage: [[b, b.e(2)] for b in B]
     142                [[(0, 1), None], [(0, 2), None], [(0, 3), (0, 2)], [(1, 1), None], [(1, 2), None], [(1, 3), (1, 2)]]
     143            """
     144            v = self.value
     145            vn = v[1].e(i)
     146            if vn is None:
     147                return None
     148            else:
     149                return self.parent()(tuple([v[0],vn]))
    149150
    150             sage: C = CrystalOfLetters(['A',2])
    151             sage: B = DirectSumOfCrystals([C,C], keepkey=True)
    152             sage: [[b,b.f(1)] for b in B]
    153             [[(0, 1), (0, 2)], [(0, 2), None], [(0, 3), None], [(1, 1), (1, 2)], [(1, 2), None], [(1, 3), None]]
    154         """
    155         v = self.value
    156         vn = v[1].f(i)
    157         if vn is None:
    158             return None
    159         else:
    160             return self.parent()(tuple([v[0],vn]))
     151        def f(self, i):
     152            r"""
     153            Returns the action of `f_i` on self.
    161154
    162     def weight(self):
    163         r"""
    164         Returns the weight of self.
     155            EXAMPLES::
    165156
    166         EXAMPLES::
     157                sage: C = CrystalOfLetters(['A',2])
     158                sage: B = DirectSumOfCrystals([C,C], keepkey=True)
     159                sage: [[b,b.f(1)] for b in B]
     160                [[(0, 1), (0, 2)], [(0, 2), None], [(0, 3), None], [(1, 1), (1, 2)], [(1, 2), None], [(1, 3), None]]
     161            """
     162            v = self.value
     163            vn = v[1].f(i)
     164            if vn is None:
     165                return None
     166            else:
     167                return self.parent()(tuple([v[0],vn]))
    167168
    168             sage: C = CrystalOfLetters(['A',2])
    169             sage: B = DirectSumOfCrystals([C,C], keepkey=True)
    170             sage: b = B( tuple([0,C(2)]) )
    171             sage: b
    172             (0, 2)
    173             sage: b.weight()
    174             (0, 1, 0)
    175         """
    176         return self.value[1].weight()
     169        def weight(self):
     170            r"""
     171            Returns the weight of self.
    177172
    178     def phi(self, i):
    179         r"""
    180         EXAMPLES::
     173            EXAMPLES::
    181174
    182             sage: C = CrystalOfLetters(['A',2])
    183             sage: B = DirectSumOfCrystals([C,C], keepkey=True)
    184             sage: b = B( tuple([0,C(2)]) )
    185             sage: b.phi(2)
    186             1
    187         """
    188         return self.value[1].phi(i)
     175                sage: C = CrystalOfLetters(['A',2])
     176                sage: B = DirectSumOfCrystals([C,C], keepkey=True)
     177                sage: b = B( tuple([0,C(2)]) )
     178                sage: b
     179                (0, 2)
     180                sage: b.weight()
     181                (0, 1, 0)
     182            """
     183            return self.value[1].weight()
    189184
    190     def epsilon(self, i):
    191         r"""
    192         EXAMPLES::
     185        def phi(self, i):
     186            r"""
     187            EXAMPLES::
    193188
    194             sage: C = CrystalOfLetters(['A',2])
    195             sage: B = DirectSumOfCrystals([C,C], keepkey=True)
    196             sage: b = B( tuple([0,C(2)]) )
    197             sage: b.epsilon(2)
    198             0
    199         """
    200         return self.value[1].epsilon(i)
     189                sage: C = CrystalOfLetters(['A',2])
     190                sage: B = DirectSumOfCrystals([C,C], keepkey=True)
     191                sage: b = B( tuple([0,C(2)]) )
     192                sage: b.phi(2)
     193                1
     194            """
     195            return self.value[1].phi(i)
    201196
     197        def epsilon(self, i):
     198            r"""
     199            EXAMPLES::
    202200
    203 DirectSumOfCrystals.Element = DirectSumOfCrystalsElement
     201                sage: C = CrystalOfLetters(['A',2])
     202                sage: B = DirectSumOfCrystals([C,C], keepkey=True)
     203                sage: b = B( tuple([0,C(2)]) )
     204                sage: b.epsilon(2)
     205                0
     206            """
     207            return self.value[1].epsilon(i)
     208
  • sage/combinat/crystals/fast_crystals.py

    diff --git a/sage/combinat/crystals/fast_crystals.py b/sage/combinat/crystals/fast_crystals.py
    a b Fast Rank Two Crystals 
    1919#
    2020#                  http://www.gnu.org/licenses/
    2121#****************************************************************************
    22 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     22
     23from sage.structure.unique_representation import UniqueRepresentation
     24from sage.structure.parent import Parent
     25from sage.categories.classical_crystals import ClassicalCrystals
    2326from sage.structure.element import Element, parent
    2427from sage.combinat.root_system.cartan_type import CartanType
    25 from sage.combinat.crystals.crystals import ClassicalCrystal, CrystalElement
    2628
    2729
    28 class FastCrystal(ClassicalCrystal):
     30class FastCrystal(UniqueRepresentation, Parent):
    2931    """
    3032    An alternative implementation of rank 2 crystals. The root
    3133    operators are implemented in memory by table lookup. This means
    32     that in comparison with the Crystals of Tableaux, these crystals
     34    that in comparison with the CrystalsOfTableaux, these crystals
    3335    are slow to instantiate but faster for computation. Implemented for
    3436    types A2, B2 and C2.
    3537   
    class FastCrystal(ClassicalCrystal): 
    6365        24
    6466        sage: C.cartan_type()
    6567        ['A', 2]
    66         sage: C.check()
    67         True
     68        sage: TestSuite(C).run()
    6869        sage: C = FastCrystal(['B',2],shape=[4,1])
    6970        sage: C.cardinality()
    7071        154
    71         sage: C.check()
    72         True
     72        sage: TestSuite(C).run()
    7373        sage: C = FastCrystal(['B',2],shape=[3/2,1/2])
    7474        sage: C.cardinality()
    7575        16
    76         sage: C.check()
    77         True
     76        sage: TestSuite(C).run()
    7877        sage: C = FastCrystal(['C',2],shape=[2,1])
    7978        sage: C.cardinality()
    8079        16
    8180        sage: C = FastCrystal(['C',2],shape=[3,1])
    8281        sage: C.cardinality()
    8382        35
    84         sage: C.check()
    85         True
     83        sage: TestSuite(C).run()
    8684    """
    8785
    8886    @staticmethod
    class FastCrystal(ClassicalCrystal): 
    112110            The fast crystal for A2 with shape [4,1]
    113111            sage: TestSuite(C).run()
    114112        """
    115         super(FastCrystal, self).__init__(category = FiniteEnumeratedSets())
     113        Parent.__init__(self, category = ClassicalCrystals())
     114#        super(FastCrystal, self).__init__(category = FiniteEnumeratedSets())
    116115        self._cartan_type = ct
    117116        if ct[1] != 2:
    118117            raise NotImplementedError
    class FastCrystal(ClassicalCrystal): 
    162161            l2_str = "%d/2"%int(2*l2)
    163162        self.rename("The fast crystal for %s2 with shape [%s,%s]"%(ct[0],l1_str,l2_str))
    164163        self.module_generators = [self(0)]
    165         self._list = ClassicalCrystal.list(self)
    166         self._digraph = ClassicalCrystal.digraph(self)
     164#        self._list = ClassicalCrystal.list(self)
     165        self._list = super(FastCrystal, self).list()
     166#        self._digraph = ClassicalCrystal.digraph(self)
     167        self._digraph = super(FastCrystal, self).digraph()
    167168        self._digraph_closure = self.digraph().transitive_closure()
    168169
    169170    def _type_a_init(self, l1, l2):
    class FastCrystal(ClassicalCrystal): 
    302303        else:
    303304            return 0
    304305
    305 class FastCrystalElement(CrystalElement):
    306     def __init__(self, parent, value, format):
    307         """
    308         EXAMPLES::
    309        
    310             sage: C = FastCrystal(['A',2],shape=[2,1])
    311             sage: c = C(0); c
    312             [0, 0, 0]
    313             sage: C[0].parent()
    314             The fast crystal for A2 with shape [2,1]
    315             sage: TestSuite(c).run()
    316         """
    317         Element.__init__(self, parent)
    318         self.value = value
    319         self.format = format
     306    class Element(Element):
     307        def __init__(self, parent, value, format):
     308            """
     309            EXAMPLES::
    320310
    321     def weight(self):
    322         """
    323         Returns the weight of self.
    324        
    325         EXAMPLES::
    326        
    327             sage: [v.weight() for v in FastCrystal(['A',2], shape=[2,1])]
    328             [(2, 1, 0), (1, 2, 0), (1, 1, 1), (1, 0, 2), (0, 1, 2), (2, 0, 1), (1, 1, 1), (0, 2, 1)]
    329             sage: [v.weight() for v in FastCrystal(['B',2], shape=[1,0])]
    330             [(1, 0), (0, 1), (0, 0), (0, -1), (-1, 0)]
    331             sage: [v.weight() for v in FastCrystal(['B',2], shape=[1/2,1/2])]
    332             [(1/2, 1/2), (1/2, -1/2), (-1/2, 1/2), (-1/2, -1/2)]
    333             sage: [v.weight() for v in FastCrystal(['C',2], shape=[1,0])]
    334             [(1, 0), (0, 1), (0, -1), (-1, 0)]
    335             sage: [v.weight() for v in FastCrystal(['C',2], shape=[1,1])]
    336             [(1, 1), (1, -1), (0, 0), (-1, 1), (-1, -1)]
    337         """
    338         delpat = self.parent().delpat[self.value]
    339         if self.parent()._cartan_type[0] == 'A':
    340             delpat = delpat + [0,]
    341         [alpha1, alpha2] = self.parent().weight_lattice_realization().simple_roots()
    342         hwv = sum(self.parent().shape[i]*self.parent().weight_lattice_realization().monomial(i) for i in range(2))
    343         return hwv - (delpat[0]+delpat[2])*alpha1 - (delpat[1]+delpat[3])*alpha2
    344        
    345     def __repr__(self):
    346         """
    347         EXAMPLES::
    348        
    349             sage: C = FastCrystal(['A',2],shape=[2,1])
    350             sage: C[0].__repr__()
    351             '[0, 0, 0]'
    352         """
    353         if self.format == "string":
    354             return repr(self.parent().delpat[self.value])
    355         elif self.format == "dual_string":
    356             return repr(self.parent().gampat[self.value])
    357         elif self.format == "simple":
    358             return repr(self.value)
    359         else:
    360             raise NotImplementedError
     311                sage: C = FastCrystal(['A',2],shape=[2,1])
     312                sage: c = C(0); c
     313                [0, 0, 0]
     314                sage: C[0].parent()
     315                The fast crystal for A2 with shape [2,1]
     316                sage: TestSuite(c).run()
     317            """
     318            Element.__init__(self, parent)
     319            self.value = value
     320            self.format = format
    361321
    362     def __eq__(self, other):
    363         """
    364         EXAMPLES::
    365        
    366             sage: C = FastCrystal(['A',2],shape=[2,1])
    367             sage: D = FastCrystal(['B',2],shape=[2,1])
    368             sage: C(0) == C(0)
    369             True
    370             sage: C(1) == C(0)
    371             False
    372             sage: C(0) == D(0)
    373             False
    374         """
    375         return self.__class__ is other.__class__ and \
    376                self.parent() == other.parent() and \
    377                self.value == other.value
     322        def weight(self):
     323            """
     324            Returns the weight of self.
    378325
    379     def __ne__(self, other):
    380         """
    381         EXAMPLES::
     326            EXAMPLES::
    382327
    383             sage: C = FastCrystal(['A',2],shape=[2,1])
    384             sage: D = FastCrystal(['B',2],shape=[2,1])
    385             sage: C(0) != C(0)
    386             False
    387             sage: C(1) != C(0)
    388             True
    389             sage: C(0) != D(0)
    390             True
    391         """
    392         return not self == other
     328                sage: [v.weight() for v in FastCrystal(['A',2], shape=[2,1])]
     329                [(2, 1, 0), (1, 2, 0), (1, 1, 1), (1, 0, 2), (0, 1, 2), (2, 0, 1), (1, 1, 1), (0, 2, 1)]
     330                sage: [v.weight() for v in FastCrystal(['B',2], shape=[1,0])]
     331                [(1, 0), (0, 1), (0, 0), (0, -1), (-1, 0)]
     332                sage: [v.weight() for v in FastCrystal(['B',2], shape=[1/2,1/2])]
     333                [(1/2, 1/2), (1/2, -1/2), (-1/2, 1/2), (-1/2, -1/2)]
     334                sage: [v.weight() for v in FastCrystal(['C',2], shape=[1,0])]
     335                [(1, 0), (0, 1), (0, -1), (-1, 0)]
     336                sage: [v.weight() for v in FastCrystal(['C',2], shape=[1,1])]
     337                [(1, 1), (1, -1), (0, 0), (-1, 1), (-1, -1)]
     338            """
     339            delpat = self.parent().delpat[self.value]
     340            if self.parent()._cartan_type[0] == 'A':
     341                delpat = delpat + [0,]
     342            [alpha1, alpha2] = self.parent().weight_lattice_realization().simple_roots()
     343            hwv = sum(self.parent().shape[i]*self.parent().weight_lattice_realization().monomial(i) for i in range(2))
     344            return hwv - (delpat[0]+delpat[2])*alpha1 - (delpat[1]+delpat[3])*alpha2
    393345
     346        def _repr_(self):
     347            """
     348            EXAMPLES::
    394349
    395     def __cmp__(self, other):
    396         """
    397         EXAMPLES::
    398        
    399             sage: C = FastCrystal(['A',2],shape=[2,1])
    400             sage: C(1) < C(2)
    401             True
    402             sage: C(2) < C(1)
    403             False
    404             sage: C(2) > C(1)
    405             True
    406             sage: C(1) <= C(1)
    407             True
    408         """
    409         if type(self) is not type(other):
    410             return cmp(type(self), type(other))
    411         if self.parent() != other.parent():
    412             return cmp(self.parent(), other.parent())
    413         return self.parent().cmp_elements(self, other)
     350                sage: C = FastCrystal(['A',2],shape=[2,1])
     351                sage: C[0]._repr_()
     352                '[0, 0, 0]'
     353            """
     354            if self.format == "string":
     355                return repr(self.parent().delpat[self.value])
     356            elif self.format == "dual_string":
     357                return repr(self.parent().gampat[self.value])
     358            elif self.format == "simple":
     359                return repr(self.value)
     360            else:
     361                raise NotImplementedError
    414362
    415     def e(self, i):
    416         """
    417         Returns the action of `e_i` on self.
    418        
    419         EXAMPLES::
    420        
    421             sage: C = FastCrystal(['A',2],shape=[2,1])
    422             sage: C(1).e(1)
    423             [0, 0, 0]
    424             sage: C(0).e(1) is None
    425             True
    426         """
    427         assert i in self.index_set()
    428         if i == 1:
    429             r = self.parent()._rootoperators[self.value][0]
    430         else:
    431             r = self.parent()._rootoperators[self.value][2]
    432         return self.parent()(r) if r is not None else None
     363        def __eq__(self, other):
     364            """
     365            EXAMPLES::
    433366
     367                sage: C = FastCrystal(['A',2],shape=[2,1])
     368                sage: D = FastCrystal(['B',2],shape=[2,1])
     369                sage: C(0) == C(0)
     370                True
     371                sage: C(1) == C(0)
     372                False
     373                sage: C(0) == D(0)
     374                False
     375            """
     376            return self.__class__ is other.__class__ and \
     377                   self.parent() == other.parent() and \
     378                   self.value == other.value
    434379
    435     def f(self, i):
    436         """
    437         Returns the action of `f_i` on self.
    438        
    439         EXAMPLES::
    440        
    441             sage: C = FastCrystal(['A',2],shape=[2,1])
    442             sage: C(6).f(1)
    443             [1, 2, 1]
    444             sage: C(7).f(1) is None
    445             True
    446         """
    447         assert i in self.index_set()
    448         if i == 1:
    449             r = self.parent()._rootoperators[self.value][1]
    450         else:
    451             r = self.parent()._rootoperators[self.value][3]
    452         return self.parent()(r) if r is not None else None           
     380        def __ne__(self, other):
     381            """
     382            EXAMPLES::
    453383
     384                sage: C = FastCrystal(['A',2],shape=[2,1])
     385                sage: D = FastCrystal(['B',2],shape=[2,1])
     386                sage: C(0) != C(0)
     387                False
     388                sage: C(1) != C(0)
     389                True
     390                sage: C(0) != D(0)
     391                True
     392            """
     393            return not self == other
    454394
    455 FastCrystal.Element = FastCrystalElement
     395
     396        def __cmp__(self, other):
     397            """
     398            EXAMPLES::
     399
     400                sage: C = FastCrystal(['A',2],shape=[2,1])
     401                sage: C(1) < C(2)
     402                True
     403                sage: C(2) < C(1)
     404                False
     405                sage: C(2) > C(1)
     406                True
     407                sage: C(1) <= C(1)
     408                True
     409            """
     410            if type(self) is not type(other):
     411                return cmp(type(self), type(other))
     412            if self.parent() != other.parent():
     413                return cmp(self.parent(), other.parent())
     414            return self.parent().cmp_elements(self, other)
     415
     416        def e(self, i):
     417            """
     418            Returns the action of `e_i` on self.
     419
     420            EXAMPLES::
     421
     422                sage: C = FastCrystal(['A',2],shape=[2,1])
     423                sage: C(1).e(1)
     424                [0, 0, 0]
     425                sage: C(0).e(1) is None
     426                True
     427            """
     428            assert i in self.index_set()
     429            if i == 1:
     430                r = self.parent()._rootoperators[self.value][0]
     431            else:
     432                r = self.parent()._rootoperators[self.value][2]
     433            return self.parent()(r) if r is not None else None
     434
     435
     436        def f(self, i):
     437            """
     438            Returns the action of `f_i` on self.
     439
     440            EXAMPLES::
     441
     442                sage: C = FastCrystal(['A',2],shape=[2,1])
     443                sage: C(6).f(1)
     444                [1, 2, 1]
     445                sage: C(7).f(1) is None
     446                True
     447            """
     448            assert i in self.index_set()
     449            if i == 1:
     450                r = self.parent()._rootoperators[self.value][1]
     451            else:
     452                r = self.parent()._rootoperators[self.value][3]
     453            return self.parent()(r) if r is not None else None           
     454
     455
     456#FastCrystal.Element = FastCrystalElement
  • sage/combinat/crystals/highest_weight_crystals.py

    diff --git a/sage/combinat/crystals/highest_weight_crystals.py b/sage/combinat/crystals/highest_weight_crystals.py
    a b Highest weight crystals 
    1717#                  http://www.gnu.org/licenses/
    1818#****************************************************************************
    1919
    20 import operator
    21 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
     20from sage.categories.classical_crystals import ClassicalCrystals
     21from sage.structure.parent import Parent
    2222from sage.combinat.root_system.cartan_type import CartanType
    23 from sage.combinat.crystals.crystals import Crystal, ClassicalCrystal, CrystalElement
    2423from sage.combinat.crystals.letters import CrystalOfLetters
    25 from sage.combinat.crystals.tensor_product import TensorProductOfCrystals, TensorProductOfCrystalsElement, CrystalOfTableaux, CrystalOfWords
    26 from sage.combinat.partition import Partition, Partitions
     24from sage.combinat.crystals.tensor_product import TensorProductOfCrystals, CrystalOfWords
    2725
    2826
    2927def HighestWeightCrystal(dominant_weight):
    def HighestWeightCrystal(dominant_weight 
    9492    else:
    9593        raise NotImplementedError
    9694
    97 class FiniteDimensionalHighestWeightCrystal_TypeE(CrystalOfWords, ClassicalCrystal):
     95class FiniteDimensionalHighestWeightCrystal_TypeE(CrystalOfWords):
    9896    """
    9997    Commonalities for all finite dimensional type E highest weight crystals
    10098
    class FiniteDimensionalHighestWeightCrys 
    123121        self._highest_weight = dominant_weight
    124122        assert dominant_weight.is_dominant()
    125123        self.rename("Finite dimensional highest weight crystal of type %s and highest weight %s"%(self._cartan_type, dominant_weight))
    126         super(FiniteDimensionalHighestWeightCrystal_TypeE, self).__init__(category = FiniteEnumeratedSets())
     124        Parent.__init__(self, category = ClassicalCrystals())
    127125        self.module_generators = [self.module_generator()]
    128126
    129127
  • sage/combinat/crystals/kirillov_reshetikhin.py

    diff --git a/sage/combinat/crystals/kirillov_reshetikhin.py b/sage/combinat/crystals/kirillov_reshetikhin.py
    a b Affine crystals 
    2222
    2323from sage.misc.cachefunc import cached_method
    2424from sage.combinat.combinat import CombinatorialObject
     25from sage.structure.parent import Parent
     26from sage.categories.finite_crystals import FiniteCrystals
    2527from sage.rings.integer import Integer
    2628from sage.misc.functional import is_even, is_odd
    27 from sage.combinat.crystals.affine import AffineCrystal, AffineCrystalFromClassical, AffineCrystalFromClassicalElement
     29from sage.combinat.crystals.affine import AffineCrystalFromClassical, AffineCrystalFromClassicalElement
    2830from sage.combinat.crystals.affine import AffineCrystalFromClassicalAndPromotion
    2931from sage.combinat.crystals.highest_weight_crystals import HighestWeightCrystal
    3032from sage.combinat.crystals.direct_sum import DirectSumOfCrystals
    3133from sage.combinat.root_system.cartan_type import CartanType
    32 from sage.combinat.crystals.tensor_product import CrystalOfTableaux, TensorProductOfCrystalsElement, TensorProductOfCrystals
     34from sage.combinat.crystals.tensor_product import CrystalOfTableaux, TensorProductOfCrystals
    3335from sage.combinat.tableau import Tableau
    3436from sage.combinat.partition import Partition, Partitions
    3537from sage.combinat.integer_vector import IntegerVectors
    def KirillovReshetikhinCrystal(cartan_ty 
    150152            raise NotImplementedError
    151153
    152154
    153 class KirillovReshetikhinGenericCrystal(AffineCrystal):
     155class KirillovReshetikhinGenericCrystal(Parent):
    154156    r"""
    155157    Generic class for Kirillov-Reshetikhin crystal `B^{r,s}` of the given type.
    156158
    class KirillovReshetikhinGenericCrystal( 
    170172            sage: K.s()
    171173            1
    172174        """
     175        Parent.__init__(self, category = FiniteCrystals())
    173176        self._cartan_type = cartan_type
    174177        self._r = r
    175178        self._s = s
    class KirillovReshetikhinGenericCrystal( 
    233236
    234237    def R_matrix(self, K):
    235238        r"""
    236         Returns the combinatorial `R`-matrix from `self \otimes K \to K \otimes self`,
    237         where `K` is a Kirillov-Reshetikhin crystal of the same type as `self`.
    238         The combinatorial `R`-matrix is the affine crystal isomorphism which maps
    239         `u_{self} \otimes u_K` to `u_K \otimes u_{\self}`, where `u_K` is the unique
    240         element in `K = B^{r,s}` of weight `s\Lambda_r - s c \Lambda_0` (see module_generator).
     239        INPUT:
     240
     241         - ``self`` -- a crystal `L`
     242         - ``K`` -- a Kirillov-Reshetikhin crystal of the same type as `L`.
     243
     244        Returns the *combinatorial `R`-matrix* from `L \otimes K \to K
     245        \otimes L`, where the combinatorial `R`-matrix is the affine
     246        crystal isomorphism which maps `u_{L} \otimes u_K` to `u_K
     247        \otimes u_{L}`, where `u_K` is the unique element in `K =
     248        B^{r,s}` of weight `s\Lambda_r - s c \Lambda_0` (see
     249        module_generator).
    241250
    242251        EXAMPLES::
    243252
    KR_type_C.Element = KR_type_CElement 
    10361045class KR_type_box(KirillovReshetikhinGenericCrystal, AffineCrystalFromClassical):
    10371046    r"""
    10381047    Class of Kirillov-Reshetikhin crystals `B^{r,s}` of type `A_{2n}^{(2)}` for `r\le n`
    1039     and type `D_{n+1}^{(2)}` for `r<n'.
     1048    and type `D_{n+1}^{(2)}` for `r<n`.
    10401049
    10411050    EXAMPLES::
    10421051
    class KR_type_box(KirillovReshetikhinGen 
    10511060    """
    10521061    def __init__(self, cartan_type, r, s):
    10531062        r"""
    1054         Initializes a Kirillov-Reshetikhin crystal self.
     1063        Initializes a Kirillov-Reshetikhin crystal ``self``.
    10551064
    10561065        TESTS::
    10571066
  • sage/combinat/crystals/letters.py

    diff --git a/sage/combinat/crystals/letters.py b/sage/combinat/crystals/letters.py
    a b from sage.structure.unique_representatio 
    2424from sage.structure.element import parent
    2525from sage.structure.parent import Parent
    2626from sage.structure.element_wrapper import ElementWrapper
    27 from sage.categories.all import FiniteEnumeratedSets
     27from sage.categories.enumerated_sets import EnumeratedSets
     28from sage.categories.classical_crystals import ClassicalCrystals
    2829from sage.combinat.root_system.cartan_type import CartanType
    29 from crystals import ClassicalCrystal, CrystalElement
     30
    3031
    3132def CrystalOfLetters(cartan_type, element_print_style = None, dual = None):
    3233    r"""
    def CrystalOfLetters(cartan_type, elemen 
    110111        raise NotImplementedError
    111112
    112113
    113 class ClassicalCrystalOfLetters(ClassicalCrystal):
     114class ClassicalCrystalOfLetters(UniqueRepresentation, Parent):
    114115    r"""
    115116    A generic class for classical crystals of letters.
    116117   
    117118    All classical crystals of letters should be instances of this class
    118119    or of subclasses. To define a new crystal of letters, one only
    119120    needs to implement a class for the elements (which subclasses
    120     Letter and CrystalElement), with appropriate e and f operations. If
     121    Letter), with appropriate e and f operations. If
    121122    the module generator is not 1, one also needs to define the
    122123    subclass ClassicalCrystalOfLetters for the crystal itself.
    123124   
    class ClassicalCrystalOfLetters(Classica 
    133134
    134135            sage: C = CrystalOfLetters(['A',5])
    135136            sage: C.category()
    136             Category of finite enumerated sets
     137            Category of classical crystals
    137138            sage: TestSuite(C).run()
    138139        """
     140        Parent.__init__(self, category = ClassicalCrystals())
    139141        self._cartan_type = CartanType(cartan_type)
    140142        self.rename("The crystal of letters for type %s"%cartan_type)
    141143        self.Element = element_class
    142         Parent.__init__(self, category = FiniteEnumeratedSets()) #, category = ClassicalCrystals()
    143144        if cartan_type == CartanType(['E',6]):
    144145            if dual:
    145146                self.module_generators = [self([6])]
    class ClassicalCrystalOfLetters(Classica 
    151152            self.module_generators = [self([7])]
    152153        else:
    153154            self.module_generators = [self(1)]
    154         self._list = ClassicalCrystal.list(self)
     155        self._list = super(ClassicalCrystalOfLetters, self).list()
    155156        self._element_print_style = element_print_style
    156         self._digraph = ClassicalCrystal.digraph(self)
     157        self._digraph = super(ClassicalCrystalOfLetters, self).digraph()
    157158        self._digraph_closure = self.digraph().transitive_closure()
    158159
    159160    def __call__(self, value):
    class ClassicalCrystalOfLetters(Classica 
    242243            return True
    243244        return False
    244245
     246    # temporary woraround while an_element is overriden by Parent
     247    _an_element_ = EnumeratedSets.ParentMethods._an_element_
     248
    245249# Utility. Note: much of this class should be factored out at some point!
    246250class Letter(ElementWrapper):
    247251    r"""
    class Letter(ElementWrapper): 
    257261        sage: Letter(ZZ, 1).parent()
    258262        Integer Ring
    259263
    260         sage: Letter(ZZ, 1).__repr__()
     264        sage: Letter(ZZ, 1)._repr_()
    261265        '1'
    262266
    263267        sage: parent1 = ZZ  # Any fake value ...
    class Letter(ElementWrapper): 
    378382# Type A
    379383#########################
    380384
    381 class Crystal_of_letters_type_A_element(Letter, CrystalElement):
     385class Crystal_of_letters_type_A_element(Letter):
    382386    r"""
    383387    Type A crystal of letters elements
    384388   
    class Crystal_of_letters_type_A_element( 
    401405   
    402406    ::
    403407   
    404         sage: C.check()
    405         True
     408        sage: TestSuite(C).run()
    406409    """
    407410   
    408411    def weight(self):
    class Crystal_of_letters_type_A_element( 
    452455# Type B
    453456#########################
    454457
    455 class Crystal_of_letters_type_B_element(Letter, CrystalElement):
     458class Crystal_of_letters_type_B_element(Letter):
    456459    r"""
    457460    Type B crystal of letters elements
    458461   
    459462    TESTS::
    460463   
    461464        sage: C = CrystalOfLetters (['B',3])
    462         sage: C.check()
    463         True
     465        sage: TestSuite(C).run()
    464466    """
    465467   
    466468    def weight(self):
    class Crystal_of_letters_type_B_element( 
    549551# Type C
    550552#########################
    551553
    552 class Crystal_of_letters_type_C_element(Letter, CrystalElement):
     554class Crystal_of_letters_type_C_element(Letter):
    553555    r"""
    554556    Type C crystal of letters elements
    555557   
    class Crystal_of_letters_type_C_element( 
    565567         [False, False, False, False, True, True],
    566568         [False, False, False, False, False, True],
    567569         [False, False, False, False, False, False]]
    568         sage: C.check()
    569         True
     570        sage: TestSuite(C).run()
    570571    """
    571572   
    572573    def weight(self):
    class Crystal_of_letters_type_C_element( 
    637638# Type D
    638639#########################
    639640
    640 class Crystal_of_letters_type_D_element(Letter, CrystalElement):
     641class Crystal_of_letters_type_D_element(Letter):
    641642    r"""
    642643    Type D crystal of letters elements
    643644   
    class Crystal_of_letters_type_D_element( 
    646647        sage: C = CrystalOfLetters(['D',4])
    647648        sage: C.list()
    648649        [1, 2, 3, 4, -4, -3, -2, -1]
    649         sage: C.check()
    650         True
     650        sage: TestSuite(C).run()
    651651    """
    652652
    653653    def weight(self):
    class Crystal_of_letters_type_D_element( 
    743743# Type G2
    744744#########################
    745745
    746 class Crystal_of_letters_type_G_element(Letter, CrystalElement):
     746class Crystal_of_letters_type_G_element(Letter):
    747747    r"""
    748748    Type G2 crystal of letters elements
    749749   
    class Crystal_of_letters_type_G_element( 
    752752        sage: C = CrystalOfLetters(['G',2])
    753753        sage: C.list()
    754754        [1, 2, 3, 0, -3, -2, -1]
    755         sage: C.check()
    756         True
     755        sage: TestSuite(C).run()
    757756    """
    758757   
    759758    def weight(self):
    class Crystal_of_letters_type_G_element( 
    857856# Type E6
    858857#########################
    859858
    860 class Crystal_of_letters_type_E6_element(Letter, CrystalElement):
     859class Crystal_of_letters_type_E6_element(Letter):
    861860    r"""
    862861    Type `E_6` crystal of letters elements. This crystal corresponds to the highest weight
    863862    crystal `B(\Lambda_1)`.
    class Crystal_of_letters_type_E6_element 
    872871        [-4, 3, 6], [-3, 1, 6], [-1, 6], [-6, 2], [-2, -6, 4], [-4, -6, 3, 5],
    873872        [-3, -6, 1, 5], [-1, -6, 5], [-5, 3], [-3, -5, 1, 4], [-1, -5, 4], [-4, 1, 2],
    874873        [-1, -4, 2, 3], [-3, 2], [-2, -3, 4], [-4, 5], [-5, 6], [-6], [-2, 1], [-1, -2, 3]]
    875         sage: C.check()
    876         True
     874        sage: TestSuite(C).run()
    877875        sage: all(b.f(i).e(i) == b for i in C.index_set() for b in C if b.f(i) is not None)
    878876        True
    879877        sage: all(b.e(i).f(i) == b for i in C.index_set() for b in C if b.e(i) is not None)
    class Crystal_of_letters_type_E6_element 
    885883    def __hash__(self):
    886884        return hash(tuple(self.value))
    887885
    888     def __repr__(self):
     886    def _repr_(self):
    889887        """
    890888        In their full representation, the vertices of this crystal are labeled
    891889        by their weight. For example vertex [-5,2,6] indicates that a 5-arrow
    class Crystal_of_letters_type_E6_element 
    11221120        else:
    11231121            return None
    11241122
    1125 class Crystal_of_letters_type_E6_element_dual(Letter, CrystalElement):
     1123class Crystal_of_letters_type_E6_element_dual(Letter):
    11261124    r"""
    11271125    Type `E_6` crystal of letters elements. This crystal corresponds to the highest weight
    11281126    crystal `B(\Lambda_6)`. This crystal is dual to `B(\Lambda_1)` of type `E_6`.
    class Crystal_of_letters_type_E6_element 
    11391137        [4, -1, -2], [1, 5, -4], [3, 5, -1, -4], [5, -3], [1, 6, -5], [3, 6, -1, -5], [4, 6, -3, -5],
    11401138        [2, 6, -4], [6, -2], [1, -6], [3, -1, -6], [4, -3, -6], [2, 5, -4, -6], [5, -2, -6], [2, -5],
    11411139        [4, -2, -5], [3, -4], [1, -3], [-1]]
    1142         sage: C.check()
    1143         True
     1140        sage: TestSuite(C).run()
    11441141        sage: all(b.f(i).e(i) == b for i in C.index_set() for b in C if b.f(i) is not None)
    11451142        True
    11461143        sage: all(b.e(i).f(i) == b for i in C.index_set() for b in C if b.e(i) is not None)
    class Crystal_of_letters_type_E6_element 
    11491146        sage: G.show(edge_labels=true, figsize=12, vertex_size=1)
    11501147    """
    11511148
    1152     def __repr__(self):
     1149    def _repr_(self):
    11531150        """
    11541151        In their full representation, the vertices of this crystal are labeled
    11551152        by their weight. For example vertex [-2,1] indicates that a 2-arrow
    class Crystal_of_letters_type_E6_element 
    12751272# Type E7
    12761273#########################
    12771274
    1278 class Crystal_of_letters_type_E7_element(Letter, CrystalElement):
     1275class Crystal_of_letters_type_E7_element(Letter):
    12791276    r"""
    12801277    Type `E_7` crystal of letters elements. This crystal corresponds to the highest weight
    12811278    crystal `B(\Lambda_7)`.
    class Crystal_of_letters_type_E7_element 
    12961293        2], [-2, -6, 4], [-6, -4, 5, 3], [-3, -6, 1, 5], [-6, -1, 5], [-5, 3],
    12971294        [-3, -5, 4, 1], [-5, -1, 4], [-4, 1, 2], [-1, -4, 3, 2], [-3, 2], [-2,
    12981295        -3, 4], [-4, 5], [-5, 6], [-6, 7], [-7], [-2, 1], [-2, -1, 3]]
    1299         sage: C.check()
    1300         True
     1296        sage: TestSuite(C).run()
    13011297        sage: all(b.f(i).e(i) == b for i in C.index_set() for b in C if b.f(i) is not None)
    13021298        True
    13031299        sage: all(b.e(i).f(i) == b for i in C.index_set() for b in C if b.e(i) is not None)
  • sage/combinat/crystals/spins.py

    diff --git a/sage/combinat/crystals/spins.py b/sage/combinat/crystals/spins.py
    a b internal_repn and signature of the cryst 
    3838
    3939## TODO: proper latex'ing of spins
    4040
     41from sage.structure.unique_representation import UniqueRepresentation
     42from sage.structure.parent import Parent
     43from sage.categories.classical_crystals import ClassicalCrystals
    4144from sage.combinat.crystals.letters import Letter
    42 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets
    4345from sage.combinat.root_system.cartan_type import CartanType
    44 from crystals import ClassicalCrystal, CrystalElement
    4546
    4647
    4748#########################
    def CrystalOfSpinsPlus(ct): 
    131132   
    132133    TESTS::
    133134   
    134         sage: D.check()
    135         True
     135        sage: TestSuite(D).run()
    136136    """
    137137    ct = CartanType(ct)
    138138    if ct[0] == 'D':
    def CrystalOfSpinsMinus(ct): 
    182182    else:
    183183        raise NotImplementedError
    184184
    185 class GenericCrystalOfSpins(ClassicalCrystal):
     185class GenericCrystalOfSpins(UniqueRepresentation, Parent):
    186186    def __init__(self, ct, element_class, case):
    187187        """
    188188        EXAMPLES::
    class GenericCrystalOfSpins(ClassicalCry 
    199199            self.rename("The minus crystal of spins for type %s"%ct)
    200200
    201201        self.Element = element_class
    202         super(GenericCrystalOfSpins, self).__init__(category = FiniteEnumeratedSets())
     202#        super(GenericCrystalOfSpins, self).__init__(category = FiniteEnumeratedSets())
     203        Parent.__init__(self, category = ClassicalCrystals())
    203204
    204205        if case == "minus":
    205206            generator = [1]*(ct[1]-1)
    class GenericCrystalOfSpins(ClassicalCry 
    208209            generator = [1]*ct[1]
    209210        self.module_generators = [self(generator)]
    210211        self._list = list(self)
    211         self._digraph = ClassicalCrystal.digraph(self)
     212#        self._digraph = ClassicalCrystal.digraph(self)
     213        self._digraph = super(GenericCrystalOfSpins, self).digraph()
    212214        self._digraph_closure = self.digraph().transitive_closure()
    213215
    214216    def __call__(self, value):
    class Spin(Letter): 
    291293        The crystal of spins for type ['B', 3]
    292294
    293295        sage: c = C([1,1,1])
    294         sage: c.__repr__()
     296        sage: c._repr_()
    295297        '[1, 1, 1]'
    296298
    297299        sage: D = CrystalOfSpins(['B',4])
    class Spin(Letter): 
    335337            sword += "+" if self.value[x] == 1 else "-"
    336338        return sword
    337339
    338 class Spin_crystal_type_B_element(Spin, CrystalElement):
     340class Spin_crystal_type_B_element(Spin):
    339341    r"""
    340342    Type B spin representation crystal element
    341343    """
    class Spin_crystal_type_B_element(Spin,  
    405407        else:
    406408            return None   
    407409
    408 class Spin_crystal_type_D_element(Spin, CrystalElement):
     410class Spin_crystal_type_D_element(Spin):
    409411    r"""
    410412    Type D spin representation crystal element
    411413    """
  • sage/combinat/crystals/tensor_product.py

    diff --git a/sage/combinat/crystals/tensor_product.py b/sage/combinat/crystals/tensor_product.py
    a b Tensor Products of Crystals 
    2020import operator
    2121from sage.misc.latex import latex
    2222from sage.misc.cachefunc import cached_method
    23 from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets, EnumeratedSets
     23from sage.structure.parent import Parent
    2424from sage.structure.element import Element, parent
     25from sage.categories.category import Category
     26from sage.categories.classical_crystals import ClassicalCrystals
    2527from sage.combinat.root_system.cartan_type import CartanType
    2628from sage.combinat.cartesian_product import CartesianProduct
    2729from sage.combinat.combinat import CombinatorialObject
    2830from sage.combinat.partition import Partition
    2931from sage.combinat.tableau import Tableau
    3032from sage.combinat.tableau import Tableau_class
    31 from crystals import CrystalElement, ClassicalCrystal, Crystal
    3233from letters import CrystalOfLetters
    3334from sage.misc.flatten import flatten
    3435from sage.rings.integer import Integer
    from sage.rings.integer import Integer 
    3738# Support classes
    3839##############################################################################
    3940
    40 from sage.structure.parent import Parent
    4141from sage.structure.unique_representation import UniqueRepresentation
    4242
    4343class TestParent(UniqueRepresentation, Parent):
    class ImmutableListWithParent(Combinator 
    8989       
    9090            sage: from sage.combinat.crystals.tensor_product import ImmutableListWithParent, TestParent
    9191            sage: l = ImmutableListWithParent(TestParent(), [1,2,3])
    92             sage: l.__repr__()
     92            sage: l._repr_()
    9393            '[1, 2, 3]'
    9494        """
    9595        return "%s"%self._list
    def TensorProductOfCrystals(*crystals, * 
    243243        sage: T = TensorProductOfCrystals(C,D, generators=[[C(rows=[[1], [2]]), D(rows=[[1]])], [C(rows=[[2], [3]]), D(rows=[[1]])]])
    244244        sage: T.cardinality()
    245245        24
    246         sage: T.check()
    247         True
     246        sage: TestSuite(T).run()
    248247        sage: T.module_generators
    249248        [[[[1], [2]], [[1]]], [[[2], [3]], [[1]]]]
    250249        sage: [x.weight() for x in T.module_generators]
    def TensorProductOfCrystals(*crystals, * 
    288287
    289288    if "generators" in options:
    290289        generators = tuple(tuple(x) if isinstance(x, list) else x for x in options["generators"])
    291         if all(isinstance(crystal,ClassicalCrystal) for crystal in crystals):
    292             return TensorProductOfClassicalCrystalsWithGenerators(crystals, generators=generators, cartan_type = cartan_type)
    293         else:
    294             return TensorProductOfCrystalsWithGenerators(crystals, generators=generators, cartan_type = cartan_type)
     290        return TensorProductOfCrystalsWithGenerators(crystals, generators=generators, cartan_type = cartan_type)
    295291    else:
    296         if all(isinstance(crystal,ClassicalCrystal) for crystal in crystals):
    297             return FullTensorProductOfClassicalCrystals(crystals, cartan_type = cartan_type)
    298         else:
    299             return FullTensorProductOfCrystals(crystals, cartan_type = cartan_type)
     292        return FullTensorProductOfCrystals(crystals, cartan_type = cartan_type)
    300293
    301294
    302 class CrystalOfWords(Crystal):
     295class CrystalOfWords(UniqueRepresentation, Parent):
    303296    """
    304297    Auxiliary class to provide a call method to create tensor product elements.
    305298    This class is shared with several tensor product classes and is also used in CrystalOfTableaux to allow
    class TensorProductOfCrystalsWithGenerat 
    335328        """
    336329        assert isinstance(crystals, tuple)
    337330        assert isinstance(generators, tuple)
    338         if all(crystal in FiniteEnumeratedSets() for crystal in crystals):
    339             category = FiniteEnumeratedSets()
    340         else:
    341             category = EnumeratedSets()
    342         super(TensorProductOfCrystalsWithGenerators, self).__init__(category = category)
     331        category = Category.meet([crystal.category() for crystal in crystals])
     332        Parent.__init__(self, category = category)
    343333        self.rename("The tensor product of the crystals %s"%(crystals,))
    344334        self.crystals = crystals
    345335        self._cartan_type = cartan_type
    346336        self.module_generators = [ self(*x) for x in generators ]
    347337
    348 class TensorProductOfClassicalCrystalsWithGenerators(TensorProductOfCrystalsWithGenerators, ClassicalCrystal):
    349     pass
    350338
    351339class FullTensorProductOfCrystals(CrystalOfWords):
    352340    def __init__(self, crystals, **options):
    class FullTensorProductOfCrystals(Crysta 
    361349            sage: TestSuite(T).run()
    362350        """
    363351        crystals = list(crystals)
    364         if all(crystal in FiniteEnumeratedSets() for crystal in crystals):
    365             category = FiniteEnumeratedSets()
    366         else:
    367             category = EnumeratedSets()
    368         super(FullTensorProductOfCrystals, self).__init__(category = category)
     352        category = Category.meet([crystal.category() for crystal in crystals])
     353        Parent.__init__(self, category = category)
    369354        self.rename("Full tensor product of the crystals %s"%(crystals,))
    370355        self.crystals = crystals
    371356        if options.has_key('cartan_type'):
    class FullTensorProductOfCrystals(Crysta 
    406391        return self.cartesian_product.cardinality()
    407392
    408393
    409 class FullTensorProductOfClassicalCrystals(FullTensorProductOfCrystals, ClassicalCrystal):
    410     pass
    411394
    412 class TensorProductOfCrystalsElement(ImmutableListWithParent, CrystalElement):
     395class TensorProductOfCrystalsElement(ImmutableListWithParent):
    413396    r"""
    414397    A class for elements of tensor products of crystals
    415398    """
    class TensorProductOfCrystalsElement(Imm 
    608591
    609592CrystalOfWords.Element = TensorProductOfCrystalsElement
    610593
    611 class CrystalOfTableaux(CrystalOfWords, ClassicalCrystal):
     594class CrystalOfTableaux(CrystalOfWords):
    612595    r"""
    613596    Crystals of tableaux. Input: a Cartan Type type and "shape", a
    614597    partition of length <= type[1]. Produces a classical crystal with
    class CrystalOfTableaux(CrystalOfWords,  
    693676        sage: T.list()
    694677        [[]]
    695678        sage: T = CrystalOfTableaux(['C',2], shape = [1])
    696         sage: T.check()
    697         True
     679        sage: TestSuite(T).run()
    698680        sage: T.list()
    699681        [[[1]], [[2]], [[-2]], [[-1]]]
    700682        sage: T = CrystalOfTableaux(['A',2], shapes = [[],[1],[2]])
    class CrystalOfTableaux(CrystalOfWords,  
    730712        sage: C = CrystalOfTableaux(['D',4],shape=[1,1,1,-1])
    731713        sage: C.cardinality()
    732714        35
    733         sage: C.check()
    734         True
     715        sage: TestSuite(C).run()
    735716
    736717    The next example checks whether a given tableau is in fact a valid type C tableau or not:
    737718
    class CrystalOfTableaux(CrystalOfWords,  
    782763            sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
    783764            sage: TestSuite(T).run()
    784765        """
    785         super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets())
     766#        super(CrystalOfTableaux, self).__init__(category = FiniteEnumeratedSets())
     767        Parent.__init__(self, category = ClassicalCrystals())
    786768        self.letters = CrystalOfLetters(cartan_type)
    787769        self.module_generators = tuple(self.module_generator(la) for la in shapes)
    788770        self.rename("The crystal of tableaux of type %s and shape(s) %s"%(cartan_type, list(list(shape) for shape in shapes)))
    class CrystalOfTableauxElement(TensorPro 
    900882            list=[parent.letters(x) for x in list]
    901883        TensorProductOfCrystalsElement.__init__(self, parent, list)
    902884
    903     def __repr__(self):
     885    def _repr_(self):
    904886        """
    905887        EXAMPLES::
    906888       
    907889            sage: T = CrystalOfTableaux(['A',3], shape = [2,2])
    908890            sage: t = T(rows=[[1,2],[3,4]])
    909             sage: t.__repr__()
     891            sage: t._repr_()
    910892            '[[1, 2], [3, 4]]'
    911893        """
    912894        return repr(self.to_tableau())
    class CrystalOfTableauxElement(TensorPro 
    978960            x.reverse()
    979961        return Tableau(tab).conjugate()
    980962
     963    def promotion(self):
     964        """
     965        Promotion for type A crystals of tableaux of rectangular shape
     966
     967        Returns the result of applying promotion on this tableau.
     968
     969        This method only makes sense in type A with rectangular shapes.
     970
     971        EXAMPLES::
     972
     973            sage: C = CrystalOfTableaux(["A",3], shape = [3,3,3])
     974            sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]]))
     975            sage: t
     976            [[1, 1, 1], [2, 2, 3], [3, 4, 4]]
     977            sage: t.promotion()
     978            [[1, 1, 2], [2, 2, 3], [3, 4, 4]]
     979            sage: t.promotion().parent()
     980            The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]]
     981        """
     982        crystal = self.parent()
     983        cartan_type = crystal.cartan_type()
     984        assert cartan_type.type() == 'A'
     985        return crystal(self.to_tableau().promotion(cartan_type.rank()))
     986
     987    def promotion_inverse(self):
     988        """
     989        Inverse promotion for type A crystals of tableaux of rectangular shape
     990
     991        Returns the result of applying inverse promotion on this tableau.
     992
     993        This method only makes sense in type A with rectangular shapes.
     994
     995        EXAMPLES::
     996
     997            sage: C = CrystalOfTableaux(["A",3], shape = [3,3,3])
     998            sage: t = C(Tableau([[1,1,1],[2,2,3],[3,4,4]]))
     999            sage: t
     1000            [[1, 1, 1], [2, 2, 3], [3, 4, 4]]
     1001            sage: t.promotion_inverse()
     1002            [[1, 1, 2], [2, 3, 3], [4, 4, 4]]
     1003            sage: t.promotion_inverse().parent()
     1004            The crystal of tableaux of type ['A', 3] and shape(s) [[3, 3, 3]]
     1005        """
     1006        crystal = self.parent()
     1007        cartan_type = crystal.cartan_type()
     1008        assert cartan_type.type() == 'A'
     1009        return crystal(self.to_tableau().promotion_inverse(cartan_type.rank()))
     1010
    9811011CrystalOfTableaux.Element = CrystalOfTableauxElement