Path: blob/master/src/sage/combinat/crystals/littelmann_path.py
8817 views
r"""1Littelmann paths23AUTHORS:45- Mark Shimozono, Anne Schilling (2012): Initial version6- Anne Schilling (2013): Implemented :class:`CrystalOfProjectedLevelZeroLSPaths`7"""8#****************************************************************************9# Copyright (C) 2012 Mark Shimozono10# Anne Schilling11#12# Distributed under the terms of the GNU General Public License (GPL)13#14# This code is distributed in the hope that it will be useful,15# but WITHOUT ANY WARRANTY; without even the implied warranty of16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU17# General Public License for more details.18#19# The full text of the GPL is available at:20#21# http://www.gnu.org/licenses/22#****************************************************************************2324from sage.misc.cachefunc import cached_in_parent_method25from sage.structure.unique_representation import UniqueRepresentation26from sage.structure.element_wrapper import ElementWrapper27from sage.structure.parent import Parent28from sage.categories.crystals import Crystals29from sage.categories.highest_weight_crystals import HighestWeightCrystals30from sage.categories.regular_crystals import RegularCrystals31from sage.categories.finite_crystals import FiniteCrystals32from sage.categories.classical_crystals import ClassicalCrystals33from sage.categories.infinite_enumerated_sets import InfiniteEnumeratedSets34from sage.combinat.root_system.cartan_type import CartanType35from sage.combinat.root_system.weyl_group import WeylGroup36from sage.rings.integer import Integer37from sage.rings.rational_field import QQ38from sage.combinat.root_system.root_system import RootSystem39from sage.functions.other import floor40from sage.misc.latex import latex414243class CrystalOfLSPaths(UniqueRepresentation, Parent):44r"""45Crystal graph of LS paths generated from the straight-line path to a given weight.4647INPUT:4849- ``cartan_type`` -- (optional) the Cartan type of a finite or affine root system50- ``starting_weight`` -- a weight; if ``cartan_type`` is given, then the weight should51be given as a list of coefficients of the fundamental weights, otherwise it should52be given in the ``weight_space`` basis; for affine highest weight crystals, one needs53to use the extended weight space.5455The crystal class of piecewise linear paths in the weight space,56generated from a straight-line path from the origin to a given57element of the weight lattice.5859OUTPUT:6061- a tuple of weights defining the directions of the piecewise linear segments6263EXAMPLES::6465sage: R = RootSystem(['A',2,1])66sage: La = R.weight_space(extended = True).basis()67sage: B = CrystalOfLSPaths(La[2]-La[0]); B68The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]6970sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1]); C71The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]72sage: B == C73True74sage: c = C.module_generators[0]; c75(-Lambda[0] + Lambda[2],)76sage: [c.f(i) for i in C.index_set()]77[None, None, (Lambda[1] - Lambda[2],)]7879sage: R = C.R; R80Root system of type ['A', 2, 1]81sage: Lambda = R.weight_space().basis(); Lambda82Finite family {0: Lambda[0], 1: Lambda[1], 2: Lambda[2]}83sage: b=C(tuple([-Lambda[0]+Lambda[2]]))84sage: b==c85True86sage: b.f(2)87(Lambda[1] - Lambda[2],)8889For classical highest weight crystals we can also compare the results with the tableaux implementation::9091sage: C = CrystalOfLSPaths(['A',2],[1,1])92sage: list(set(C.list()))93[(-Lambda[1] - Lambda[2],), (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2]), (-Lambda[1] + 2*Lambda[2],),94(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2]), (Lambda[1] - 2*Lambda[2],), (-2*Lambda[1] + Lambda[2],),95(2*Lambda[1] - Lambda[2],), (Lambda[1] + Lambda[2],)]96sage: C.cardinality()97898sage: B = CrystalOfTableaux(['A',2],shape=[2,1])99sage: B.cardinality()1008101sage: B.digraph().is_isomorphic(C.digraph())102True103104Make sure you use the weight space and not the weight lattice for your weights::105106sage: R = RootSystem(['A',2,1])107sage: La = R.weight_lattice(extended = True).basis()108sage: B = CrystalOfLSPaths(La[2]); B109Traceback (most recent call last):110...111ValueError: Please use the weight space, rather than weight lattice for your weights112113REFERENCES:114115.. [Littelmann95] P. Littelmann, Paths and root operators in representation116theory. Ann. of Math. (2) 142 (1995), no. 3, 499-525.117"""118119@staticmethod120def __classcall_private__(cls, starting_weight, cartan_type = None):121"""122Classcall to mend the input.123124Internally, the CrystalOfLSPaths code works with a ``starting_weight`` that125is in the ``weight_space`` associated to the crystal. The user can, however,126also input a ``cartan_type`` and the coefficients of the fundamental weights127as ``starting_weight``. This code transforms the input into the right128format (also necessary for UniqueRepresentation).129130TESTS::131132sage: CrystalOfLSPaths(['A',2,1],[-1,0,1])133The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]134135sage: R = RootSystem(['B',2,1])136sage: La = R.weight_space().basis()137sage: C = CrystalOfLSPaths(['B',2,1],[0,0,1])138sage: B = CrystalOfLSPaths(La[2])139sage: B is C140True141"""142if cartan_type is not None:143cartan_type, starting_weight = CartanType(starting_weight), cartan_type144if cartan_type.is_affine():145extended = True146else:147extended = False148149R = RootSystem(cartan_type)150P = R.weight_space(extended = extended)151Lambda = P.basis()152offset = R.index_set()[Integer(0)]153starting_weight = P.sum(starting_weight[j-offset]*Lambda[j] for j in R.index_set())154155return super(CrystalOfLSPaths, cls).__classcall__(cls, starting_weight)156157def __init__(self, starting_weight):158"""159EXAMPLES::160161sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1]); C162The crystal of LS paths of type ['A', 2, 1] and weight -Lambda[0] + Lambda[2]163sage: C.R164Root system of type ['A', 2, 1]165sage: C.weight166-Lambda[0] + Lambda[2]167sage: C.weight.parent()168Extended weight space over the Rational Field of the Root system of type ['A', 2, 1]169sage: C.module_generators170[(-Lambda[0] + Lambda[2],)]171172TESTS::173174sage: C = CrystalOfLSPaths(['A',2,1], [-1,0,1])175sage: TestSuite(C).run()176sage: C = CrystalOfLSPaths(['E',6], [1,0,0,0,0,0])177sage: TestSuite(C).run()178"""179cartan_type = starting_weight.parent().cartan_type()180self.R = RootSystem(cartan_type)181self.weight = starting_weight182if not self.weight.parent().base_ring().has_coerce_map_from(QQ):183raise ValueError, "Please use the weight space, rather than weight lattice for your weights"184self._cartan_type = cartan_type185self._name = "The crystal of LS paths of type %s and weight %s"%(cartan_type,starting_weight)186if cartan_type.is_affine():187if all(i>=0 for i in starting_weight.coefficients()):188Parent.__init__( self, category = (RegularCrystals(),189HighestWeightCrystals(),190InfiniteEnumeratedSets()) )191elif starting_weight.parent().is_extended():192Parent.__init__(self, category = (RegularCrystals(), InfiniteEnumeratedSets()))193else:194Parent.__init__(self, category = (RegularCrystals(), FiniteCrystals()))195else:196Parent.__init__(self, category = ClassicalCrystals())197198if starting_weight == starting_weight.parent().zero():199initial_element = self(tuple([]))200else:201initial_element = self(tuple([starting_weight]))202self.module_generators = [initial_element]203204def _repr_(self):205"""206EXAMPLES::207208sage: CrystalOfLSPaths(['B',3],[1,1,0]) # indirect doctest209The crystal of LS paths of type ['B', 3] and weight Lambda[1] + Lambda[2]210"""211return self._name212213class Element(ElementWrapper):214"""215TESTS::216217sage: C = CrystalOfLSPaths(['E',6],[1,0,0,0,0,0])218sage: c=C.an_element()219sage: TestSuite(c).run()220"""221222def endpoint(self):223r"""224Computes the endpoint of the path.225226EXAMPLES::227228sage: C = CrystalOfLSPaths(['A',2],[1,1])229sage: b = C.module_generators[0]230sage: b.endpoint()231Lambda[1] + Lambda[2]232sage: b.f_string([1,2,2,1])233(-Lambda[1] - Lambda[2],)234sage: b.f_string([1,2,2,1]).endpoint()235-Lambda[1] - Lambda[2]236sage: b.f_string([1,2])237(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])238sage: b.f_string([1,2]).endpoint()2390240sage: b = C([])241sage: b.endpoint()2420243"""244if len(self.value) > 0:245return sum(self.value)246return self.parent().weight.parent().zero()247#return self.parent().R.weight_space(extended = self.parent().extended).zero()248249def compress(self):250r"""251Merges consecutive positively parallel steps present in the path.252253EXAMPLES::254255sage: C = CrystalOfLSPaths(['A',2],[1,1])256sage: Lambda = C.R.weight_space().fundamental_weights(); Lambda257Finite family {1: Lambda[1], 2: Lambda[2]}258sage: c = C(tuple([1/2*Lambda[1]+1/2*Lambda[2], 1/2*Lambda[1]+1/2*Lambda[2]]))259sage: c.compress()260(Lambda[1] + Lambda[2],)261"""262def positively_parallel_weights(v, w):263"""264Checks whether the vectors ``v`` and ``w`` are positive scalar multiples of each other.265"""266supp = v.support()267if len(supp) > 0:268i = supp[0]269if v[i]*w[i] > 0 and v[i]*w == w[i]*v:270return True271return False272273if len(self.value) == 0:274return self275q = []276curr = self.value[0]277for i in range(1,len(self.value)):278if positively_parallel_weights(curr,self.value[i]):279curr = curr + self.value[i]280else:281q.append(curr)282curr = self.value[i]283q.append(curr)284return self.parent()(tuple(q))285286def split_step(self, which_step, r):287r"""288Splits indicated step into two parallel steps of relative lengths `r` and `1-r`.289290INPUT:291292- ``which_step`` -- a position in the tuple ``self``293- ``r`` -- a rational number between 0 and 1294295EXAMPLES::296297sage: C = CrystalOfLSPaths(['A',2],[1,1])298sage: b = C.module_generators[0]299sage: b.split_step(0,1/3)300(1/3*Lambda[1] + 1/3*Lambda[2], 2/3*Lambda[1] + 2/3*Lambda[2])301"""302assert which_step in range(len(self.value))303v = self.value[which_step]304return self.parent()(self.value[:which_step]+tuple([r*v,(1-r)*v])+self.value[which_step+1:])305306def reflect_step(self, which_step, i):307r"""308Apply the `i`-th simple reflection to the indicated step in ``self``.309310EXAMPLES::311312sage: C = CrystalOfLSPaths(['A',2],[1,1])313sage: b = C.module_generators[0]314sage: b.reflect_step(0,1)315(-Lambda[1] + 2*Lambda[2],)316sage: b.reflect_step(0,2)317(2*Lambda[1] - Lambda[2],)318"""319assert i in self.index_set()320assert which_step in range(len(self.value))321return self.parent()(self.value[:which_step]+tuple([self.value[which_step].simple_reflection(i)])+self.value[which_step+1:])322323def _string_data(self, i):324r"""325Computes the `i`-string data of ``self``.326327TESTS::328329sage: C = CrystalOfLSPaths(['A',2],[1,1])330sage: b = C.module_generators[0]331sage: b._string_data(1)332()333sage: b._string_data(2)334()335sage: b.f(1)._string_data(1)336((0, -1, -1),)337sage: b.f(1).f(2)._string_data(2)338((0, -1, -1),)339"""340if len(self.value) == 0:341return ()342# get the i-th simple coroot343alv = self.value[0].parent().alphacheck()[i]344# Compute the i-heights of the steps of vs345steps = [v.scalar(alv) for v in self.value]346# Get the wet step data347minima_pos = []348ps = 0349psmin = 0350for ix in range(len(steps)):351ps = ps + steps[ix]352if ps < psmin:353minima_pos.append((ix,ps,steps[ix]))354psmin = ps355return tuple(minima_pos)356357def epsilon(self, i):358r"""359Returns the distance to the beginning of the `i`-string.360361This method overrides the generic implementation in the category of crystals362since this computation is more efficient.363364EXAMPLES::365366sage: C = CrystalOfLSPaths(['A',2],[1,1])367sage: [c.epsilon(1) for c in C]368[0, 1, 0, 0, 1, 0, 1, 2]369sage: [c.epsilon(2) for c in C]370[0, 0, 1, 2, 1, 1, 0, 0]371"""372return self.e(i,length_only=True)373374def phi(self, i):375r"""376Returns the distance to the end of the `i`-string.377378This method overrides the generic implementation in the category of crystals379since this computation is more efficient.380381EXAMPLES::382383sage: C = CrystalOfLSPaths(['A',2],[1,1])384sage: [c.phi(1) for c in C]385[1, 0, 0, 1, 0, 2, 1, 0]386sage: [c.phi(2) for c in C]387[1, 2, 1, 0, 0, 0, 0, 1]388"""389return self.f(i,length_only=True)390391def e(self, i, power=1, to_string_end=False, length_only=False):392r"""393Returns the `i`-th crystal raising operator on ``self``.394395INPUT:396397- ``i`` -- element of the index set of the underlying root system398- ``power`` -- positive integer; specifies the power of the raising operator399to be applied (default: 1)400- ``to_string_end`` -- boolean; if set to True, returns the dominant end of the401`i`-string of ``self``. (default: False)402- ``length_only`` -- boolean; if set to True, returns the distance to the dominant403end of the `i`-string of ``self``.404405EXAMPLES::406407sage: C = CrystalOfLSPaths(['A',2],[1,1])408sage: c = C[2]; c409(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])410sage: c.e(1)411sage: c.e(2)412(-Lambda[1] + 2*Lambda[2],)413sage: c.e(2,to_string_end=True)414(-Lambda[1] + 2*Lambda[2],)415sage: c.e(1,to_string_end=True)416(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])417sage: c.e(1,length_only=True)4180419"""420assert i in self.index_set()421data = self._string_data(i)422# compute the minimum i-height M on the path423if len(data) == 0:424M = 0425else:426M = data[-1][1]427max_raisings = floor(-M)428if length_only:429return max_raisings430# set the power of e_i to apply431if to_string_end:432p = max_raisings433else:434p = power435if p > max_raisings:436return None437438# copy the vector sequence into a working vector sequence ws439#!!! ws only needs to be the actual vector sequence, not some440#!!! fancy crystal graph element441ws = self.parent()(self.value)442443ix = len(data)-1444while ix >= 0 and data[ix][1] < M + p:445# get the index of the current step to be processed446j = data[ix][0]447# find the i-height where the current step might need to be split448if ix == 0:449prev_ht = M + p450else:451prev_ht = min(data[ix-1][1],M+p)452# if necessary split the step. Then reflect the wet part.453if data[ix][1] - data[ix][2] > prev_ht:454ws = ws.split_step(j,1-(prev_ht-data[ix][1])/(-data[ix][2]))455ws = ws.reflect_step(j+1,i)456else:457ws = ws.reflect_step(j,i)458ix = ix - 1459#!!! at this point we should return the fancy crystal graph element460#!!! corresponding to the humble vector sequence ws461return self.parent()(ws.compress())462463def dualize(self):464r"""465Returns dualized path.466467EXAMPLES::468469sage: C = CrystalOfLSPaths(['A',2],[1,1])470sage: for c in C:471... print c, c.dualize()472...473(Lambda[1] + Lambda[2],) (-Lambda[1] - Lambda[2],)474(-Lambda[1] + 2*Lambda[2],) (Lambda[1] - 2*Lambda[2],)475(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2]) (1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])476(Lambda[1] - 2*Lambda[2],) (-Lambda[1] + 2*Lambda[2],)477(-Lambda[1] - Lambda[2],) (Lambda[1] + Lambda[2],)478(2*Lambda[1] - Lambda[2],) (-2*Lambda[1] + Lambda[2],)479(-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2]) (-Lambda[1] + 1/2*Lambda[2], Lambda[1] - 1/2*Lambda[2])480(-2*Lambda[1] + Lambda[2],) (2*Lambda[1] - Lambda[2],)481"""482if len(self.value) == 0:483return self484dual_path = [-v for v in self.value]485dual_path.reverse()486return self.parent()(tuple(dual_path))487488def f(self, i, power=1, to_string_end=False, length_only=False):489r"""490Returns the `i`-th crystal lowering operator on ``self``.491492INPUT:493494- ``i`` -- element of the index set of the underlying root system495- ``power`` -- positive integer; specifies the power of the lowering operator496to be applied (default: 1)497- ``to_string_end`` -- boolean; if set to True, returns the anti-dominant end of the498`i`-string of ``self``. (default: False)499- ``length_only`` -- boolean; if set to True, returns the distance to the anti-dominant500end of the `i`-string of ``self``.501502EXAMPLES::503504sage: C = CrystalOfLSPaths(['A',2],[1,1])505sage: c = C.module_generators[0]506sage: c.f(1)507(-Lambda[1] + 2*Lambda[2],)508sage: c.f(1,power=2)509sage: c.f(2)510(2*Lambda[1] - Lambda[2],)511sage: c.f(2,to_string_end=True)512(2*Lambda[1] - Lambda[2],)513sage: c.f(2,length_only=True)5141515516sage: C = CrystalOfLSPaths(['A',2,1],[-1,-1,2])517sage: c = C.module_generators[0]518sage: c.f(2,power=2)519(Lambda[0] + Lambda[1] - 2*Lambda[2],)520"""521dual_path = self.dualize()522dual_path = dual_path.e(i, power, to_string_end, length_only)523if length_only:524return dual_path525if dual_path == None:526return None527return dual_path.dualize()528529def s(self, i):530r"""531Computes the reflection of ``self`` along the `i`-string.532533This method is more efficient than the generic implementation since it uses534powers of `e` and `f` in the Littelmann model directly.535536EXAMPLES::537538sage: C = CrystalOfLSPaths(['A',2],[1,1])539sage: c = C.module_generators[0]540sage: c.s(1)541(-Lambda[1] + 2*Lambda[2],)542sage: c.s(2)543(2*Lambda[1] - Lambda[2],)544545sage: C = CrystalOfLSPaths(['A',2,1],[-1,0,1])546sage: c = C.module_generators[0]; c547(-Lambda[0] + Lambda[2],)548sage: c.s(2)549(Lambda[1] - Lambda[2],)550sage: c.s(1)551(-Lambda[0] + Lambda[2],)552sage: c.f(2).s(1)553(Lambda[0] - Lambda[1],)554"""555ph = self.phi(i)556ep = self.epsilon(i)557diff = ph - ep558if diff >= 0:559return self.f(i, power=diff)560else:561return self.e(i, power=-diff)562563def _latex_(self):564r"""565Latex method for ``self``.566567EXAMPLES::568569sage: C = CrystalOfLSPaths(['A',2],[1,1])570sage: c = C.module_generators[0]571sage: c._latex_()572[\Lambda_{1} + \Lambda_{2}]573"""574return [latex(p) for p in self.value]575576577class CrystalOfProjectedLevelZeroLSPaths(CrystalOfLSPaths):578r"""579Crystal of projected level zero LS paths.580581INPUT:582583- ``weight`` -- a dominant weight of the weight space of an affine Kac-Moody root system584585When ``weight`` is just a single fundamental weight `\Lambda_r`, this crystal is586isomorphic to a Kirillov-Reshetikhin (KR) crystal, see also587:meth:`sage.combinat.crystals.kirillov_reshetikhin.KirillovReshetikhinCrystalFromLSPaths`.588For general weights, it is isomorphic to a tensor product of single-column KR crystals.589590EXAMPLES::591592sage: R = RootSystem(['C',3,1])593sage: La = R.weight_space().basis()594sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])595sage: LS.cardinality()59684597sage: GLS = LS.digraph()598599sage: K1 = KirillovReshetikhinCrystal(['C',3,1],1,1)600sage: K3 = KirillovReshetikhinCrystal(['C',3,1],3,1)601sage: T = TensorProductOfCrystals(K3,K1)602sage: T.cardinality()60384604sage: GT = T.digraph() # long time605sage: GLS.is_isomorphic(GT, edge_labels = True) # long time606True607608TESTS::609610sage: ct = CartanType(['A',4,2]).dual()611sage: P = RootSystem(ct).weight_space()612sage: La = P.fundamental_weights()613sage: C = CrystalOfProjectedLevelZeroLSPaths(La[1])614sage: C.list()615[(-Lambda[0] + Lambda[1],),616(Lambda[0] - Lambda[1],),617(Lambda[1] - 2*Lambda[2],),618(-Lambda[1] + 2*Lambda[2],),619(1/2*Lambda[1] - Lambda[2], -1/2*Lambda[1] + Lambda[2])]620"""621622@staticmethod623def __classcall_private__(cls, weight):624"""625Classcall to mend the input.626627Internally, the CrystalOfProjectedLevelZeroLSPaths uses a level zero weight,628which is passed on to CrystalOfLSPaths. ``weight`` is first coerced to629a level zero weight.630631TESTS::632633sage: R = RootSystem(['C',3,1])634sage: La = R.weight_space().basis()635sage: C = CrystalOfProjectedLevelZeroLSPaths(La[1] + La[2])636sage: C2 = CrystalOfProjectedLevelZeroLSPaths(La[1] + La[2])637sage: C is C2638True639"""640La = weight.parent().basis()641weight = weight - (weight.level())*La[0]/(La[0].level())642return super(CrystalOfLSPaths, cls).__classcall__(cls, weight)643644def one_dimensional_configuration_sum(self, q = None, group_components = True):645r"""646Compute the one-dimensional configuration sum.647648INPUT:649650- ``q`` -- (default: ``None``) a variable or ``None``; if ``None``,651a variable ``q`` is set in the code652- ``group_components`` -- (default: ``True``) boolean; if ``True``,653then the terms are grouped by classical component654655The one-dimensional configuration sum is the sum of the weights of all elements in the crystal656weighted by the energy function. For untwisted types it uses the parabolic quantum Bruhat graph, see [LNSSS2013]_.657In the dual-of-untwisted case, the parabolic quantum Bruhat graph is defined by658exchanging the roles of roots and coroots (which is still conjectural at this point).659660EXAMPLES::661662sage: R = RootSystem(['A',2,1])663sage: La = R.weight_space().basis()664sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1])665sage: LS.one_dimensional_configuration_sum()666B[-2*Lambda[1] + 2*Lambda[2]] + (q+1)*B[-Lambda[1]]667+ (q+1)*B[Lambda[1] - Lambda[2]] + B[2*Lambda[1]] + B[-2*Lambda[2]] + (q+1)*B[Lambda[2]]668sage: R.<t> = ZZ[]669sage: LS.one_dimensional_configuration_sum(t, False)670B[-2*Lambda[1] + 2*Lambda[2]] + (t+1)*B[-Lambda[1]] + (t+1)*B[Lambda[1] - Lambda[2]]671+ B[2*Lambda[1]] + B[-2*Lambda[2]] + (t+1)*B[Lambda[2]]672673TESTS::674675sage: R = RootSystem(['B',3,1])676sage: La = R.weight_space().basis()677sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])678sage: LS.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum(group_components=False) # long time679True680sage: K1 = KirillovReshetikhinCrystal(['B',3,1],1,1)681sage: K2 = KirillovReshetikhinCrystal(['B',3,1],2,1)682sage: T = TensorProductOfCrystals(K2,K1)683sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum()684True685686sage: R = RootSystem(['D',4,2])687sage: La = R.weight_space().basis()688sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])689sage: K1 = KirillovReshetikhinCrystal(['D',4,2],1,1)690sage: K2 = KirillovReshetikhinCrystal(['D',4,2],2,1)691sage: T = TensorProductOfCrystals(K2,K1)692sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum()693True694695sage: R = RootSystem(['A',5,2])696sage: La = R.weight_space().basis()697sage: LS = CrystalOfProjectedLevelZeroLSPaths(3*La[1])698sage: K1 = KirillovReshetikhinCrystal(['A',5,2],1,1)699sage: T = TensorProductOfCrystals(K1,K1,K1)700sage: T.one_dimensional_configuration_sum() == LS.one_dimensional_configuration_sum()701True702"""703if q is None:704from sage.rings.all import QQ705q = QQ['q'].gens()[0]706P0 = self.weight_lattice_realization().classical()707B = P0.algebra(q.parent())708if group_components:709G = self.digraph(index_set = self.cartan_type().classical().index_set())710C = G.connected_components()711return sum(q**(c[0].energy_function())*B.sum(B(P0(b.weight())) for b in c) for c in C)712return B.sum(q**(b.energy_function())*B(P0(b.weight())) for b in self)713714def is_perfect(self, level=1):715r"""716Checks whether the crystal ``self`` is perfect (of level ``level``).717718INPUT:719720- ``level`` -- (default: 1) positive integer721722A crystal `\mathcal{B}` is perfect of level `\ell` if:723724#. `\mathcal{B}` is isomorphic to the crystal graph of a finite-dimensional `U_q^{'}(\mathfrak{g})`-module.725#. `\mathcal{B}\otimes \mathcal{B}` is connected.726#. There exists a `\lambda\in X`, such that `\mathrm{wt}(\mathcal{B}) \subset \lambda727+ \sum_{i\in I} \mathbb{Z}_{\le 0} \alpha_i` and there is a unique element in `\mathcal{B}` of classical728weight `\lambda`.729#. `\forall b \in \mathcal{B}, \mathrm{level}(\varepsilon (b)) \geq \ell`.730#. `\forall \Lambda` dominant weights of level `\ell`, there exist unique elements731`b_{\Lambda}, b^{\Lambda} \in \mathcal{B}`,732such that `\varepsilon ( b_{\Lambda}) = \Lambda = \varphi( b^{\Lambda})`.733734Points (1)-(3) are known to hold. This method checks points (4) and (5).735736EXAMPLES::737738sage: C = CartanType(['C',2,1])739sage: R = RootSystem(C)740sage: La = R.weight_space().basis()741sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1])742sage: LS.is_perfect()743False744sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[2])745sage: LS.is_perfect()746True747748sage: C = CartanType(['E',6,1])749sage: R = RootSystem(C)750sage: La = R.weight_space().basis()751sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1])752sage: LS.is_perfect()753True754sage: LS.is_perfect(2)755False756757sage: C = CartanType(['D',4,1])758sage: R = RootSystem(C)759sage: La = R.weight_space().basis()760sage: all(CrystalOfProjectedLevelZeroLSPaths(La[i]).is_perfect() for i in [1,2,3,4])761True762763sage: C = CartanType(['A',6,2])764sage: R = RootSystem(C)765sage: La = R.weight_space().basis()766sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])767sage: LS.is_perfect()768True769sage: LS.is_perfect(2)770False771"""772MPhi = []773for b in self:774p = b.Phi().level()775assert p == b.Epsilon().level()776if p < level:777return False778if p == level:779MPhi += [b]780weights = []781I = self.index_set()782rank = len(I)783La = self.weight_lattice_realization().basis()784from sage.combinat.integer_vector import IntegerVectors785for n in range(1,level+1):786for c in IntegerVectors(n, rank):787w = sum(c[i]*La[i] for i in I)788if w.level() == level:789weights.append(w)790return sorted([b.Phi() for b in MPhi]) == sorted(weights)791792class Element(CrystalOfLSPaths.Element):793"""794Element of a crystal of projected level zero LS paths.795"""796797@cached_in_parent_method798def scalar_factors(self):799r"""800Obtain the scalar factors for ``self``.801802Each LS path (or ``self``) can be written as a piecewise linear map803804.. MATH::805806\pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'} - \sigma_{u'-1}) \nu_{u'} + (t-\sigma_{u-1}) \nu_{u}807808for `0<\sigma_1<\sigma_2<\cdots<\sigma_s=1` and `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`.809This method returns the tuple of `(\sigma_1,\ldots,\sigma_s)`.810811EXAMPLES::812813sage: R = RootSystem(['C',3,1])814sage: La = R.weight_space().basis()815sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])816sage: b = LS.module_generators[0]817sage: b.scalar_factors()818[1]819sage: c = b.f(1).f(3).f(2)820sage: c.scalar_factors()821[1/3, 1]822"""823weight = self.parent().weight824l = []825s = 0826for c in self.value:827supp = c.support()828if len(supp) > 0:829for w in weight.orbit():830i = supp[0]831# Check whether the vectors c and w are positive scalar multiples of each other832if i in w.support() and c[i]*w[i] > 0 and c[i]*w == w[i]*c:833s += c[i] / w[i]834l += [s]835break836return l837838@cached_in_parent_method839def weyl_group_representation(self):840r"""841Transforms the weights in the LS path ``self`` to elements in the Weyl group.842843Each LS path can be written as the piecewise linear map:844845.. MATH::846847\pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'} - \sigma_{u'-1}) \nu_{u'} + (t-\sigma_{u-1}) \nu_{u}848849for `0<\sigma_1<\sigma_2<\cdots<\sigma_s=1` and `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`.850Each weight `\nu_u` is also associated to a Weyl group element. This method returns the list851of Weyl group elements associated to the `\nu_u` for `1\le u\le s`.852853EXAMPLES::854855sage: R = RootSystem(['C',3,1])856sage: La = R.weight_space().basis()857sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])858sage: b = LS.module_generators[0]859sage: c = b.f(1).f(3).f(2)860sage: c.weyl_group_representation()861[s2*s3*s1, s3*s1]862"""863cartan = self.parent().weight.parent().cartan_type().classical()864I = cartan.index_set()865W = WeylGroup(cartan,prefix='s')866return [W.from_reduced_word(x.to_dominant_chamber(index_set=I, reduced_word=True)[1]) for x in self.value]867868@cached_in_parent_method869def energy_function(self):870r"""871Return the energy function of ``self``.872873The energy function `D(\pi)` of the level zero LS path `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)`874requires a series of definitions; for simplicity the root system is assumed to be untwisted affine.875876The LS path `\pi` is a piecewise linear map from the unit interval `[0,1]` to the weight lattice.877It is specified by "times" `0=\sigma_0<\sigma_1<\dotsm<\sigma_s=1` and "direction vectors"878`x_u \lambda` where `x_u \in W/W_J` for `1\le u\le s`, and `W_J` is the879stabilizer of `\lambda` in the finite Weyl group `W`. Precisely,880881.. MATH::882883\pi(t)=\sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1})x_{u'}\lambda+(t-\sigma_{u-1})x_{u}\lambda884885for `1\le u\le s` and `\sigma_{u-1} \le t \le \sigma_{u}`.886887For any `x,y\in W/W_J` let888889.. MATH::890891d: x= w_{0} \stackrel{\beta_{1}}{\leftarrow}892w_{1} \stackrel{\beta_{2}}{\leftarrow} \cdots893\stackrel{\beta_{n}}{\leftarrow} w_{n}=y894895be a shortest directed path in the parabolic quantum Bruhat graph. Define896897.. MATH::898899\mathrm{wt}(d):=\sum_{\substack{1\le k\le n \\ \ell(w_{k-1})<\ell(w_k)}}900\beta_{k}^{\vee}901902It can be shown that `\mathrm{wt}(d)` depends only on `x,y`;903call its value `\mathrm{wt}(x,y)`. The energy function `D(\pi)` is defined by904905.. MATH::906907D(\pi)=-\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda,\mathrm{wt}(x_u,x_{u+1}) \rangle908909For more information, see [LNSSS2013]_.910911REFERENCES:912913.. [LNSSS2013] C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono,914A uniform model for Kirillov-Reshetikhin crystals. Extended abstract.915DMTCS proc, to appear ( {{{:arXiv:`1211.6019`}}} )916917.. NOTE::918919In the dual-of-untwisted case the parabolic quantum Bruhat graph that is used is obtained by920exchanging the roles of roots and coroots. Moreover, in the computation of the921pairing the short roots must be doubled (or tripled for type `G`). This factor922is determined by the translation factor of the corresponding root.923Type `BC` is viewed as untwisted type, whereas the dual of `BC` is viewed as twisted.924Except for the untwisted cases, these formulas are currently still conjectural.925926EXAMPLES::927928sage: R = RootSystem(['C',3,1])929sage: La = R.weight_space().basis()930sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3])931sage: b = LS.module_generators[0]932sage: c = b.f(1).f(3).f(2)933sage: c.energy_function()9340935sage: c=b.e(0)936sage: c.energy_function()9371938939sage: R = RootSystem(['A',2,1])940sage: La = R.weight_space().basis()941sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1])942sage: b = LS.module_generators[0]943sage: c = b.e(0)944sage: c.energy_function()9451946sage: [c.energy_function() for c in sorted(LS.list())]947[0, 1, 0, 0, 0, 1, 0, 1, 0]948949The next test checks that the energy function is constant on classically connected components::950951sage: R = RootSystem(['A',2,1])952sage: La = R.weight_space().basis()953sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])954sage: G = LS.digraph(index_set=[1,2])955sage: C = G.connected_components()956sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]957[True, True, True, True]958959sage: R = RootSystem(['D',4,2])960sage: La = R.weight_space().basis()961sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[2])962sage: J = R.cartan_type().classical().index_set()963sage: hw = [x for x in LS if x.is_highest_weight(J)]964sage: [(x.weight(), x.energy_function()) for x in hw]965[(-2*Lambda[0] + Lambda[2], 0), (-2*Lambda[0] + Lambda[1], 1), (0, 2)]966sage: G = LS.digraph(index_set=J)967sage: C = G.connected_components()968sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C]969[True, True, True]970971sage: R = RootSystem(CartanType(['G',2,1]).dual())972sage: La = R.weight_space().basis()973sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2])974sage: G = LS.digraph(index_set=[1,2])975sage: C = G.connected_components()976sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time977[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]978979sage: ct = CartanType(['BC',2,2]).dual()980sage: R = RootSystem(ct)981sage: La = R.weight_space().basis()982sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])983sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set())984sage: C = G.connected_components()985sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time986[True, True, True, True, True, True, True, True, True, True, True]987988sage: R = RootSystem(['BC',2,2])989sage: La = R.weight_space().basis()990sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2])991sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set())992sage: C = G.connected_components()993sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] # long time994[True, True, True, True, True, True, True, True, True, True, True, True, True, True, True,995True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True]996"""997weight = self.parent().weight998P = weight.parent()999c_weight = P.classical()(weight)1000ct = P.cartan_type()1001cartan = ct.classical()1002Qv = RootSystem(cartan).coroot_lattice()1003W = WeylGroup(cartan,prefix='s')1004J = tuple(weight.weyl_stabilizer())1005L = self.weyl_group_representation()1006if ct.is_untwisted_affine() or ct.type() == 'BC':1007untwisted = True1008G = W.quantum_bruhat_graph(J)1009else:1010untwisted = False1011cartan_dual = cartan.dual()1012Wd = WeylGroup(cartan_dual, prefix='s')1013G = Wd.quantum_bruhat_graph(J)1014Qd = RootSystem(cartan_dual).root_lattice()1015dualize = lambda x: Qv.from_vector(x.to_vector())1016L = [Wd.from_reduced_word(x.reduced_word()) for x in L]1017def stretch_short_root(a):1018# stretches roots by translation factor1019if ct.dual().type() == 'BC':1020return ct.c()[a.to_simple_root()]*a1021return ct.dual().c()[a.to_simple_root()]*a1022#if a.is_short_root():1023# if cartan_dual.type() == 'G':1024# return 3*a1025# else:1026# return 2*a1027#return a1028paths = [G.shortest_path(L[i+1],L[i]) for i in range(len(L)-1)]1029paths_labels = [[G.edge_label(p[i],p[i+1]) for i in range(len(p)-1) if p[i].length()+1 != p[i+1].length()] for p in paths]1030scalars = self.scalar_factors()1031if untwisted:1032s = sum((1-scalars[i])*c_weight.scalar( Qv.sum(root.associated_coroot()1033for root in paths_labels[i]) ) for i in range(len(paths_labels)))1034if ct.type() == 'BC':1035return 2*s1036else:1037return s1038else:1039s = sum((1-scalars[i])*c_weight.scalar( dualize (Qd.sum(stretch_short_root(root) for root in paths_labels[i])) ) for i in range(len(paths_labels)))1040if ct.dual().type() == 'BC':1041return s/21042else:1043return s104410451046