Path: blob/master/src/sage/combinat/crystals/alcove_path.py
8817 views
r"""1Alcove paths23AUTHORS:45- Brant Jones (2008): initial version6- Arthur Lubovsky (2013-03-07): rewritten to implement affine type78Special thanks to: Nicolas Borie, Anne Schilling, Travis Scrimshaw, and9Nicolas Thiery.10"""11#*****************************************************************************12# Copyright (C) 2008 Brant Jones <brant at math.ucdavis.edu>13# Copyright (C) 2013 Arthur Lubovsky <alubovsky at albany.edu>14#15# Distributed under the terms of the GNU General Public License (GPL)16#17# This code is distributed in the hope that it will be useful,18# but WITHOUT ANY WARRANTY; without even the implied warranty of19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU20# General Public License for more details.21#22# The full text of the GPL is available at:23#24# http://www.gnu.org/licenses/25#****************************************************************************2627from sage.structure.parent import Parent28from sage.structure.element import Element29from sage.structure.element_wrapper import ElementWrapper30from sage.structure.unique_representation import UniqueRepresentation31from sage.categories.crystals import Crystals32from sage.categories.finite_crystals import FiniteCrystals33from sage.graphs.all import DiGraph34from sage.combinat.root_system.cartan_type import CartanType35from sage.combinat.root_system.root_system import RootSystem36from sage.all import vector37from sage.rings.integer import Integer38from sage.rings.all import ZZ39from sage.combinat.root_system.weyl_group import WeylGroup40from sage.misc.misc_c import prod41from sage.categories.sets_cat import Sets42from sage.combinat.crystals.littelmann_path import CrystalOfLSPaths43from sage.misc.cachefunc import cached_method, cached_in_parent_method44from sage.categories.highest_weight_crystals import HighestWeightCrystals45from copy import copy46from sage.misc.latex import latex4748# necessary for tests49from sage.combinat.partition import Partitions50from sage.combinat.crystals.all import CrystalOfTableaux515253class CrystalOfAlcovePaths(UniqueRepresentation, Parent):54r"""55Crystal of alcove paths generated from a "straight-line" path to the56negative of a given dominant weight.5758INPUT:5960- ``cartan_type`` -- Cartan type of a finite or affine untwisted root61system.6263- ``weight`` -- dominant weight as a list of (integral) coefficients of the64fundamental weights.6566- ``highest_weight_crystal`` -- (Default: ``True``) If ``True``67returns the highest weight crystal. If ``False`` returns an68object which is close to being isomorphic to the tensor product69of Kirillov-Reshetikhin crystals of column shape in the70following sense: We get all the vertices, but only some of the71edges. We'll call the included edges pseudo-Demazure. They are72all non-zero edges and the 0-edges not at the end of a 0-string73of edges, i.e. not those with `f_{0}(b) = b'` with74`\phi_0(b) =1`. (Whereas Demazure 0-edges are those that75are not at the beginning of a zero string) In this case the76weight `[c_1, c_2, \ldots, c_k]` represents77`\sum_{i=1}^k c_i \omega_i`.7879.. NOTE::8081If ``highest_weight_crystal`` = ``False``, since we do not82get the full crystal, ``TestSuite`` will fail on the83Stembridge axioms.8485.. SEEALSO::8687- :class:`Crystals`8889EXAMPLES:9091The following example appears in Figure 2 of [LP2008]_::9293sage: C = CrystalOfAlcovePaths(['G',2],[0,1])94sage: G = C.digraph()95sage: GG = DiGraph({96... () : {(0) : 2 },97... (0) : {(0,8) : 1 },98... (0,1) : {(0,1,7) : 2 },99... (0,1,2) : {(0,1,2,9) : 1 },100... (0,1,2,3) : {(0,1,2,3,4) : 2 },101... (0,1,2,6) : {(0,1,2,3) : 1 },102... (0,1,2,9) : {(0,1,2,6) : 1 },103... (0,1,7) : {(0,1,2) : 2 },104... (0,1,7,9) : {(0,1,2,9) : 2 },105... (0,5) : {(0,1) : 1, (0,5,7) : 2 },106... (0,5,7) : {(0,5,7,9) : 1 },107... (0,5,7,9) : {(0,1,7,9) : 1 },108... (0,8) : {(0,5) : 1 },109... })110sage: G.is_isomorphic(GG)111True112sage: for (u,v,i) in G.edges(): print (u.integer_sequence() , v.integer_sequence(), i)113([], [0], 2)114([0], [0, 8], 1)115([0, 1], [0, 1, 7], 2)116([0, 1, 2], [0, 1, 2, 9], 1)117([0, 1, 2, 3], [0, 1, 2, 3, 4], 2)118([0, 1, 2, 6], [0, 1, 2, 3], 1)119([0, 1, 2, 9], [0, 1, 2, 6], 1)120([0, 1, 7], [0, 1, 2], 2)121([0, 1, 7, 9], [0, 1, 2, 9], 2)122([0, 5], [0, 1], 1)123([0, 5], [0, 5, 7], 2)124([0, 5, 7], [0, 5, 7, 9], 1)125([0, 5, 7, 9], [0, 1, 7, 9], 1)126([0, 8], [0, 5], 1)127128Alcove path crystals are a discrete version of Littelmann paths.129We verify that the alcove path crystal is isomorphic to the LS130path crystal::131132sage: C1 = CrystalOfAlcovePaths(['C',3],[2,1,0])133sage: g1 = C1.digraph() #long time134sage: C2 = CrystalOfLSPaths(['C',3],[2,1,0])135sage: g2 = C2.digraph() #long time136sage: g1.is_isomorphic(g2, edge_labels=True) #long time137True138139The preferred initialization method is via explicit weights rather than a Cartan type140and the coefficients of the fundamental weights::141142sage: R = RootSystem(['C',3])143sage: P = R.weight_lattice()144sage: La = P.fundamental_weights()145sage: C = CrystalOfAlcovePaths(2*La[1]+La[2]); C146Highest weight crystal of alcove paths of type ['C', 3] and weight 2*Lambda[1] + Lambda[2]147sage: C1==C148True149150We now explain the data structure::151152sage: C = CrystalOfAlcovePaths(['A',2],[2,0]) ; C153Highest weight crystal of alcove paths of type ['A', 2] and weight 2*Lambda[1]154sage: C._R.lambda_chain()155[(alpha[1], 0), (alpha[1] + alpha[2], 0), (alpha[1], 1), (alpha[1] + alpha[2], 1)]156157The previous list gives the initial "straight line" path from the158fundamental alcove `A_o` to its translation `A_o - \lambda` where159`\lambda = 2\omega_1` in this example. The initial path for weight160`\lambda` is called the `\lambda`-chain. This path is constructed from161the ordered pairs `(\beta, k)`, by crossing the hyperplane orthogonal to162`\beta` at height `-k`. We can view a plot of this path as follows::163164sage: x=C( () )165sage: x.plot() # not tested - outputs a pdf166167An element of the crystal is given by a subset of the `\lambda`-chain.168This subset indicates the hyperplanes where the initial path should be169folded. The highest weight element is given by the empty subset. ::170171sage: x172()173sage: x.f(1).f(2)174((alpha[1], 1), (alpha[1] + alpha[2], 1))175sage: x.f(1).f(2).integer_sequence()176[2, 3]177sage: C([2,3])178((alpha[1], 1), (alpha[1] + alpha[2], 1))179sage: C([2,3]).is_admissible() #check if a valid vertex180True181sage: C([1,3]).is_admissible() #check if a valid vertex182False183184Alcove path crystals now works in affine type (:trac:`14143`)::185186sage: C = CrystalOfAlcovePaths(['A',2,1],[1,0,0]) ; C187Highest weight crystal of alcove paths of type ['A', 2, 1] and weight Lambda[0]188sage: x=C( () )189sage: x.f(0)190((alpha[0], 0),)191sage: C.R192Root system of type ['A', 2, 1]193sage: C.weight194Lambda[0]195196Test that the tensor products of Kirillov-Reshetikhin crystals197minus non-pseudo-Demazure arrows is in bijection with alcove path198construction::199200sage: K = KirillovReshetikhinCrystal(['B',3,1],2,1)201sage: T = TensorProductOfCrystals(K,K)202sage: g = T.digraph() #long time203sage: for e in g.edges(): #long time204....: if e[0].phi(0) == 1 and e[2] == 0: #long time205....: g.delete_edge(e) #long time206207sage: C = CrystalOfAlcovePaths(['B',3,1],[0,2,0], highest_weight_crystal=False)208sage: g2 = C.digraph_fast() #long time209sage: g.is_isomorphic(g2, edge_labels = True) #long time210True211212.. NOTE::213214In type `C_n^{(1)}`, the Kirillov-Reshetikhin crystal is not connected215when restricted to pseudo-Demazure arrows, hence the previous example will216fail for type `C_n^{(1)}` crystals.217218::219220sage: R = RootSystem(['B',3])221sage: P = R.weight_lattice()222sage: La = P.fundamental_weights()223sage: D = CrystalOfAlcovePaths(2*La[2], highest_weight_crystal=False)224sage: C == D225True226227.. WARNING:: Weights from finite root systems index non-highest weight crystals.228229REFERENCES:230231.. [LP2008] C. Lenart and A. Postnikov. *A combinatorial model for232crystals of Kac-Moody algebras*. Trans. Amer. Math. Soc. 360 (2008),2334349-4381.234"""235236@staticmethod237def __classcall_private__(cls, starting_weight, cartan_type = None,238highest_weight_crystal=None):239"""240Classcall to mend the input.241242Internally, the CrystalOfAlcovePaths code works with a ``starting_weight`` that243is in the ``weight_space`` associated to the crystal. The user can, however,244also input a ``cartan_type`` and the coefficients of the fundamental weights245as ``starting_weight``. This code transforms the input into the right246format (also necessary for UniqueRepresentation).247248TESTS::249250sage: C = CrystalOfAlcovePaths(['A',2,1], [1,0,0])251sage: C2 = CrystalOfAlcovePaths(CartanType(['A',2,1]), (1,0,0))252sage: C is C2253True254sage: R = RootSystem(['B',2,1])255sage: La = R.weight_space().basis()256sage: B1 = CrystalOfAlcovePaths(['B',2,1],[0,0,1])257sage: B2 = CrystalOfAlcovePaths(La[2])258sage: B1 is B2259True260"""261if isinstance(cartan_type, bool): # new style signature, optional arguments leak over262highest_weight_crystal = cartan_type263264elif isinstance(cartan_type, list) or isinstance(cartan_type, tuple): #old style signature265#switch positional arguments266cartan_type, starting_weight = CartanType(starting_weight), cartan_type267268if highest_weight_crystal == False:269cartan_type = cartan_type.classical()270271R = RootSystem(cartan_type)272P = R.weight_space()273Lambda = P.basis()274offset = R.index_set()[Integer(0)]275starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set())276277#set defaults278if highest_weight_crystal == None:279highest_weight_crystal = True280281if not starting_weight.is_dominant():282raise ValueError("{0} is not a dominant weight".format(starting_weight))283284285return super(CrystalOfAlcovePaths, cls).__classcall__(cls,286starting_weight, highest_weight_crystal)287288289def __init__(self, starting_weight, highest_weight_crystal):290r"""291Initialize ``self``.292293TESTS::294295sage: C = CrystalOfAlcovePaths(['G',2],[0,1])296sage: TestSuite(C).run()297298sage: C = CrystalOfAlcovePaths(['A',2,1],[1,0,0])299sage: TestSuite(C).run() #long time300301sage: C = CrystalOfAlcovePaths(['A',2,1],[1,0],False)302sage: TestSuite(C).run(skip="_test_stembridge_local_axioms") #long time303"""304##########################################################################305# NOTE:306# If cartan_type.is_affine() == True and highest weight crystal == False,307# since we only use the positive roots of the *finite* root system308# to get the crystal we set self._finite_cartan_type is true309#310# We want the indexing set to include 0 so use the affine type notation311# for the cartan type.312##########################################################################313cartan_type = starting_weight.parent().cartan_type()314315self.weight = starting_weight316self.R = RootSystem(cartan_type)317self._highest_weight_crystal = highest_weight_crystal318self._cartan_type = cartan_type319320321if cartan_type.is_finite() and highest_weight_crystal == True:322Parent.__init__(self, category = FiniteCrystals() )323self._R = RootsWithHeight(starting_weight)324self._finite_cartan_type = True325elif cartan_type.is_finite() and highest_weight_crystal == False:326Parent.__init__(self, category = FiniteCrystals() )327self._R = RootsWithHeight(starting_weight)328self._finite_cartan_type = True329self._cartan_type = cartan_type.affine()330else:331Parent.__init__(self, category = HighestWeightCrystals())332self._R = RootsWithHeight(starting_weight)333self._finite_cartan_type = False334335336self.module_generators = ( self.element_class(self, ()), )337338def _repr_(self):339"""340Return a string representation of ``self``.341342EXAMPLES::343344sage: C = CrystalOfAlcovePaths(['A',2,1], [1,0,0])345sage: C346Highest weight crystal of alcove paths of type ['A', 2, 1] and weight Lambda[0]347sage: C = CrystalOfAlcovePaths(['A',2,1], [1,0], False)348sage: C349Crystal of alcove paths of type ['A', 2, 1] and weight Lambda[1]350"""351if self._highest_weight_crystal:352return "Highest weight crystal of alcove paths of type %s and weight %s"%(self._cartan_type, self.weight)353return "Crystal of alcove paths of type %s and weight %s"%(self._cartan_type, self.weight)354355def _element_constructor_(self, data):356"""357Construct an element of ``self`` from ``data``.358359EXAMPLES::360361sage: C = CrystalOfAlcovePaths(['A',2],[3,2])362sage: C([8,9])363((alpha[1], 2), (alpha[1] + alpha[2], 4))364"""365if isinstance(data, tuple):366return self.element_class(self, data)367elif isinstance(data, list):368lambda_chain = self._R.lambda_chain()369#data starts indexing at 0370return self.element_class(self, tuple(sorted([lambda_chain[i] for i in data])))371372def vertices(self):373"""374Return a list of all the vertices of the crystal.375376The vertices are represented as lists of integers recording the folding377positions.378379One can compute all vertices of the crystal by finding all the380admissible subsets of the `\lambda`-chain (see method381is_admissible, for definition). We use the breath first382search algorithm.383384.. WARNING::385386This method is (currently) only useful for the case when387``highest_weight_crystal = False``, where you cannot always388reach all vertices of the crystal using crystal operators,389starting from the highest weight vertex. This method is390typically slower than generating the crystal graph using391crystal operators.392393EXAMPLES::394395sage: C = CrystalOfAlcovePaths(['C',2],[1,0])396sage: C.vertices()397[[], [0], [0, 1], [0, 1, 2]]398sage: C = CrystalOfAlcovePaths(['C',2,1],[2,1],False)399sage: len(C.vertices())40080401402The number of elements reachable using the crystal operators from the403module generator::404405sage: len(list(C))40655407"""408lambda_chain = self._R.lambda_chain()409len_lambda_chain = len(lambda_chain)410W = WeylGroup(self._R._cartan_type, prefix='s')411s = W.simple_reflections()412highest_weight_crystal = self._highest_weight_crystal413414if highest_weight_crystal == True:415successors = 'bruhat_upper_covers'416else:417successors = 'quantum_bruhat_successors'418419# lst contains ordered pairs (w,l) l= list of positions that get420# you to the word, it needs to be refreshed421422#initialization423lst=[]424for i in range(len_lambda_chain):425associated_reflection = lambda_chain[i].root.associated_reflection()426if len(associated_reflection) == 1:427lst.append( (prod([ s[j] for j in associated_reflection ]), [i]) )428429l=copy(lst)430431while True:432lst2 = []433for x in lst:434suc = getattr(x[0], successors)()435for j in range(x[1][-1]+1, len_lambda_chain):436temp = x[0] * prod(437[ s[k] for k in lambda_chain[j].root.associated_reflection() ])438if temp in suc:439lst2.append((temp,x[1]+[j]))440l.append((temp,x[1]+[j]))441if lst2 == []:442break443else :444lst = lst2445446return [ [] ] + [i[1] for i in l]447448def digraph_fast(self, depth=None):449r"""450Return the crystal :class:`graph <DiGraph>` with maximum depth451``depth`` deep starting at the module generator. Significant speed up452for highest_weight_crystals of affine type.453454EXAMPLES::455456sage: CrystalOfAlcovePaths(['A',2], [1,1]).digraph_fast(depth=3)457Digraph on 7 vertices458459TESTS:460461The following example demonstrates the speed improvement.462The speedup in non-affine types is small however::463464sage: cartan_type = ['A',2,1] #long time465sage: weight = [1,1,0] #long time466sage: depth = 5 #long time467sage: C = CrystalOfAlcovePaths(cartan_type, weight) #long time468sage: %timeit C.digraph_fast(depth) # not tested46910 loops, best of 3: 171 ms per loop470sage: %timeit C.digraph(subset=C.subcrystal(max_depth=depth, direction='lower')) #not tested4711 loops, best of 3: 19.7 s per loop472sage: G1 = C.digraph_fast(depth) #long time473sage: G2 = C.digraph(subset=C.subcrystal(max_depth=depth, direction='lower')) #long time474sage: G1.is_isomorphic(G2, edge_labels=True) #long time475True476477"""478if not self._highest_weight_crystal:479return super(CrystalOfAlcovePaths, self).digraph()480481if self._cartan_type.is_affine() and depth is None:482depth = 10483I = self.index_set()484485rank = 0486G = { self.module_generators[0]: {} }487visited = { self.module_generators[0] }488489while depth is None or rank < depth:490recently_visited = set()491for x in visited:492G.setdefault(x, {}) # does nothing if there's a default493for i in I:494xfi = x.f(i)495if xfi != None:496G[x][xfi] = i497recently_visited.add(xfi)498if len(recently_visited) == 0: # No new nodes, nothing more to do499break500rank += 1501visited = recently_visited502503return DiGraph(G)504505class CrystalOfAlcovePathsElement(ElementWrapper):506"""507Crystal of alcove paths element.508509INPUT:510511- ``data`` -- a list of folding positions in the lambda chain (indexing512starts at 0) or a tuple of :class:`RootsWithHeight` giving folding513positions in the lambda chain.514515EXAMPLES::516517sage: C = CrystalOfAlcovePaths(['A',2],[3,2])518sage: x = C ( () )519sage: x.f(1).f(2)520((alpha[1], 2), (alpha[1] + alpha[2], 4))521sage: x.f(1).f(2).integer_sequence()522[8, 9]523sage: C([8,9])524((alpha[1], 2), (alpha[1] + alpha[2], 4))525"""526def __iter__(self):527r"""528Initialize ``self``.529530EXAMPLES::531532sage: C = CrystalOfAlcovePaths(['A',2],[1,0])533sage: lst = list(C)534sage: for i in lst[2]: i535(alpha[1], 0)536(alpha[1] + alpha[2], 0)537"""538return iter(self.value)539540def is_admissible(self):541r"""542Diagnostic test to check if ``self`` is a valid element of the crystal.543544If ``self.value`` is given by545546.. MATH::547548(\beta_1, i_1), (\beta_2, i_2), \ldots, (\beta_k, i_k),549550for highest weight crystals this checks if the sequence551552.. MATH::5535541 \rightarrow s_{\beta_1} \rightarrow555s_{\beta_1}s_{\beta_2} \rightarrow \cdots \rightarrow556s_{\beta_1}s_{\beta_2} \ldots s_{\beta_k}557558is a path in the Bruhat graph. If ``highest_weight_crystal=False``,559then the method checks if the above sequence is a path in the quantum560Bruhat graph.561562EXAMPLES::563564sage: C = CrystalOfAlcovePaths(['A',2],[1,1]); C565Highest weight crystal of alcove paths of type ['A', 2] and weight Lambda[1] + Lambda[2]566sage: roots = sorted(list(C._R._root_lattice.positive_roots())); roots567[alpha[1], alpha[1] + alpha[2], alpha[2]]568sage: r1 = C._R(roots[0],0); r1569(alpha[1], 0)570sage: r2 = C._R(roots[2],0); r2571(alpha[2], 0)572sage: r3 = C._R(roots[1],1); r3573(alpha[1] + alpha[2], 1)574sage: x = C( ( r1,r2) )575sage: x.is_admissible()576True577sage: x = C( (r3,) ); x578((alpha[1] + alpha[2], 1),)579sage: x.is_admissible()580False581sage: C = CrystalOfAlcovePaths(['C',2,1],[2,1],False)582sage: C([7,8]).is_admissible()583True584sage: C = CrystalOfAlcovePaths(['A',2],[3,2])585sage: C([2,3]).is_admissible()586True587588.. TODO:: Better doctest589"""590W = WeylGroup(self.parent()._R._cartan_type, prefix='s')591s = W.simple_reflections()592highest_weight_crystal = self.parent()._highest_weight_crystal593594if highest_weight_crystal == True:595successors = 'bruhat_upper_covers'596else:597successors = 'quantum_bruhat_successors'598599#start at the identity600w = W.unit()601for i in self:602t = prod( [ s[j] for j in i.root.associated_reflection() ] )603successor = w * t604if successor not in getattr(w, successors)():605return False606w = successor607return True608609def _latex_(self):610r"""611Return a `\LaTeX` representation of ``self``.612613EXAMPLES::614615sage: C = CrystalOfAlcovePaths(['A',2],[1,1])616sage: C([1,2])._latex_()617[(\alpha_{1} + \alpha_{2}, 0), (\alpha_{1}, 0)]618"""619return [ (latex(i.root),i.height) for i in self.value ]620621@cached_in_parent_method622def integer_sequence(self):623r"""624Return a list of integers corresponding to positions in625the `\lambda`-chain where it is folded.626627.. TODO::628629Incorporate this method into the ``_repr_`` for finite Cartan type.630631.. NOTE::632633Only works for finite Cartan types and indexing starts at 0.634635EXAMPLES::636637sage: C = CrystalOfAlcovePaths(['A',2],[3,2])638sage: x = C( () )639sage: x.f(1).f(2).integer_sequence()640[8, 9]641"""642lambda_chain = self.parent()._R.lambda_chain()643return [lambda_chain.index(j) for j in self.value]644645def phi(self, i):646r"""647Return the distance to the end of the `i`-string.648649This method overrides the generic implementation in the category of650crystals since this computation is more efficient.651652EXAMPLES::653654sage: C = CrystalOfAlcovePaths(['A',2],[1,1])655sage: [c.phi(1) for c in C]656[1, 2, 0, 1, 0, 0, 1, 0]657sage: [c.phi(2) for c in C]658[1, 0, 2, 0, 1, 1, 0, 0]659"""660highest_weight_crystal = self.parent()._highest_weight_crystal661positions, gi = self._gi(i)662663m=max(gi)664665if not highest_weight_crystal and i == 0:666raise NotImplementedError667# I think the M below should still work in this case668669M = Integer(m)/2 - Integer(1)/2670return M671672def epsilon(self, i):673r"""674Return the distance to the start of the `i`-string.675676EXAMPLES::677678sage: C = CrystalOfAlcovePaths(['A',2],[1,1])679sage: [c.epsilon(1) for c in C]680[0, 0, 1, 1, 0, 2, 0, 1]681sage: [c.epsilon(2) for c in C]682[0, 1, 0, 0, 1, 0, 2, 1]683"""684#crude but functional685j = 0686temp = self687temp = temp.e(i)688while temp != None:689j+=1690temp = temp.e(i)691692return j693694def weight(self):695"""696Returns the weight of self.697698EXAMPLES::699700sage: C = CrystalOfAlcovePaths(['A',2],[2,0])701sage: for i in C: i.weight()7022*Lambda[1]703Lambda[2]704Lambda[1] - Lambda[2]705-2*Lambda[1] + 2*Lambda[2]706-Lambda[1]707-2*Lambda[2]708709710"""711root_space = self.parent().R.root_space()712weight = -self.parent().weight713for i in self.value[::-1]:714root = root_space(i.root)715weight = -i.height*root + weight.reflection(root)716717return -weight718719#def __repr__(self):720#return str(self.integer_sequence())721722def plot(self):723r"""724Return a plot ``self``.725726.. NOTE::727728Currently only implemented for types `A_2`, `B_2`, and `C_2`.729730EXAMPLES::731732sage: C = CrystalOfAlcovePaths(['A',2],[2,0])733sage: x = C( () ).f(1).f(2)734sage: x.plot() # Not tested - creates a pdf735"""736ct = self.parent()._R._cartan_type.dual()737word = self.parent()._R.word()738integer_sequence = self.integer_sequence()739foldings = [False for i in word]740for i in integer_sequence:741foldings[i] = True742affine_ambient_space = RootSystem(ct.affine()).ambient_space()743return affine_ambient_space.plot() + affine_ambient_space.plot_alcove_walk( word, foldings=foldings, labels=False)744745def __eq__(self, other):746r"""747Test equality of ``self.value`` and ``other.value``.748749EXAMPLES::750751sage: C=CrystalOfAlcovePaths(['B',2],[1,0])752sage: lst=list(C)753sage: lst[2] == lst[2]754True755sage: lst[2] == lst[1]756False757"""758#note: may want to use _eq_ for coercion759try:760return self.value == other.value761except (NameError, AttributeError):762return False763764def __lt__(self, other):765r"""766Test if ``self.value`` is less than ``other.value`` in dictionary order.767768EXAMPLES::769770sage: C = CrystalOfAlcovePaths(['A',2],[2,0])771sage: x = C( () )772sage: x.__lt__(x.f(1))773True774sage: a=x.f(1) ; b = x.f(1).f(1).f(2)775sage: a.__lt__(b)776False777"""778return self.value < other.value779780def __gt__(self, other):781r"""782Test if ``self.value`` is greater than ``other.value`` in dictionary783order.784785EXAMPLES::786787sage: C = CrystalOfAlcovePaths(['A',2],[2,0])788sage: x = C( () )789sage: x.__gt__(x.f(1))790False791sage: a=x.f(1) ; b = x.f(1).f(1).f(2)792sage: a.__gt__(b)793True794"""795return self.value > other.value796797def _folding_data(self, i):798r"""799Compute information needed to build the graph `g_{\alpha_i}`.800Results of this method are sent to _gi for further processing.801802INPUT:803804- ``i`` -- element of the index_set of the underlying root_system.805806OUTPUT:807808A dictionary where the keys are of type RootsWithHeight which record809positions where `\pm \alpha_i` shows up in the folded `\lambda` chain.810The values are `1` if `\alpha_i` is in the corresponding position in811the folded `\lambda`-chain, `-1` if `-\alpha_i` is in the corresponding812position in the folded `\lambda`-chain.813814.. NOTE::815816*infinity* is a special key that records the "sign at infinity".817818::819820sage: C=CrystalOfAlcovePaths(['A',2],[1,1])821sage: x=C( () ).f(1)822sage: x._folding_data(2)823{(alpha[1] + alpha[2], 1): 1, 'infinity': 1, (alpha[2], 0): 1}824"""825Parent = self.parent()826827#self.value contains the admissible sequence as a tuple of Element828829finite_cartan_type = Parent._finite_cartan_type # bool830J = list(self.value)831832#NOTE: R is a RootsWithHeight object and NOT a RootSystem object833R = Parent._R834weight = Parent.weight835836signs = {}837838# 0 arrows in the case of finite cartan type839# always allow 0 arrows840if finite_cartan_type and i == 0:841Beta = R._root_lattice.highest_root()842elif i in self.index_set():843Beta = R._root_lattice.simple_root(i)844845max_height_Beta = weight.scalar(Beta.associated_coroot())846847if len(J) == 0:848for k in range( max_height_Beta ) :849x = R(Beta, k)850signs[x]=self._sign(Beta)851signs['infinity'] = self._sign(Beta)852853elif len(J) > 0 :854#NOTE: we assume J is sorted by order on Element of RootsWithHeight855856for k in range( max_height_Beta ):857x = R(Beta, k)858if x <= J[0]:859signs[x] = self._sign(Beta)860861for j in range( len(J) ):862863Beta = Beta.reflection(J[j].root)864sign_Beta = self._sign(Beta)865max_height_Beta = weight.scalar(866(sign_Beta * Beta).associated_coroot())867868869# some optimization so we don't initialize too many objects870# range(c1,c2) can be replaced by range(max_height_Beta) but it871# checks unnecessary extra things872873c1 = J[j]._cmp_v[0] * max_height_Beta874if j == len(J) - 1:875c2 = max_height_Beta876else:877c2 = min (max_height_Beta, J[j+1]._cmp_v[0]*max_height_Beta + 1)878879for k in range(c1,c2):880881x=R( sign_Beta * Beta , k)882883if (884( j < len(J) - 1 and J[j] < x <= J[j+1] ) or885( j == len(J) - 1 and J[j] < x)886):887signs[x] = sign_Beta888889signs['infinity'] = sign_Beta # tail sign tells something about last step890# in g_alpha891892if finite_cartan_type and i==0:893signs = { x : -signs[x] for x in signs.keys() }894895return signs896897def e(self, i):898r"""899Return the `i`-th crystal raising operator on ``self``.900901INPUT:902903- ``i`` -- element of the index set of the underlying root system.904905EXAMPLES::906907sage: C = CrystalOfAlcovePaths(['A',2],[2,0]); C908Highest weight crystal of alcove paths of type ['A', 2] and weight 2*Lambda[1]909sage: x = C( () )910sage: x.e(1)911sage: x.f(1) == x.f(1).f(2).e(2)912True913"""914Parent = self.parent()915finite_cartan_type = Parent._finite_cartan_type916917J = list(self.value)918positions, gi = self._gi(i)919920m=max(gi)921m_index = len(gi)-1-list(reversed(gi)).index(m) # last max in gi922923924if finite_cartan_type and i==0 :925M = Integer(m)/2 + Integer(1)/2926else:927M = Integer(m)/2 - Integer(1)/2928929930KR_test = finite_cartan_type and i==0 and m_index < len(gi) - 1931KR_test = KR_test and M >= 1932933######################################################################934# NOTE:935# In the KR_case we want to insure that positions[m_index] is in J936# If m_index > 0 then it's always true937# If m_index == 0 then M >=1 guarantees this938######################################################################939940if ( (not finite_cartan_type or i!=0) and m_index < len(gi)-1 # alpha_i is a simple root941) or KR_test:942943944J.remove(positions[m_index])945if m_index+1 < len(positions): # if m_index+1 != 'infinity'946# i.e. positions[m_index+1] makes sense947J.append(positions[m_index+1])948return_value = Parent ( tuple( sorted(J) ) )949950# we attach to each admissible sequence a list951# which encodes a path (via root operators) from the () generator952# to the admissible sequence953# this is useful for investing the crystal954955try:956return_value.i_string = self.i_string + [['e',i]]957except AttributeError:958return_value.i_string = [['e',i]]959960return return_value961else:962return None963964@cached_method965def _gi(self, i):966r"""967Compute information needed to build the graph `g_{\alpha_i}`.968This graph is used to apply the `i`-th crystal operator.969970INPUT:971972- ``i`` - element of the index_set of the underlying root_system.973974OUTPUT:975976A tuple ``(positions, gi)``:977978- ``positions`` -- is a list of RootsWithHeight. These appear sorted in979their natural order, and record where `\pm \alpha_i` shows up in980the folded `\lambda`-chain.981982- ``gi`` -- is a list of integers recording the height983(up to affine transformation) of `\pm \alpha_i`984in the folded `\lambda`-chain whose location is recorded by985``positions``.986987.. NOTE::988989- ``positions`` has length one less than ``gi`` since it does not990contain the position 'infinity'.991992- To get the real `g_{\alpha_i}` one has to divide by 2 and add 1/2993or divide by 2 and subtract 1/2 depending on if994``self._finite_cartan_type==True and i == 0``995or not. This is done in crystal operator methods.996997EXAMPLES::998999sage: C=CrystalOfAlcovePaths(['A',2],[1,1])1000sage: x=C( () ).f(1)1001sage: x._gi(2)1002([(alpha[2], 0), (alpha[1] + alpha[2], 1)], [1, 3, 5])1003"""1004signs = self._folding_data(i)1005positions = sorted( [ x for x in signs.keys() if x != 'infinity' ] )10061007if len(positions)==0 :1008return ( positions, [ signs['infinity'] ] )10091010gi = [ signs[ positions[0] ] ]1011for j in range(1,len(positions)):1012gi.append(1013gi[j-1] +1014signs[positions[j-1]]*self._eps(positions[j-1]) + signs[positions[j]] )1015gi.append( gi[-1] +1016signs[positions[-1]]*self._eps(positions[-1]) + signs['infinity'] )10171018return (positions, gi)10191020def f(self, i):1021r"""1022Returns the `i`-th crystal lowering operator on ``self``.10231024INPUT:10251026- ``i`` -- element of the index_set of the underlying root_system.10271028EXAMPLES::10291030sage: C=CrystalOfAlcovePaths(['B',2],[1,1])1031sage: x=C( () )1032sage: x.f(1)1033((alpha[1], 0),)1034sage: x.f(1).f(2)1035((alpha[1], 0), (alpha[1] + alpha[2], 2))10361037"""1038Parent = self.parent()1039finite_cartan_type = Parent._finite_cartan_type10401041# get a copy in a form of a list of self.value1042J = list(self.value)1043positions, gi = self._gi(i)10441045m=max(gi)1046m_index=gi.index(m)104710481049if finite_cartan_type and i==0 :10501051# python doesn't handle fractions natively1052M = Integer(m)/2 + Integer(1)/21053else:1054M = Integer(m)/2 - Integer(1)/2105510561057# boolian determining when to move a folding in KR case1058KR_test = finite_cartan_type and i==01059KR_test = KR_test and M > 110601061# In the KR case, we return a value other than None when1062# `\alpha_i` is in position m_index - 11063# (The following relies on a technical condition (C2) )1064# note insert reference1065#1066# if m_index - 1 == 0 then M > 1 and (C2) forces1067# `\alhpa_i` in positions[m_index - 1]1068#1069# otherwise if m_index - 1 > 0 then (C2) is enough10701071if ( (not finite_cartan_type or i!=0) and M > 0 # alpha_i is a simple root1072) or KR_test :# KR case10731074J.append(positions[m_index-1])1075if m_index < len(positions): # if m_index != 'infinity'1076# thus positions[m_index] makes sense1077J.remove(positions[m_index])1078return_value = Parent ( tuple( sorted(J) ) )10791080# we attach to each admissible sequence a list1081# which encodes a path (via root operators) from the generator ()10821083try:1084return_value.i_string = self.i_string + [['f',i]]1085except AttributeError:1086return_value.i_string = [['f',i]]10871088return return_value1089else:1090return None10911092@staticmethod1093def _sign(root):1094r"""1095Return `1` if root is a positive root, and `-1` if root is a negative1096root.10971098EXAMPLES::10991100sage: from sage.combinat.crystals.alcove_path import CrystalOfAlcovePathsElement1101sage: rl = RootSystem(['A',2]).root_lattice()1102sage: x = rl.from_vector(vector([0,1]))1103sage: CrystalOfAlcovePathsElement._sign(x)110411105"""1106if root.is_positive_root():1107return 11108else:1109return -111101111def _eps(self, root):1112r"""1113Return `-1` if root is in ``self.value``, otherwise return `1`.11141115EXAMPLES::11161117sage: C = CrystalOfAlcovePaths(['C',2],[3,2])1118sage: x = C( () ).f(1).f(2); x1119((alpha[1], 2), (2*alpha[1] + alpha[2], 4))1120sage: x._eps(x.value[0])1121-11122sage: R = C._R1123sage: y = R ( x.value[0].root, 1 ); y1124(alpha[1], 1)1125sage: x._eps(y)1126111271128"""1129if root in self.value:1130return -11131else:1132return 111331134CrystalOfAlcovePaths.Element = CrystalOfAlcovePathsElement1135#deprecate the old name1136from sage.misc.superseded import deprecated_function_alias1137ClassicalCrystalOfAlcovePaths = deprecated_function_alias(14143, CrystalOfAlcovePaths)11381139class RootsWithHeight(UniqueRepresentation, Parent):1140r"""1141Data structure of the ordered pairs `(\beta,k)`,1142where `\beta` is a positive root and `k` is a non-negative integer. A total1143order is implemented on this set, and depends on the weight.11441145INPUT:11461147- ``cartan_type`` -- Cartan type of a finite or affine untwisted root1148system11491150- ``weight`` -- dominant weight as a list of (integral) coefficients of1151the fundamental weights11521153EXAMPLES::11541155sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1156sage: R = RootsWithHeight(['A',2],[1,1]); R1157Roots with height of cartan type ['A', 2] and dominant weight Lambda[1] + Lambda[2]11581159sage: r1 = R._root_lattice.from_vector(vector([1,0])); r11160alpha[1]1161sage: r2 = R._root_lattice.from_vector(vector([1,1])); r21162alpha[1] + alpha[2]11631164sage: x = R(r1,0); x1165(alpha[1], 0)1166sage: y = R(r2,1); y1167(alpha[1] + alpha[2], 1)1168sage: x < y1169True1170"""11711172@staticmethod1173def __classcall_private__(cls, starting_weight, cartan_type = None):1174"""1175Classcall to mend the input.11761177Internally, the RootsWithHeight code works with a ``starting_weight`` that1178is in the ``weight_space`` associated to the crystal. The user can, however,1179also input a ``cartan_type`` and the coefficients of the fundamental weights1180as ``starting_weight``. This code transforms the input into the right1181format (also necessary for UniqueRepresentation).11821183TESTS::1184sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1185sage: R = RootsWithHeight(['A',2],[3,2])1186sage: S = RootsWithHeight(CartanType(['A',2]), (3,2))1187sage: R is S1188True11891190sage: R = RootSystem(['B',2,1])1191sage: La = R.weight_space().basis()1192sage: C = RootsWithHeight(['B',2,1],[0,0,1])1193sage: B = RootsWithHeight(La[2])1194sage: B is C1195True11961197"""1198if cartan_type is not None:1199cartan_type, starting_weight = CartanType(starting_weight), cartan_type12001201R = RootSystem(cartan_type)1202P = R.weight_space()1203Lambda = P.basis()1204offset = R.index_set()[Integer(0)]1205starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set())12061207return super(RootsWithHeight, cls).__classcall__(cls, starting_weight)120812091210def __init__(self, weight):1211r"""1212Initialize ``self``.12131214EXAMPLES::12151216sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1217sage: R = RootsWithHeight(['A',2],[3,2])1218sage: TestSuite(R).run()1219"""1220Parent.__init__(self, category = Sets() )12211222cartan_type = weight.parent().cartan_type()1223self._cartan_type = cartan_type1224self._root_system = RootSystem(cartan_type)1225self._root_lattice = self._root_system.root_lattice()1226self._weight_lattice = self._root_system.weight_lattice()1227self.weight = weight12281229def _repr_(self):1230"""1231Return a string representation of ``self``.12321233EXAMPLES::12341235sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1236sage: RootsWithHeight(['A',2],[3,2])1237Roots with height of cartan type ['A', 2] and dominant weight 3*Lambda[1] + 2*Lambda[2]1238"""1239return "Roots with height of cartan type %s and dominant weight %s"%(1240self._root_system.cartan_type(), self.weight)12411242def _max_height(self, root):1243r"""1244If root is `\beta`, return `k = \langle \lambda, \beta^{\vee} \rangle`.12451246Only ordered pairs of the form `(\beta, l)` for `0 \leq l < k` are1247allowed.12481249EXAMPLES::12501251sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1252sage: C = RootsWithHeight(['A',3],[3,2,0])1253sage: x = C._root_lattice.from_vector(vector([1,1])); x1254alpha[1] + alpha[2]1255sage: C._max_height(x)125651257"""1258return self.weight.scalar(root.associated_coroot())12591260@cached_method1261def word(self):1262r"""1263Gives the initial alcove path (`\lambda`-chain) in terms of simple1264roots. Used for plotting the path.12651266.. NOTE::12671268Currently only implemented for finite Cartan types.12691270EXAMPLES::12711272sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1273sage: R = RootsWithHeight(['A',2],[3,2])1274sage: R.word()1275[2, 1, 2, 0, 1, 2, 1, 0, 1, 2]1276"""1277cartan_type = self._root_system.cartan_type()1278if not cartan_type.is_finite():1279raise NotImplementedError1280simple_roots = self._root_lattice.simple_roots().list()1281lambda_chain = [ x.root for x in self.lambda_chain() ]12821283coroot_lattice = RootSystem(cartan_type).coroot_lattice()1284cohighest_root = coroot_lattice.highest_root()12851286word = []1287for i in range(len(lambda_chain)):1288beta = lambda_chain[i]1289for j in reversed(range(i)):1290beta = beta.reflection(lambda_chain[j])1291#beta is now a simple root or the highest root12921293coroot = beta.associated_coroot()1294support = coroot.support() # the path is in dual affine space1295if len(support) == 1: # beta is a simple root1296word.append(support[0])1297elif coroot == -cohighest_root:1298word.append(0)1299else:1300assert False, 'should never get here'13011302return word13031304@cached_method1305def lambda_chain(self):1306r"""1307Return the unfolded `\lambda`-chain.13081309.. NOTE:: Only works in root systems of finite type.13101311EXAMPLES::13121313sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1314sage: R = RootsWithHeight(['A',2],[1,1]); R1315Roots with height of cartan type ['A', 2] and dominant weight Lambda[1] + Lambda[2]1316sage: R.lambda_chain()1317[(alpha[2], 0), (alpha[1] + alpha[2], 0), (alpha[1], 0), (alpha[1] + alpha[2], 1)]1318"""1319if not self._root_lattice.cartan_type().is_finite():1320raise ValueError("cartan type {0} is not finite".format(self._root_lattice.cartan_type()))13211322l=[]1323for i in self._root_lattice.positive_roots():1324for j in range(self._max_height(i)):1325l.append(self(i,j))13261327return sorted(l)13281329def _element_constructor_(self, root, height):1330r"""1331Construct a :class:`RootsWithHeightElement` with ``self`` as the parent.13321333EXAMPLES::13341335sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1336sage: rl = RootSystem(['A',2]).root_lattice()1337sage: x = rl.from_vector(vector([1,1])); x1338alpha[1] + alpha[2]1339sage: R = RootsWithHeight(['A',2],[1,1]); R1340Roots with height of cartan type ['A', 2] and dominant weight Lambda[1] + Lambda[2]1341sage: y = R(x,1); y1342(alpha[1] + alpha[2], 1)1343"""1344root = self._root_lattice.from_vector(vector(root))1345return self.element_class(self, root, height)13461347def _an_element_(self):1348r"""13491350EXAMPLES::13511352sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1353sage: R = RootsWithHeight(['A',2],[3,2])1354sage: R._an_element_()1355(alpha[1], 0)1356"""1357return self( self._root_lattice.from_vector(vector([1])), 0 )13581359class RootsWithHeightElement(Element):1360r"""1361Element of :class:`RootsWithHeight`.13621363INPUT:13641365- ``root`` -- A positive root `\beta` in our root system1366- ``height`` -- Is an integer, such that1367`0 \leq l \leq \langle \lambda, \beta^{\vee} \rangle`13681369EXAMPLES::13701371sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1372sage: rl = RootSystem(['A',2]).root_lattice()1373sage: x = rl.from_vector(vector([1,1])); x1374alpha[1] + alpha[2]1375sage: R = RootsWithHeight(['A',2],[1,1]); R1376Roots with height of cartan type ['A', 2] and dominant weight Lambda[1] + Lambda[2]1377sage: y = R(x, 1); y1378(alpha[1] + alpha[2], 1)1379"""1380def __init__(self, parent, root, height):1381r"""1382Initialize ``self``.13831384EXAMPLES::13851386sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1387sage: rl = RootSystem(['A',2]).root_lattice()1388sage: x = rl.from_vector(vector([1,1]))1389sage: R = RootsWithHeight(['A',2],[3,2])1390sage: y = R(x, 1); y1391(alpha[1] + alpha[2], 1)1392sage: TestSuite(x).run()1393"""1394Element.__init__(self, parent)1395max_height = parent._max_height(root)13961397# make sure the height is in the right range, this also catches negative1398# roots13991400if not 0 <= height < max_height:1401raise ValueError("%d out of allowed range [%d,%d)"%(height, 0, max_height))14021403v = [height/max_height]1404v.extend( [ x/max_height for x in root.associated_coroot().to_vector() ] )1405#v.insert(0, height/max_height)14061407# the map from (root, height) --> _cmp_v is injective14081409self._cmp_v = tuple(v)1410self.root = root1411self.height = height14121413def _repr_(self):1414r"""1415Return a string representation of ``self``.14161417EXAMPLES::14181419sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1420sage: R = RootsWithHeight(['A',2],[3,2])1421sage: rl = RootSystem(['A',2]).root_lattice()1422sage: vec = rl.from_vector(vector([1,1])); vec1423alpha[1] + alpha[2]1424sage: R(vec,1)1425(alpha[1] + alpha[2], 1)1426"""1427return "(%s, %s)" % (self.root, self.height)14281429def __hash__(self):1430r"""14311432EXAMPLES::14331434sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1435sage: R = RootsWithHeight(['A',2],[3,2])1436sage: rl = RootSystem(['A',2]).root_lattice()1437sage: root = rl.from_vector(vector([1,1]))1438sage: vec = R(root,0)1439sage: hash(vec) == hash(vec)1440True1441"""1442return hash(self._cmp_v)14431444def __eq__(self, other):1445r"""14461447EXAMPLES::14481449sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1450sage: R = RootsWithHeight(['A',2],[3,2])1451sage: rl = RootSystem(['A',2]).root_lattice()1452sage: v1 = rl.from_vector(vector([1,1]))1453sage: v2 = rl.from_vector(vector([1]))1454sage: x1 = R(v1,1) ; x2 = R(v1,0) ; x3 = R(v2,1)1455sage: x1.__eq__(x1)1456True1457sage: x1.__eq__(x2)1458False1459sage: x1.__eq__(x3)1460False1461"""1462try:1463return self._cmp_v == other._cmp_v1464except (NameError, AttributeError):1465return False14661467def __cmp__(self, other):1468r"""1469Define a total order on :class:`RootsWithHeightElement`. This defines1470the initial `\lambda`-chain.14711472EXAMPLES::14731474sage: from sage.combinat.crystals.alcove_path import RootsWithHeight1475sage: R = RootsWithHeight(['A',2],[3,2])1476sage: rl = RootSystem(['A',2]).root_lattice()1477sage: v1 = rl.from_vector(vector([1,1]))1478sage: v2 = rl.from_vector(vector([1]))1479sage: x1 = R(v1,1) ; x2 = R(v1,0) ; x3 = R(v2,1)1480sage: x1.__cmp__(x2)148111482sage: x1.__cmp__(x3)1483-114841485"""1486# I suspect that if you redefine this method to produce a1487# different (valid) `\lambda`-chain the rest of the1488# code should still work.1489#todo: check if self and other have the same parent ?1490#assert self.parent() is other.parent(), "elements have different parents"1491return cmp(self._cmp_v, other._cmp_v)14921493RootsWithHeight.Element = RootsWithHeightElement14941495#####################################################################1496# Test code, by comparing with existing crystal implementations.1497#####################################################################14981499def _test_some_specific_examples(clss=CrystalOfAlcovePaths):1500r"""1501Test against some specific (finite type) examples.15021503EXAMPLES::15041505sage: from sage.combinat.crystals.alcove_path import _test_some_specific_examples1506sage: _test_some_specific_examples(CrystalOfAlcovePaths)1507G2 example passed.1508C3 example passed.1509B3 example 1 passed.1510B3 example 2 passed.1511True1512"""1513# This appears in Lenart.1514C = clss(['G',2],[0,1])1515G = C.digraph()15161517GT = DiGraph({1518() : {(0) : 2 },1519(0) : {(0,8) : 1 },1520(0,1) : {(0,1,7) : 2 },1521(0,1,2) : {(0,1,2,9) : 1 },1522(0,1,2,3) : {(0,1,2,3,4) : 2 },1523(0,1,2,6) : {(0,1,2,3) : 1 },1524(0,1,2,9) : {(0,1,2,6) : 1 },1525(0,1,7) : {(0,1,2) : 2 },1526(0,1,7,9) : {(0,1,2,9) : 2 },1527(0,5) : {(0,1) : 1, (0,5,7) : 2 },1528(0,5,7) : {(0,5,7,9) : 1 },1529(0,5,7,9) : {(0,1,7,9) : 1 },1530(0,8) : {(0,5) : 1 }1531})15321533if (G.is_isomorphic(GT) != True):1534return False1535else:1536print "G2 example passed."15371538# Some examples from Hong--Kang:15391540# type C, ex. 8.3.5, pg. 1891541C = clss(['C',3],[0,0,1])1542G = C.digraph()1543GT = DiGraph({1544():{ (0): 3},1545(0):{ (0, 6): 2},1546(0, 1):{ (0, 1, 3): 3, (0, 1, 7): 1},1547(0, 1, 2):{ (0, 1, 2, 3): 3},1548(0, 1, 2, 3):{ (0, 1, 2, 3, 8): 2},1549(0, 1, 2, 3, 4):{ (0, 1, 2, 3, 4, 5): 3},1550(0, 1, 2, 3, 8):{ (0, 1, 2, 3, 4): 2},1551(0, 1, 3):{ (0, 1, 3, 7): 1},1552(0, 1, 3, 7):{ (0, 1, 2, 3): 1, (0, 1, 3, 7, 8): 2},1553(0, 1, 3, 7, 8):{ (0, 1, 2, 3, 8): 1},1554(0, 1, 7):{ (0, 1, 2): 1, (0, 1, 3, 7): 3},1555(0, 6):{ (0, 1): 2, (0, 6, 7): 1},1556(0, 6, 7):{ (0, 1, 7): 2}1557})15581559if (G.is_isomorphic(GT) != True):1560return False1561else:1562print "C3 example passed."15631564# type B, fig. 8.1 pg. 1721565C = clss(['B',3],[2,0,0])1566G = C.digraph()15671568GT = DiGraph({1569():{ (6): 1},1570(0):{ (0, 7): 2},1571(0, 1):{ (0, 1, 11): 3},1572(0, 1, 2):{ (0, 1, 2, 9): 2},1573(0, 1, 2, 3):{ (0, 1, 2, 3, 10): 1},1574(0, 1, 2, 3, 10):{ (0, 1, 2, 3, 4): 1},1575(0, 1, 2, 9):{ (0, 1, 2, 3): 2, (0, 1, 2, 9, 10): 1},1576(0, 1, 2, 9, 10):{ (0, 1, 2, 3, 10): 2},1577(0, 1, 5):{ (0, 1, 2): 3, (0, 1, 5, 9): 2},1578(0, 1, 5, 9):{ (0, 1, 2, 9): 3, (0, 1, 5, 9, 10): 1},1579(0, 1, 5, 9, 10):{ (0, 1, 2, 9, 10): 3},1580(0, 1, 8):{ (0, 1, 5): 3},1581(0, 1, 8, 9):{ (0, 1, 5, 9): 3, (0, 1, 8, 9, 10): 1},1582(0, 1, 8, 9, 10):{ (0, 1, 5, 9, 10): 3},1583(0, 1, 11):{ (0, 1, 8): 3},1584(0, 7):{ (0, 1): 2, (0, 7, 11): 3},1585(0, 7, 8):{ (0, 7, 8, 9): 2},1586(0, 7, 8, 9):{ (0, 1, 8, 9): 2},1587(0, 7, 8, 9, 10):{ (0, 1, 8, 9, 10): 2},1588(0, 7, 11):{ (0, 1, 11): 2, (0, 7, 8): 3},1589(6):{ (0): 1, (6, 7): 2},1590(6, 7):{ (0, 7): 1, (6, 7, 11): 3},1591(6, 7, 8):{ (0, 7, 8): 1, (6, 7, 8, 9): 2},1592(6, 7, 8, 9):{ (6, 7, 8, 9, 10): 1},1593(6, 7, 8, 9, 10):{ (0, 7, 8, 9, 10): 1},1594(6, 7, 11):{ (0, 7, 11): 1, (6, 7, 8): 3}1595})15961597if (G.is_isomorphic(GT) != True):1598return False1599else:1600print "B3 example 1 passed."16011602C = clss(['B',3],[0,1,0])1603G = C.digraph()16041605GT = DiGraph({1606():{ (0): 2},1607(0):{ (0, 1): 1, (0, 7): 3},1608(0, 1):{ (0, 1, 7): 3},1609(0, 1, 2):{ (0, 1, 2, 8): 2},1610(0, 1, 2, 3):{ (0, 1, 2, 3, 5): 1, (0, 1, 2, 3, 9): 3},1611(0, 1, 2, 3, 4):{ (0, 1, 2, 3, 4, 5): 1},1612(0, 1, 2, 3, 4, 5):{ (0, 1, 2, 3, 4, 5, 6): 2},1613(0, 1, 2, 3, 5):{ (0, 1, 2, 3, 5, 9): 3},1614(0, 1, 2, 3, 5, 9):{ (0, 1, 2, 3, 4, 5): 3},1615(0, 1, 2, 3, 9):{ (0, 1, 2, 3, 4): 3, (0, 1, 2, 3, 5, 9): 1},1616(0, 1, 2, 5):{ (0, 1, 2, 3, 5): 2},1617(0, 1, 2, 8):{ (0, 1, 2, 3): 2},1618(0, 1, 2, 8, 9):{ (0, 1, 2, 3, 9): 2},1619(0, 1, 7):{ (0, 1, 2): 3, (0, 1, 7, 8): 2},1620(0, 1, 7, 8):{ (0, 1, 7, 8, 9): 3},1621(0, 1, 7, 8, 9):{ (0, 1, 2, 8, 9): 3},1622(0, 2):{ (0, 1, 2): 1, (0, 2, 5): 2},1623(0, 2, 5):{ (0, 2, 5, 8): 1},1624(0, 2, 5, 8):{ (0, 1, 2, 5): 1},1625(0, 7):{ (0, 1, 7): 1, (0, 2): 3}1626})16271628if (G.is_isomorphic(GT) != True):1629return False1630else:1631print "B3 example 2 passed."16321633# type B, fig. 8.3 pg. 17416341635return True16361637def compare_graphs(g1, g2, node1, node2):1638r"""1639Compare two edge-labeled :class:`graphs <DiGraph>` obtained from1640``Crystal.digraph()``, starting from the root nodes of each graph.16411642- ``g1`` -- :class:`graphs <DiGraph>`, first digraph1643- ``g2`` -- :class:`graphs <DiGraph>`, second digraph1644- ``node1`` -- element of ``g1``1645- ``node2`` -- element of ``g2``16461647Traverse ``g1`` starting at ``node1`` and compare this graph with1648the one obtained by traversing ``g2`` starting with ``node2``.1649If the graphs match (including labels) then return ``True``.1650Return ``False`` otherwise.16511652EXAMPLES::16531654sage: from sage.combinat.crystals.alcove_path import compare_graphs1655sage: G1 = sage.combinat.crystals.all.CrystalOfTableaux(['A',3], shape=[1,1]).digraph()1656sage: C = CrystalOfAlcovePaths(['A',3],[0,1,0])1657sage: G2 = C.digraph()1658sage: compare_graphs(G1, G2, C( () ), G2.vertices()[0])1659True1660"""1661for out_edge in g1.outgoing_edges( node1 ):1662matched = False1663for o2 in g2.outgoing_edges( node2 ):1664if o2[2] == out_edge[2]:1665if matched == True:1666print "ERROR: Two edges with the same label for ", out_edge, " exist."1667return False1668matched = True1669result = compare_graphs(g1, g2, out_edge[1], o2[1])1670if result == False:1671return False1672if matched == False:1673print "ERROR: No matching edge for ", out_edge, "."1674return False1675return True16761677def _test_against_tableaux(R, N, k, clss=CrystalOfAlcovePaths):1678r"""1679Tests :class:`CrystalOfAlcovePaths` against all of the tableaux crystals1680of type `R` in rank `N` with highest weight given by a partition of `k`.16811682EXAMPLES::16831684sage: from sage.combinat.crystals.alcove_path import _test_against_tableaux1685sage: _test_against_tableaux(['A',3], 3, 2)1686** Shape [2]1687T has 10 nodes.1688C weight [2, 0, 0]1689C has 10 nodes.1690Compare graphs: True1691** Shape [1, 1]1692T has 6 nodes.1693C weight [0, 1, 0]1694C has 6 nodes.1695Compare graphs: True1696"""1697shapes = Partitions(k).list()1698for shape in shapes:1699print "** Shape ", shape1700T = CrystalOfTableaux(R, shape = shape)1701ct = len(T.list())1702print " T has ", ct, " nodes."1703#T.digraph().show(edge_labels=True)1704H = T.digraph()1705weight = T.module_generators[0].weight()1706w = [ weight.scalar(RootSystem(R).ambient_space().simple_coroot(i)) for i in range(1,N+1) ]1707print " C weight ", w17081709C = clss(R , w)17101711cc = len(C.list())1712#C.digraph().show(edge_labels=True)1713G = C.digraph()1714print " C has ", cc, " nodes."1715if cc != ct:1716print "FAIL: number of nodes differ.", cc, ct1717return1718print " Compare graphs: ", compare_graphs(G, H, C(()), H.vertices()[0])17191720def _test_with_lspaths_crystal(cartan_type, weight, depth=10):1721r"""1722Test if the digraphs generated are isomorphic to the ones generated by1723lspath model.17241725INPUT:17261727- ``cartan_type`` -- Cartan type of a finite or affine untwisted root1728system.1729- ``weight`` -- dominant weight as a list of (integral) coefficients of the1730fundamental weights.1731- ``depth`` -- starting at the module generator how deep do you want to1732generate the crystal, useful for affine types.17331734EXAMPLES::17351736sage: from sage.combinat.crystals.alcove_path import _test_with_lspaths_crystal1737sage: _test_with_lspaths_crystal(['A',3,1],[1,0,0,0],10) #long time1738True1739sage: _test_with_lspaths_crystal(['G',2,1],[1,0,0,0,0],10) #long time1740True1741"""1742G1 = CrystalOfAlcovePaths(cartan_type, weight).digraph_fast(depth)1743C = CrystalOfLSPaths(cartan_type, weight)1744G2 = C.digraph(subset=C.subcrystal(max_depth=depth, direction='lower'))17451746return G1.is_isomorphic(G2, edge_labels=True)1747174817491750