def _homogeneous_generators_noncommutative_variables_zero_Hecke(self, r): r""" Returns the ``r^{th}`` homogeneous generator, viewed as an element inside the affine zero Hecke algebra. This is the sum of all cyclicly decreasing elements of order ``r``. INPUT: - ``r`` -- A positive integer OUTPUT: - An element of the affine zero Hecke algebra. EXAMPLES:: sage: g = SymmetricFunctions(QQ).kBoundedSubspace(3,1).K_kschur() sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(2) T1*T0 + T2*T0 + T0*T3 + T3*T2 + T3*T1 + T2*T1 sage: g._homogeneous_generators_noncommutative_variables_zero_Hecke(0) 1 """ from sage.combinat.root_system.weyl_group import WeylGroup from sage.algebras.iwahori_hecke_algebra import IwahoriHeckeAlgebraT W = WeylGroup(['A',self.k,1]) H = IwahoriHeckeAlgebraT(W, 0, base_ring = self.base_ring()) Hgens = H.algebra_generators() S = [w.reduced_word() for w in W.pieri_factors() if w.length() == r] return sum( (prod((Hgens[i] for i in w), 1) for w in S), 0 )
def __classcall_private__(cls, w, n, x=None, k=None): r""" Classcall to mend the input. TESTS:: sage: A = crystals.AffineFactorization([[3,1],[1]], 4, k=3); A Crystal on affine factorizations of type A3 associated to s3*s2*s1 sage: AC = crystals.AffineFactorization([Core([4,1],4),Core([1],4)], 4, k=3) sage: AC is A True """ if k is not None: from sage.combinat.core import Core from sage.combinat.partition import Partition W = WeylGroup(['A', k, 1], prefix='s') if isinstance(w[0], Core): w = [w[0].to_bounded_partition(), w[1].to_bounded_partition()] else: w = [Partition(w[0]), Partition(w[1])] w0 = W.from_reduced_word(w[0].from_kbounded_to_reduced_word(k)) w1 = W.from_reduced_word(w[1].from_kbounded_to_reduced_word(k)) w = w0 * (w1.inverse()) return super(AffineFactorizationCrystal, cls).__classcall__(cls, w, n, x)
def __init__(self, root_system, hot_start=None): self.root_system = root_system if hot_start is None: self.W = WeylGroup(root_system) self.weyl_dic = self._compute_weyl_dictionary() else: self.W = hot_start["W"] self.weyl_dic = hot_start["weyl_dic"] self.domain = self.W.domain() self.reduced_words = sorted(self.weyl_dic.keys(), key=len) self.simple_roots = self.domain.simple_roots().values() self.rank = len(self.simple_roots) self.rho = self.domain.rho() self.pos_roots = self.domain.positive_roots() # Matrix of all simple roots, for faster matrix solving self.simple_root_matrix = matrix( [list(s.to_vector()) for s in self.simple_roots]).transpose() self.action_dic, self.rho_action_dic = self.get_action_dic()
def weyl_group_representation(self): r""" Transforms the weights in the LS path ``self`` to elements in the Weyl group. Each LS path can be written as the piecewise linear map: .. MATH:: \pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'} - \sigma_{u'-1}) \nu_{u'} + (t-\sigma_{u-1}) \nu_{u} for `0<\sigma_1<\sigma_2<\cdots<\sigma_s=1` and `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`. Each weight `\nu_u` is also associated to a Weyl group element. This method returns the list of Weyl group elements associated to the `\nu_u` for `1\le u\le s`. EXAMPLES:: sage: R = RootSystem(['C',3,1]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3]) sage: b = LS.module_generators[0] sage: c = b.f(1).f(3).f(2) sage: c.weyl_group_representation() [s2*s3*s1, s3*s1] """ cartan = self.parent().weight.parent().cartan_type().classical() I = cartan.index_set() W = WeylGroup(cartan,prefix='s') return [W.from_reduced_word(x.to_dominant_chamber(index_set=I, reduced_word=True)[1]) for x in self.value]
def weyl_group_representation(self): r""" Transforms the weights in the LS path ``self`` to elements in the Weyl group. Each LS path can be written as the piecewise linear map: .. MATH:: \pi(t) = \sum_{u'=1}^{u-1} (\sigma_{u'} - \sigma_{u'-1}) \nu_{u'} + (t-\sigma_{u-1}) \nu_{u} for `0<\sigma_1<\sigma_2<\cdots<\sigma_s=1` and `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`. Each weight `\nu_u` is also associated to a Weyl group element. This method returns the list of Weyl group elements associated to the `\nu_u` for `1\le u\le s`. EXAMPLES:: sage: R = RootSystem(['C',3,1]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3]) sage: b = LS.module_generators[0] sage: c = b.f(1).f(3).f(2) sage: c.weyl_group_representation() [s2*s3*s1, s3*s1] """ weight = self.parent().weight level = -weight.coefficient(0) La = weight.parent().fundamental_weights() cartan = weight.parent().cartan_type().classical() W = WeylGroup(cartan, prefix="s") scalars = [0] + self.scalar_factors() l = [ (self.value[i] / (scalars[i + 1] - scalars[i]) + level * La[0]).to_dominant_chamber(reduced_word=True) for i in range(len(scalars) - 1) ] return [W.from_reduced_word(a[1]) for a in l]
def __classcall_private__(cls, W, q1, q2=-1, base_ring=None, prefix="T"): """ TESTS:: sage: H = IwahoriHeckeAlgebraT("A2", 1) sage: H.coxeter_group() == WeylGroup("A2") True sage: H.cartan_type() == CartanType("A2") True sage: H.base_ring() == ZZ True sage: H._q2 == -1 True """ if W not in CoxeterGroups(): W = WeylGroup(W) if base_ring is None: base_ring = q1.parent() q2 = base_ring(q2) return super(IwahoriHeckeAlgebraT, cls).__classcall__(cls, W, q1=q1, q2=q2, base_ring=base_ring, prefix=prefix)
def weyl_group(self): """ Return the Weyl group of ``self``. EXAMPLES:: sage: L = LieAlgebra(QQ, cartan_type=['A', 2]) sage: L.weyl_group() Weyl Group of type ['A', 2] (as a matrix group acting on the ambient space) """ from sage.combinat.root_system.weyl_group import WeylGroup return WeylGroup(self._cartan_type)
def __classcall__(cls, W): """ EXAMPLES:: sage: from sage.monoids.j_trivial_monoids import * sage: HeckeMonoid(['A',3]).cardinality() 24 """ from sage.categories.coxeter_groups import CoxeterGroups if not W in CoxeterGroups(): from sage.combinat.root_system.weyl_group import WeylGroup W = WeylGroup(W) # CoxeterGroup(W) return super(PiMonoid, cls).__classcall__(cls, W)
def __init__(self, n, R = ZZ, prefix = 'a'): """ Initiates the affine nilTemperley Lieb algebra over the ring `R`. EXAMPLES:: sage: A = AffineNilTemperleyLiebTypeA(3, prefix="a"); A The affine nilTemperley Lieb algebra A3 over the ring Integer Ring sage: TestSuite(A).run() sage: A = AffineNilTemperleyLiebTypeA(3, QQ); A The affine nilTemperley Lieb algebra A3 over the ring Rational Field """ if not isinstance(R, Ring): raise TypeError("Argument R must be a ring.") self._cartan_type = CartanType(['A',n-1,1]) self._n = n W = WeylGroup(self._cartan_type) self._prefix = prefix self._index_set = W.index_set() self._base_ring = R category = AlgebrasWithBasis(R) CombinatorialFreeModule.__init__(self, R, W, category = category)
def __init__(self, n, R=ZZ, prefix='a'): """ Initiates the affine nilTemperley Lieb algebra over the ring `R`. EXAMPLES:: sage: A = AffineNilTemperleyLiebTypeA(3, prefix="a"); A The affine nilTemperley Lieb algebra A3 over the ring Integer Ring sage: TestSuite(A).run() sage: A = AffineNilTemperleyLiebTypeA(3, QQ); A The affine nilTemperley Lieb algebra A3 over the ring Rational Field """ if not isinstance(R, Ring): raise TypeError("Argument R must be a ring.") self._cartan_type = CartanType(['A', n - 1, 1]) self._n = n W = WeylGroup(self._cartan_type) self._prefix = prefix self._index_set = W.index_set() self._base_ring = R category = AlgebrasWithBasis(R) CombinatorialFreeModule.__init__(self, R, W, category=category)
def weyl_group(self, prefix=None): """ Returns the Weyl group associated to self. EXAMPLES:: sage: RootSystem(['F',4]).ambient_space().weyl_group() Weyl Group of type ['F', 4] (as a matrix group acting on the ambient space) sage: RootSystem(['F',4]).root_space().weyl_group() Weyl Group of type ['F', 4] (as a matrix group acting on the root space) """ from sage.combinat.root_system.weyl_group import WeylGroup return WeylGroup(self, prefix=prefix)
def __classcall_private__(cls, w, n, x = None, k = None): r""" Classcall to mend the input. TESTS:: sage: A = crystals.AffineFactorization([[3,1],[1]], 4, k=3); A Crystal on affine factorizations of type A3 associated to s3*s2*s1 sage: AC = crystals.AffineFactorization([Core([4,1],4),Core([1],4)], 4, k=3) sage: AC is A True """ if k is not None: from sage.combinat.core import Core from sage.combinat.partition import Partition W = WeylGroup(['A',k,1], prefix='s') if isinstance(w[0], Core): w = [w[0].to_bounded_partition(), w[1].to_bounded_partition()] else: w = [Partition(w[0]), Partition(w[1])] w0 = W.from_reduced_word(w[0].from_kbounded_to_reduced_word(k)) w1 = W.from_reduced_word(w[1].from_kbounded_to_reduced_word(k)) w = w0*(w1.inverse()) return super(AffineFactorizationCrystal, cls).__classcall__(cls, w, n, x)
def maximal_elements(self): r""" The current algorithm uses the fact that the maximal Pieri factors of affine type A,B,C, or D either contain a finite Weyl group element, or contain an affine Weyl group element whose reflection by `s_0` gets a finite Weyl group element, and that either of these finite group elements will serve as a maximal element for finite Pieri factors. A better algorithm is desirable. EXAMPLES:: sage: PF = WeylGroup(['A',5]).pieri_factors() sage: [v.reduced_word() for v in PF.maximal_elements()] [[5, 4, 3, 2, 1]] sage: WeylGroup(['B',4]).pieri_factors().maximal_elements() [ [-1 0 0 0] [ 0 1 0 0] [ 0 0 1 0] [ 0 0 0 1] ] """ ct = self.W.cartan_type() # The following line may need to be changed when generalizing to more than types A and B. if ct.type() != 'A' and ct.type() != 'B': raise NotImplementedError( "currently only implemented for finite types A and B") ct_aff = ct.dual().affine() max_elts_affine = WeylGroup(ct_aff).pieri_factors().maximal_elements() for w in max_elts_affine: if 0 not in w.reduced_word(): return [self.W.from_reduced_word(w.reduced_word())] for w in max_elts_affine: if 0 not in w.apply_simple_reflection(0).reduced_word(): return [ self.W.from_reduced_word( w.apply_simple_reflection(0).reduced_word()) ]
def stanley_symm_poly_weight(self,w): r""" Weight used in computing Stanley symmetric polynomials of type `B`. The weight for finite type B is the number of components of the support of an element minus the number of occurrences of `n` in a reduced word. EXAMPLES:: sage: W = WeylGroup(['B',5]) sage: PF = W.pieri_factors() sage: PF.stanley_symm_poly_weight(W.from_reduced_word([3,1,5])) 2 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([3,4,5])) 0 sage: PF.stanley_symm_poly_weight(W.from_reduced_word([1,2,3,4,5,4])) 0 """ r = w.reduced_word().count(self.W.n) return WeylGroup(self.W.cartan_type().dual().affine()).pieri_factors().stanley_symm_poly_weight(w) - r
def CoxeterGroup(data, implementation="reflection", base_ring=None, index_set=None): """ Return an implementation of the Coxeter group given by ``data``. INPUT: - ``data`` -- a Cartan type (or coercible into; see :class:`CartanType`) or a Coxeter matrix or graph - ``implementation`` -- (default: ``'reflection'``) can be one of the following: * ``'permutation'`` - as a permutation representation * ``'matrix'`` - as a Weyl group (as a matrix group acting on the root space); if this is not implemented, this uses the "reflection" implementation * ``'coxeter3'`` - using the coxeter3 package * ``'reflection'`` - as elements in the reflection representation; see :class:`~sage.groups.matrix_gps.coxeter_groups.CoxeterMatrixGroup` - ``base_ring`` -- (optional) the base ring for the ``'reflection'`` implementation - ``index_set`` -- (optional) the index set for the ``'reflection'`` implementation EXAMPLES: Now assume that ``data`` represents a Cartan type. If ``implementation`` is not specified, the reflection representation is returned:: sage: W = CoxeterGroup(["A",2]) sage: W Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [1 3] [3 1] sage: W = CoxeterGroup(["A",3,1]); W Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [1 3 2 3] [3 1 3 2] [2 3 1 3] [3 2 3 1] sage: W = CoxeterGroup(['H',3]); W Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [1 3 2] [3 1 5] [2 5 1] We now use the ``implementation`` option:: sage: W = CoxeterGroup(["A",2], implementation = "permutation") # optional - chevie sage: W # optional - chevie Permutation Group with generators [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6)] sage: W.category() # optional - chevie Join of Category of finite permutation groups and Category of finite coxeter groups sage: W = CoxeterGroup(["A",2], implementation="matrix") sage: W Weyl Group of type ['A', 2] (as a matrix group acting on the ambient space) sage: W = CoxeterGroup(["H",3], implementation="matrix") sage: W Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [1 3 2] [3 1 5] [2 5 1] sage: W = CoxeterGroup(["H",3], implementation="reflection") sage: W Finite Coxeter group over Universal Cyclotomic Field with Coxeter matrix: [1 3 2] [3 1 5] [2 5 1] sage: W = CoxeterGroup(["A",4,1], implementation="permutation") Traceback (most recent call last): ... NotImplementedError: Coxeter group of type ['A', 4, 1] as permutation group not implemented We use the different options for the "reflection" implementation:: sage: W = CoxeterGroup(["H",3], implementation="reflection", base_ring=RR) sage: W Finite Coxeter group over Real Field with 53 bits of precision with Coxeter matrix: [1 3 2] [3 1 5] [2 5 1] sage: W = CoxeterGroup([[1,10],[10,1]], implementation="reflection", index_set=['a','b'], base_ring=SR) sage: W Finite Coxeter group over Symbolic Ring with Coxeter matrix: [ 1 10] [10 1] TESTS:: sage: W = groups.misc.CoxeterGroup(["H",3]) """ if implementation not in [ "permutation", "matrix", "coxeter3", "reflection", None ]: raise ValueError("invalid type implementation") try: cartan_type = CartanType(data) except ( TypeError, ValueError ): # If it is not a Cartan type, try to see if we can represent it as a matrix group return CoxeterMatrixGroup(data, base_ring, index_set) if implementation is None: implementation = "matrix" if implementation == "reflection": return CoxeterMatrixGroup(cartan_type, base_ring, index_set) if implementation == "coxeter3": try: from sage.libs.coxeter3.coxeter_group import CoxeterGroup except ImportError: raise RuntimeError("coxeter3 must be installed") else: return CoxeterGroup(cartan_type) if implementation == "permutation" and is_chevie_available() and \ cartan_type.is_finite() and cartan_type.is_irreducible(): return CoxeterGroupAsPermutationGroup(cartan_type) elif implementation == "matrix": if cartan_type.is_crystallographic(): return WeylGroup(cartan_type) return CoxeterMatrixGroup(cartan_type, base_ring, index_set) raise NotImplementedError( "Coxeter group of type {} as {} group not implemented".format( cartan_type, implementation))
def energy_function(self): r""" Return the energy function of ``self``. The energy function `D(\pi)` of the level zero LS path `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)` requires a series of definitions; for simplicity the root system is assumed to be untwisted affine. The LS path `\pi` is a piecewise linear map from the unit interval `[0,1]` to the weight lattice. It is specified by "times" `0=\sigma_0<\sigma_1<\dotsm<\sigma_s=1` and "direction vectors" `x_u \lambda` where `x_u \in W/W_J` for `1\le u\le s`, and `W_J` is the stabilizer of `\lambda` in the finite Weyl group `W`. Precisely, .. MATH:: \pi(t)=\sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1})x_{u'}\lambda+(t-\sigma_{u-1})x_{u}\lambda for `1\le u\le s` and `\sigma_{u-1} \le t \le \sigma_{u}`. For any `x,y\in W/W_J` let .. MATH:: d: x= w_{0} \stackrel{\beta_{1}}{\leftarrow} w_{1} \stackrel{\beta_{2}}{\leftarrow} \cdots \stackrel{\beta_{n}}{\leftarrow} w_{n}=y be a shortest directed path in the parabolic quantum Bruhat graph. Define .. MATH:: \mathrm{wt}(d):=\sum_{\substack{1\le k\le n \\ \ell(w_{k-1})<\ell(w_k)}} \beta_{k}^{\vee} It can be shown that `\mathrm{wt}(d)` depends only on `x,y`; call its value `\mathrm{wt}(x,y)`. The energy function `D(\pi)` is defined by .. MATH:: D(\pi)=-\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda,\mathrm{wt}(x_u,x_{u+1}) \rangle For more information, see [LNSSS2013]_. REFERENCES: .. [LNSSS2013] C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono, A uniform model for Kirillov-Reshetikhin crystals. Extended abstract. DMTCS proc, to appear ( {{{:arXiv:`1211.6019`}}} ) .. NOTE:: In the dual-of-untwisted case the parabolic quantum Bruhat graph that is used is obtained by exchanging the roles of roots and coroots. Moreover, in the computation of the pairing the short roots must be doubled (or tripled for type `G`). This factor is determined by the translation factor of the corresponding root. Type `BC` is viewed as untwisted type, whereas the dual of `BC` is viewed as twisted. Except for the untwisted cases, these formulas are currently still conjectural. EXAMPLES:: sage: R = RootSystem(['C',3,1]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3]) sage: b = LS.module_generators[0] sage: c = b.f(1).f(3).f(2) sage: c.energy_function() 0 sage: c=b.e(0) sage: c.energy_function() 1 sage: R = RootSystem(['A',2,1]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]) sage: b = LS.module_generators[0] sage: c = b.e(0) sage: c.energy_function() 1 sage: [c.energy_function() for c in sorted(LS.list())] [0, 1, 0, 0, 0, 1, 0, 1, 0] The next test checks that the energy function is constant on classically connected components:: sage: R = RootSystem(['A',2,1]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2]) sage: G = LS.digraph(index_set=[1,2]) sage: C = G.connected_components() sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] [True, True, True, True] sage: R = RootSystem(['D',4,2]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[2]) sage: J = R.cartan_type().classical().index_set() sage: hw = [x for x in LS if x.is_highest_weight(J)] sage: [(x.weight(), x.energy_function()) for x in hw] [(-2*Lambda[0] + Lambda[2], 0), (-2*Lambda[0] + Lambda[1], 1), (0, 2)] sage: G = LS.digraph(index_set=J) sage: C = G.connected_components() sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] [True, True, True] sage: R = RootSystem(CartanType(['G',2,1]).dual()) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[2]) sage: G = LS.digraph(index_set=[1,2]) sage: C = G.connected_components() sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] sage: ct = CartanType(['BC',2,2]).dual() sage: R = RootSystem(ct) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2]) sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set()) sage: C = G.connected_components() sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] [True, True, True, True, True, True, True, True, True, True, True] sage: R = RootSystem(['BC',2,2]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2]) sage: G = LS.digraph(index_set=R.cartan_type().classical().index_set()) sage: C = G.connected_components() sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] [True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True, True] """ weight = self.parent().weight P = weight.parent() c_weight = P.classical()(weight) ct = P.cartan_type() cartan = ct.classical() Qv = RootSystem(cartan).coroot_lattice() W = WeylGroup(cartan,prefix='s') J = tuple(weight.weyl_stabilizer()) L = self.weyl_group_representation() if ct.is_untwisted_affine() or ct.type() == 'BC': untwisted = True G = W.quantum_bruhat_graph(J) else: untwisted = False cartan_dual = cartan.dual() Wd = WeylGroup(cartan_dual, prefix='s') G = Wd.quantum_bruhat_graph(J) Qd = RootSystem(cartan_dual).root_lattice() dualize = lambda x: Qv.from_vector(x.to_vector()) L = [Wd.from_reduced_word(x.reduced_word()) for x in L] def stretch_short_root(a): # stretches roots by translation factor if ct.dual().type() == 'BC': return ct.c()[a.to_simple_root()]*a return ct.dual().c()[a.to_simple_root()]*a #if a.is_short_root(): # if cartan_dual.type() == 'G': # return 3*a # else: # return 2*a #return a paths = [G.shortest_path(L[i+1],L[i]) for i in range(len(L)-1)] paths_labels = [[G.edge_label(p[i],p[i+1]) for i in range(len(p)-1) if p[i].length()+1 != p[i+1].length()] for p in paths] scalars = self.scalar_factors() if untwisted: s = sum((1-scalars[i])*c_weight.scalar( Qv.sum(root.associated_coroot() for root in paths_labels[i]) ) for i in range(len(paths_labels))) if ct.type() == 'BC': return 2*s else: return s else: s = sum((1-scalars[i])*c_weight.scalar( dualize (Qd.sum(stretch_short_root(root) for root in paths_labels[i])) ) for i in range(len(paths_labels))) if ct.dual().type() == 'BC': return s/2 else: return s
def CoxeterGroup(cartan_type, implementation=None): """ INPUT: - ``cartan_type`` -- a cartan type (or coercible into; see :class:`CartanType`) - ``implementation`` -- "permutation", "matrix", "coxeter3", or None (default: None) Returns an implementation of the Coxeter group of type ``cartan_type``. EXAMPLES: If ``implementation`` is not specified, a permutation representation is returned whenever possible (finite irreducible Cartan type, with the GAP3 Chevie package available):: sage: W = CoxeterGroup(["A",2]) sage: W # optional - chevie Permutation Group with generators [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6)] Otherwise, a Weyl group is returned:: sage: W = CoxeterGroup(["A",3,1]) sage: W Weyl Group of type ['A', 3, 1] (as a matrix group acting on the root space) We now use the ``implementation`` option:: sage: W = CoxeterGroup(["A",2], implementation = "permutation") # optional - chevie sage: W # optional - chevie Permutation Group with generators [(1,3)(2,5)(4,6), (1,4)(2,3)(5,6)] sage: W.category() # optional - chevie Join of Category of finite permutation groups and Category of finite coxeter groups sage: W = CoxeterGroup(["A",2], implementation = "matrix") sage: W Weyl Group of type ['A', 2] (as a matrix group acting on the ambient space) sage: W = CoxeterGroup(["H",3], implementation = "matrix") Traceback (most recent call last): ... NotImplementedError: Coxeter group of type ['H', 3] as matrix group not implemented sage: W = CoxeterGroup(["A",4,1], implementation = "permutation") Traceback (most recent call last): ... NotImplementedError: Coxeter group of type ['A', 4, 1] as permutation group not implemented """ assert implementation in ["permutation", "matrix", "coxeter3", None] cartan_type = CartanType(cartan_type) if implementation is None: if cartan_type.is_finite() and cartan_type.is_irreducible( ) and is_chevie_available(): implementation = "permutation" else: implementation = "matrix" if implementation == "coxeter3": try: from sage.libs.coxeter3.coxeter_group import CoxeterGroup except ImportError: raise RuntimeError, "coxeter3 must be installed" else: return CoxeterGroup(cartan_type) if implementation == "permutation" and is_chevie_available() and \ cartan_type.is_finite() and cartan_type.is_irreducible(): return CoxeterGroupAsPermutationGroup(cartan_type) elif implementation == "matrix" and cartan_type.is_crystalographic(): return WeylGroup(cartan_type) else: raise NotImplementedError, "Coxeter group of type %s as %s group not implemented " % ( cartan_type, implementation)
def string_parameters(self, word=None): r""" Return the string parameters of ``self`` corresponding to the reduced word ``word``. Given a reduced expression `w = s_{i_1} \cdots s_{i_k}`, the string parameters of `b \in B` corresponding to `w` are `(a_1, \ldots, a_k)` such that .. MATH:: \begin{aligned} e_{i_m}^{a_m} \cdots e_{i_1}^{a_1} b & \neq 0 \\ e_{i_m}^{a_m+1} \cdots e_{i_1}^{a_1} b & = 0 \end{aligned} for all `1 \leq m \leq k`. For connected components isomorphic to `B(\lambda)` or `B(\infty)`, if `w = w_0` is the longest element of the Weyl group, then the path determined by the string parametrization terminates at the highest weight vector. INPUT: - ``word`` -- a word in the alphabet of the index set; if not specified and we are in finite type, then this will be some reduced expression for the long element determined by the Weyl group EXAMPLES:: sage: B = crystals.infinity.NakajimaMonomials(['A',3]) sage: mg = B.highest_weight_vector() sage: w0 = [1,2,1,3,2,1] sage: mg.string_parameters(w0) [0, 0, 0, 0, 0, 0] sage: mg.f_string([1]).string_parameters(w0) [1, 0, 0, 0, 0, 0] sage: mg.f_string([1,1,1]).string_parameters(w0) [3, 0, 0, 0, 0, 0] sage: mg.f_string([1,1,1,2,2]).string_parameters(w0) [1, 2, 2, 0, 0, 0] sage: mg.f_string([1,1,1,2,2]) == mg.f_string([1,1,2,2,1]) True sage: x = mg.f_string([1,1,1,2,2,1,3,3,2,1,1,1]) sage: x.string_parameters(w0) [4, 1, 1, 2, 2, 2] sage: x.string_parameters([3,2,1,3,2,3]) [2, 3, 7, 0, 0, 0] sage: x == mg.f_string([1]*7 + [2]*3 + [3]*2) True :: sage: B = crystals.infinity.Tableaux("A5") sage: b = B(rows=[[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,3,6,6,6,6,6,6], ....: [2,2,2,2,2,2,2,2,2,4,5,5,5,6], ....: [3,3,3,3,3,3,3,5], ....: [4,4,4,6,6,6], ....: [5,6]]) sage: b.string_parameters([1,2,1,3,2,1,4,3,2,1,5,4,3,2,1]) [0, 1, 1, 1, 1, 0, 4, 4, 3, 0, 11, 10, 7, 7, 6] sage: B = crystals.infinity.Tableaux("G2") sage: b = B(rows=[[1,1,1,1,1,3,3,0,-3,-3,-2,-2,-1,-1,-1,-1],[2,3,3,3]]) sage: b.string_parameters([2,1,2,1,2,1]) [5, 13, 11, 15, 4, 4] sage: b.string_parameters([1,2,1,2,1,2]) [7, 12, 15, 8, 10, 0] :: sage: C = crystals.Tableaux(['C',2], shape=[2,1]) sage: mg = C.highest_weight_vector() sage: lw = C.lowest_weight_vectors()[0] sage: lw.string_parameters([1,2,1,2]) [1, 2, 3, 1] sage: lw.string_parameters([2,1,2,1]) [1, 3, 2, 1] sage: lw.e_string([2,1,1,1,2,2,1]) == mg True sage: lw.e_string([1,2,2,1,1,1,2]) == mg True TESTS:: sage: B = crystals.infinity.NakajimaMonomials(['B',3]) sage: mg = B.highest_weight_vector() sage: mg.string_parameters() [0, 0, 0, 0, 0, 0, 0, 0, 0] sage: w0 = WeylGroup(['B',3]).long_element().reduced_word() sage: def f_word(params): ....: return reversed([index for i, index in enumerate(w0) ....: for _ in range(params[i])]) sage: all(mg.f_string( f_word(x.value.string_parameters(w0)) ) == x.value ....: for x in B.subcrystal(max_depth=4)) True sage: B = crystals.infinity.NakajimaMonomials(['A',2,1]) sage: mg = B.highest_weight_vector() sage: mg.string_parameters() Traceback (most recent call last): ... ValueError: the word must be specified because the Weyl group is not finite """ if word is None: if not self.cartan_type().is_finite(): raise ValueError("the word must be specified because" " the Weyl group is not finite") from sage.combinat.root_system.weyl_group import WeylGroup word = WeylGroup( self.cartan_type()).long_element().reduced_word() x = self params = [] for i in word: count = 0 y = x.e(i) while y is not None: x = y y = x.e(i) count += 1 params.append(count) return params
class WeightSet: """Class to do simple computations with the weights of a weight module. Parameters ---------- root_system : str String representing the root system (e.g. 'A2') Attributes ---------- root_system : str String representing the root system (e.g. 'A2') W : WeylGroup Object encoding the Weyl group. weyl_dic : dict(str, WeylGroup.element_class) Dictionary mapping strings representing a Weyl group element as reduced word in simple reflections, to the Weyl group element. reduced_words : list[str] Sorted list of all strings representing Weyl group elements as reduced word in simple reflections. simple_roots : list[RootSpace.element_class] List of the simple roots rank : int Rank of root system rho : RootSpace.element_class Half the sum of all positive roots pos_roots : List[RootSpace.element_class] List of all the positive roots action_dic : Dict[str, np.array(np.int32, np.int32)] dictionary mapping each string representing an element of the Weyl group to a matrix expressing the action on the simple roots. rho_action_dic : Dict[str, np.array(np.int32)] dictionary mapping each string representing an element of the Weyl group to a vector representing the image of the dot action on rho. """ @classmethod def from_bgg(cls, BGG): """Initialize from an instance of BGGComplex. Some data can be reused, and this gives roughly 3x faster initialization. Parameters ---------- BGG : BGGComplex The BGGComplex to initialize from. """ hot_start = {"W": BGG.W, "weyl_dic": BGG.reduced_word_dic} return cls(BGG.root_system, hot_start=hot_start) def __init__(self, root_system, hot_start=None): self.root_system = root_system if hot_start is None: self.W = WeylGroup(root_system) self.weyl_dic = self._compute_weyl_dictionary() else: self.W = hot_start["W"] self.weyl_dic = hot_start["weyl_dic"] self.domain = self.W.domain() self.reduced_words = sorted(self.weyl_dic.keys(), key=len) self.simple_roots = self.domain.simple_roots().values() self.rank = len(self.simple_roots) self.rho = self.domain.rho() self.pos_roots = self.domain.positive_roots() # Matrix of all simple roots, for faster matrix solving self.simple_root_matrix = matrix( [list(s.to_vector()) for s in self.simple_roots]).transpose() self.action_dic, self.rho_action_dic = self.get_action_dic() def _compute_weyl_dictionary(self): """Construct a dictionary enumerating all of the elements of the Weyl group. The keys are reduced words of the elements """ reduced_word_dic = { "".join([str(s) for s in g.reduced_word()]): g for g in self.W } return reduced_word_dic def weight_to_tuple(self, weight): """Convert element of weight lattice to a sum of simple roots. Parameters ---------- weight : RootSpace.element_class Returns ------- tuple[int] tuple representing root as linear combination of simple roots """ b = weight.to_vector() b = matrix(b).transpose() return tuple(self.simple_root_matrix.solve_right(b).transpose().list()) def tuple_to_weight(self, t): """Inverse of `weight_to_tuple`. Parameters ---------- t : tuple[int] Returns ------- RootSpace.element_class """ return sum(int(a) * b for a, b in zip(t, self.simple_roots)) def get_action_dic(self): """Compute weyl group action as well as action on rho. Returns ------- Dict[str, np.array(np.int32, np.int32)] : dictionary mapping each string representing an element of the Weyl group to a matrix expressing the action on the simple roots. Dict[str, np.array(np.int32)] : dictionary mapping each string representing an element of the Weyl group to a vector representing the image of the dot action on rho. """ action_dic = dict() rho_action_dic = dict() for s, w in self.weyl_dic.items(): # s is a string, w is a matrix # Compute action of w on every simple root, decompose result in simple roots, encode result as matrix. action_mat = [] for mu in self.simple_roots: action_mat.append(self.weight_to_tuple(w.action(mu))) action_dic[s] = np.array(action_mat, dtype=np.int32) # Encode the dot action of w on rho. rho_action_dic[s] = np.array( self.weight_to_tuple(w.action(self.rho) - self.rho), dtype=np.int32) return action_dic, rho_action_dic def dot_action(self, w, mu): r"""Compute the dot action of w on mu. The dot action :math:`w\cdot\mu = w(\mu+\rho)-\rho`, with :math:`\rho` half the sum of all the positive roots. Parameters ---------- w : str string representing the weyl group element mu : iterable(int) the weight Returns ------- np.array[np.int32] vector encoding the new weight """ # The dot action w.mu = w(mu+rho)-rho = w*mu + (w*rho-rho). # The former term is given by action_dic, the latter by rho_action_dic return (np.matmul(self.action_dic[w].T, np.array(mu, dtype=np.int32)) + self.rho_action_dic[w]) def dot_orbit(self, mu): """Compute the orbit of the Weyl group action on a weight. Parameters ---------- mu : iterable(int) A weight Returns ------- dict(str, np.array[np.int32]) Dictionary mapping Weyl group elements to weights encoded as numpy vectors. """ return {w: self.dot_action(w, mu) for w in self.reduced_words} def is_dot_regular(self, mu): """Check if mu has a non-trivial stabilizer under the dot action. Parameters ---------- mu : iterable(int) The weight Returns ------- bool `True` if the weight is dot-regular """ for s in self.reduced_words[1:]: if np.all(self.dot_action(s, mu) == mu): return False # no stabilizer found return True def compute_weights(self, weights): """Find dot-regular weights and associated dominant weights of a set of weights. Parameters ---------- weights : iterable(iterable(int)) Iterable of weights returns ------- list(tuple[tuple(int), tuple(int), int]) list of triples consisting of dot-regular weight, associated dominant, and the length of the Weyl group element making the weight dominant under the dot action. """ regular_weights = [] for mu in weights: if self.is_dot_regular(mu): mu_prime, w = self.make_dominant(mu) regular_weights.append((mu, tuple(mu_prime), len(w))) return regular_weights def is_dominant(self, mu): """Use sagemath built-in function to check if weight is dominant. Parameters ---------- mu : iterable(int) the weight Returns ------- bool `True` if weight is dominant """ return self.tuple_to_weight(mu).is_dominant() def make_dominant(self, mu): """For a dot-regular weight mu, w such that if w.mu is dominant. Such a w exists iff mu is dot-regular, in which case it is also unique. Parameters ---------- mu : iterable(int) the dot-regular weight Returns ------- tuple(int) The dominant weight w.mu str the string representing the Weyl group element w. """ for w in self.reduced_words: new_mu = self.dot_action(w, mu) if self.is_dominant(new_mu): return new_mu, w else: raise ValueError( "Could not make weight %s dominant, probably it is not dot-regular." ) def get_vertex_weights(self, mu): """For a given dot-regular mu, return its orbit under the dot-action. Parameters ---------- mu : iterable(int) Returns ------- list[tuple[int]] list of weights """ vertex_weights = dict() for w in self.reduced_words: vertex_weights[w] = tuple(self.dot_action(w, mu)) return vertex_weights def highest_weight_rep_dim(self, mu): """Give dimension of highest weight representation of integral dominant weight. Parameters ---------- mu : tuple(int) A integral dominant weight Returns ------- int dimension of highest weight representation. """ mu_weight = self.tuple_to_weight(mu) numerator = 1 denominator = 1 for alpha in self.pos_roots: numerator *= (mu_weight + self.rho).dot_product(alpha) denominator *= self.rho.dot_product(alpha) return numerator // denominator
class BGGComplex: """A class encoding all the things we need of the BGG complex. Parameters ---------- root_system : str String encoding the Dynkin diagram of the root system (e.g. `'A3'`) pickle_directory : str (optional) Directory where to store `.pkl` files to save computations regarding maps in the BGG complex. If `None`, maps are not saved. (default: `None`) Attributes ------- root_system : str String encoding Dynkin diagram. W : WeylGroup Object encoding the Weyl group. LA : LieAlgebraChevalleyBasis Object encoding the Lie algebra in the Chevalley basis over Q PBW : PoincareBirkhoffWittBasis Poincaré-Birkhoff-Witt basis of the universal enveloping algebra. The maps in the BGG complex are elements of this basis. This package uses a slight modification of the PBW class as implemented in Sagemath proper. lattice : RootSpace The space of roots S : FiniteFamily Set of simple reflections in the Weyl group T : FIniteFamily Set of reflections in the Weyl group cycles : List List of 4-tuples encoding the length-4 cycles in the Bruhat graph simple_roots : List (Ordered) list of the simple roots as elements of `lattice` rank : int Rank of the root system neg_roots : list List of the negative roots in the root system, encoded as `numpy.ndarray`. alpha_to_index : dict Dictionary mapping negative roots (as elements of `lattice`) to an ordered index encoding the negative root as basis element of the lie algebra $n$ spanned by the negative roots. zero_root : element of `lattice` The zero root rho : element of `lattice` Half the sum of all positive roots """ def __init__(self, root_system, pickle_directory=None): self.root_system = root_system self.W = WeylGroup(root_system) self.domain = self.W.domain() self.LA = LieAlgebra(QQ, cartan_type=root_system) self.PBW = PoincareBirkhoffWittBasis(self.LA, None, "PBW", cache_degree=5) # self.PBW = self.LA.pbw_basis() self.PBW_alg_gens = self.PBW.algebra_generators() self.lattice = self.domain.root_system.root_lattice() self.S = self.W.simple_reflections() self.T = self.W.reflections() self.signs = None self.cycles = None self._compute_weyl_dictionary() self._construct_BGG_graph() self.find_cycles() self.simple_roots = self.domain.simple_roots().values() self.rank = len(self.simple_roots) # for PBW computations we need to put the right order on the negative roots. # This order coincides with that of the sagemath source code. lie_alg_order = {k: i for i, k in enumerate(self.LA.basis().keys())} ordered_roots = sorted( [ self._weight_to_alpha_sum(r) for r in self.domain.negative_roots() ], key=lambda rr: lie_alg_order[rr], ) self.neg_roots = [-self._alpha_sum_to_array(r) for r in ordered_roots] self.alpha_to_index = { self._weight_to_alpha_sum(-self._tuple_to_weight(r)): i for i, r in enumerate(self.neg_roots) } self.zero_root = self.domain.zero() self.pickle_directory = pickle_directory if pickle_directory is None: self.pickle_maps = False else: self.pickle_maps = True if self.pickle_maps: self._maps = self._read_maps() else: self._maps = dict() self.rho = self.domain.rho() self._action_dic = dict() for s, w in self.reduced_word_dic.items(): self._action_dic[s] = { i: self._weight_to_alpha_sum(w.action(mu)) for i, mu in dict(self.domain.simple_roots()).items() } self._rho_action_dic = dict() for s, w in self.reduced_word_dic.items(): self._rho_action_dic[s] = self._weight_to_alpha_sum( w.action(self.rho) - self.rho) def _compute_weyl_dictionary(self): """Construct a dictionary enumerating all of the elements of the Weyl group.""" self.reduced_word_dic = { "".join([str(s) for s in g.reduced_word()]): g for g in self.W } self.reduced_word_dic_reversed = dict( [[v, k] for k, v in self.reduced_word_dic.items()]) self.reduced_words = sorted( self.reduced_word_dic.keys(), key=len) # sort the reduced words by their length long_element = self.W.long_element() self.dual_words = { s: self.reduced_word_dic_reversed[long_element * w] for s, w in self.reduced_word_dic.items() } # the dual word is the word times the longest element self.column = defaultdict(list) max_len = 0 for red_word in self.reduced_words: length = len(red_word) max_len = max(max_len, length) self.column[length] += [red_word] self.max_word_length = max_len def _construct_BGG_graph(self): """Find all the arrows in the BGG Graph. There is an arrow w->w' if len(w')=len(w)+1 and w' = t.w for some t in T. """ self.arrows = [] for w in self.reduced_words: for t in self.T: product_word = self.reduced_word_dic_reversed[ t * self.reduced_word_dic[w]] if len(product_word) == len(w) + 1: self.arrows += [(w, product_word)] self.arrows = sorted( self.arrows, key=lambda t: len(t[0])) # sort the arrows by the word length self.graph = DiGraph(self.arrows) def plot_graph(self): """Create a pretty plot of the BGG graph, with vertices colored by word lenght.""" BGGVertices = sorted(self.reduced_words, key=len) BGGPartition = [list(v) for k, v in groupby(BGGVertices, len)] BGGGraphPlot = self.graph.to_undirected().graphplot( partition=BGGPartition, vertex_labels=None, vertex_size=30) display(BGGGraphPlot.plot()) def find_cycles(self): """Find all the admitted cycles in the BGG graph. An admitted cycle consists of two paths a->b->c and a->b'->c, where the word length increases by 1 each step. The cycles are returned as tuples (a,b,c,b',a). """ # only compute cycles if we haven't yet done so already if self.cycles is None: # for faster searching, make a dictionary of pairs (v,[u_1,...,u_k]) where v is a vertex and u_i # are vertices such that there is an arrow v->u_i first = lambda x: x[0] second = lambda x: x[1] outgoing = { k: list(map(second, v)) for k, v in groupby(sorted(self.arrows, key=first), first) } # outgoing[max(self.reduced_words,key=lambda x: len(x))]=[] outgoing[self.reduced_word_dic_reversed[ self.W.long_element()]] = [] # make a dictionary of pairs (v,[u_1,...,u_k]) where v is a vertex and u_i are vertices such that # there is an arrow u_i->v incoming = { k: list(map(first, v)) for k, v in groupby(sorted(self.arrows, key=second), second) } incoming[""] = [] # enumerate all paths of length 2, a->b->c, where length goes +1,+1 self.cycles = chain.from_iterable( [[a + (v, ) for v in outgoing[a[-1]]] for a in self.arrows]) # enumerate all paths of length 3, a->b->c->b' such that b' != b, # b<b' in lexicographic order (to avoid duplicates) and length goes +1,+1,-1 self.cycles = chain.from_iterable( [[a + (v, ) for v in incoming[a[-1]] if v > a[1]] for a in self.cycles]) # enumerate all cycles of length 4, a->b->c->b'->a such that b'!=b and length goes +1,+1,-1,-1 self.cycles = [ a + (a[0], ) for a in self.cycles if a[0] in incoming[a[-1]] ] return self.cycles def compute_signs(self, force_recompute=False): """Compute signs making making product of signs around all squares equal to -1. Returns ------- dict[tuple(str,str), int] Dictionary mapping edges in the Bruhat graph to {+1,-1} """ if not force_recompute: if self.signs is not None: return self.signs self.signs = compute_signs(self) return self.signs def compute_maps(self, root, column=None, check=False, pbar=None): """Compute the (unsigned) maps of the BGG complex for a given weight. Parameters ---------- root : tuple(int) root for which to compute the maps. column : int or `None` (default: `None`) Try to only compute the maps up to this particular column if not `None`. This is faster in particular for small or large values of `column`. check : bool (default: False) After computing all the maps, perform a check whether they are correct for debugging purposes. pbar : tqdm (default: None) tqdm progress bar to give status updates about the progress. If `None` this feature is disabled. Returns ------- dict mapping edges (in form ('w1', 'w2')) to elements of `self.PBW`. """ # Convert to tuple to make sure root is hasheable root = tuple(root) # If the maps are not in the cache, compute them and cache the result if root in self._maps: cached_result = self._maps[root] else: cached_result = None MapSolver = BGGMapSolver(self, root, pbar=pbar, cached_results=cached_result) self._maps[root] = MapSolver.solve(column=column) if check: maps_OK = MapSolver.check_maps() if not maps_OK: raise ValueError( "For root %s the map solver produced something wrong" % root) if self.pickle_maps: self._store_maps() return self._maps[root] def _read_maps(self): target_path = os.path.join(self.pickle_directory, self.root_system + r"_maps.pkl") try: with open(target_path, "rb") as file: maps = pickle.load(file) return maps except IOError: return dict() def _store_maps(self): target_path = os.path.join(self.pickle_directory, self.root_system + r"_maps.pkl") try: with open(target_path, "wb") as file: pickle.dump(self._maps, file, pickle.HIGHEST_PROTOCOL) except IOError: pass def _weight_to_tuple(self, weight): """Convert rootspace element to tuple.""" b = weight.to_vector() b = matrix(b).transpose() A = [list(a.to_vector()) for a in self.simple_roots] A = matrix(A).transpose() return tuple(A.solve_right(b).transpose().list()) def _weight_to_alpha_sum(self, weight): """Express a weight in the lattice as a linear combination of alpha[i]'s. These objects form the keys for elements of the Lie algebra, and for factors in the universal enveloping algebra. """ if type(weight) is not tuple and type(weight) is not list: tup = self._weight_to_tuple(weight) else: tup = tuple(weight) alpha = self.lattice.alpha() zero = self.lattice.zero() return sum((int(c) * alpha[i + 1] for i, c in enumerate(tup)), zero) def _alpha_sum_to_array(self, weight): output = array(vector(ZZ, self.rank)) for i, c in weight.monomial_coefficients().items(): output[i - 1] = c return output def _tuple_to_weight(self, t): """Turn a tuple encoding a linear combination of simple roots back into a weight.""" return sum(int(a) * b for a, b in zip(t, self.simple_roots)) def _is_dot_regular(self, mu): """Check if a weight is dot-regular by checking that it has trivial stabilizer under dot action. Parameters ---------- mu : element of `self.lattice` Weight encoded as linear combination of `alpha[i]`, use `self._weight_to_alpha_sum()` to convert to this format """ for w in self.reduced_words[1:]: if (self._dot_action(w, mu) == mu ): # Stabilizer is non-empty, mu is not dot regular return False # No nontrivial stabilizer found return True def _make_dominant(self, mu): """Given a dot-regular weight, find the associated dominant weight. Parameters ---------- mu : element of `self.lattice` Weight encoded as linear combination of `alpha[i]`, use `self._weight_to_alpha_sum()` to convert to this format Returns ------- dominant weight encoded in same format as input """ for w in self.reduced_words: new_mu = self._dot_action(w, mu) if new_mu.is_dominant(): return new_mu, w # Nothing found raise Exception( "The weight %s can not be made dominant. Probably it is not dot-regular." % mu) # def compute_weights(self, weight_module): # all_weights = weight_module.weight_dic.keys() # regular_weights = [] # for mu in all_weights: # if self._is_dot_regular(mu): # mu_prime, w = self._make_dominant(mu) # # mu_prime = self._weight_to_alpha_sum(mu_prime) # # w = self.reduced_word_dic_reversed[w] # regular_weights.append((mu, mu_prime, len(w))) # return all_weights, regular_weights def _dot_action(self, w, mu): """Dot action of Weyl group on weights. Parameters ---------- w : element of `self.W` mu : RootSpace.element_class Weight encoded as linear combination of `alpha[i]`, use `self._weight_to_alpha_sum()` to convert to this format """ action = self._action_dic[w] mu_action = sum( [ action[i] * int(c) for i, c in mu.monomial_coefficients().items() ], self.lattice.zero(), ) return mu_action + self._rho_action_dic[w] def display_pbw(self, f, notebook=True): """Typesets an element of PBW of the universal enveloping algebra with LaTeX. Parameters ---------- f : PoincareBirkhoffWittBasis.element_class The element to display notebook : bool (optional, default: `True`) Uses IPython display with math if `True`, otherwise just returns the LaTeX code as string. """ map_string = [] first_term = True for monomial, coefficient in f.monomial_coefficients().items(): alphas = monomial.dict().items() if first_term: if coefficient < 0: sign_string = "-" else: sign_string = "" first_term = False else: if coefficient < 0: sign_string = "-" else: sign_string = "+" if abs(coefficient) == 1: coeff_string = "" else: coeff_string = str(abs(coefficient)) term_strings = [sign_string + coeff_string] for alpha, power in alphas: if power > 1: power_string = r"^{" + str(power) + r"}" else: power_string = "" alpha_string = "".join( str(k) * int(-v) for k, v in alpha.monomial_coefficients().items()) term_strings.append(r"f_{" + alpha_string + r"}" + power_string) map_string.append(r"\,".join(term_strings)) if notebook: display(Math(" ".join(map_string))) else: return " ".join(map_string) def _display_map(self, arrow, f): """Display a single arrow plus map of the BGG complex.""" f_string = self.display_pbw(f, notebook=False) display(Math(r"\to".join(arrow) + r",\,\," + f_string)) def display_maps(self, mu): """Display all the maps of the BGG complex for a given mu, in appropriate order. Parameters ---------- mu : tuple tuple encoding the weight as linear combination of simple roots """ maps = self.compute_maps(mu) maps = sorted(maps.items(), key=lambda s: (len(s[0][0]), s[0])) for arrow, f in maps: self._display_map(arrow, f)
def __init__(self, root_system, pickle_directory=None): self.root_system = root_system self.W = WeylGroup(root_system) self.domain = self.W.domain() self.LA = LieAlgebra(QQ, cartan_type=root_system) self.PBW = PoincareBirkhoffWittBasis(self.LA, None, "PBW", cache_degree=5) # self.PBW = self.LA.pbw_basis() self.PBW_alg_gens = self.PBW.algebra_generators() self.lattice = self.domain.root_system.root_lattice() self.S = self.W.simple_reflections() self.T = self.W.reflections() self.signs = None self.cycles = None self._compute_weyl_dictionary() self._construct_BGG_graph() self.find_cycles() self.simple_roots = self.domain.simple_roots().values() self.rank = len(self.simple_roots) # for PBW computations we need to put the right order on the negative roots. # This order coincides with that of the sagemath source code. lie_alg_order = {k: i for i, k in enumerate(self.LA.basis().keys())} ordered_roots = sorted( [ self._weight_to_alpha_sum(r) for r in self.domain.negative_roots() ], key=lambda rr: lie_alg_order[rr], ) self.neg_roots = [-self._alpha_sum_to_array(r) for r in ordered_roots] self.alpha_to_index = { self._weight_to_alpha_sum(-self._tuple_to_weight(r)): i for i, r in enumerate(self.neg_roots) } self.zero_root = self.domain.zero() self.pickle_directory = pickle_directory if pickle_directory is None: self.pickle_maps = False else: self.pickle_maps = True if self.pickle_maps: self._maps = self._read_maps() else: self._maps = dict() self.rho = self.domain.rho() self._action_dic = dict() for s, w in self.reduced_word_dic.items(): self._action_dic[s] = { i: self._weight_to_alpha_sum(w.action(mu)) for i, mu in dict(self.domain.simple_roots()).items() } self._rho_action_dic = dict() for s, w in self.reduced_word_dic.items(): self._rho_action_dic[s] = self._weight_to_alpha_sum( w.action(self.rho) - self.rho)
def energy_function(self): r""" Returns the energy function of ``self`` for untwisted types. For level zero LS paths `\pi \in \mathbb{B}_\mathrm{cl}(\lambda)`, the energy function is defined as follows: .. MATH:: D(\pi)=-\sum_{u=1}^{s-1} (1-\sigma_{u}) \langle \lambda,\mathrm{wt}(b_{u}) \rangle To make sense of this equation, we first need some definitions. Let us write the LS path (or ``self``) as a piecewise linear map .. MATH:: \pi(t)=\sum_{u'=1}^{u-1} (\sigma_{u'}-\sigma_{u'-1})x_{u'}+(t-\sigma_{u-1})x_{u} for `\sigma_{u-1} \le t \le \sigma_{u}` and `1 \le u \le s`. Here `b_{u}` is a shortest directed path from `x_{u+1}` to `x_{u}` in the parabolic quantum Bruhat graph. For any `x,y\in W/W_J`, where `W_J` is a parabolic subgroup of `W` (the stabilizer of the weight `\lambda`), let .. MATH:: d: x= w_{0} \stackrel{\beta_{1}}{\leftarrow} w_{1} \stackrel{\beta_{2}}{\leftarrow} \cdots \stackrel{\beta_{n}}{\leftarrow} w_{n}=y be a shortest path in the parabolic quantum Bruhat graph. The weight is defined as .. MATH:: \mathrm{wt}(d):=\sum_{ \begin{subarray}{c} 1 \le k \le n \text{ such that } \\ w_{k-1} \stackrel{\beta_{k}}{\leftarrow} w_{k} \text{ is a down arrow} \end{subarray} } \beta_{k}^{\vee}. For more information, see [LNSSS2013]_. REFERENCES: .. [LNSSS2013] C. Lenart, S. Naito, D. Sagaki, A. Schilling, M. Shimozono, A uniform model for Kirillov-Reshetikhin crystals. Extended abstract. DMTCS proc, to appear ( {{{:arXiv:`1211.6019`}}} ) .. WARNING:: The energy function for LS paths is currently only implemented for untwisted types! EXAMPLES:: sage: R = RootSystem(['C',3,1]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(La[1]+La[3]) sage: b = LS.module_generators[0] sage: c = b.f(1).f(3).f(2) sage: c.energy_function() 0 sage: c=b.e(0) sage: c.energy_function() 1 sage: R = RootSystem(['A',2,1]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]) sage: b = LS.module_generators[0] sage: c = b.e(0) sage: c.energy_function() 1 sage: [c.energy_function() for c in sorted(LS.list())] [0, 1, 0, 0, 0, 1, 0, 1, 0] The next test checks that the energy function is constant on classically connected components:: sage: R = RootSystem(['A',2,1]) sage: La = R.weight_space().basis() sage: LS = CrystalOfProjectedLevelZeroLSPaths(2*La[1]+La[2]) sage: G = LS.digraph(index_set=[1,2]) sage: C = G.connected_components() sage: [all(c[0].energy_function()==a.energy_function() for a in c) for c in C] [True, True, True, True] """ weight = self.parent().weight c_weight = weight.parent().classical()(weight) cartan = weight.parent().cartan_type().classical() W = WeylGroup(cartan, prefix="s") R = RootSystem(cartan).coroot_lattice() G = W.quantum_bruhat_graph(tuple(weight.weyl_stabilizer())) L = self.weyl_group_representation() paths = [G.shortest_path(L[i + 1], L[i]) for i in range(len(L) - 1)] paths_labels = [ [G.edge_label(p[i], p[i + 1]) for i in range(len(p) - 1) if p[i].length() + 1 != p[i + 1].length()] for p in paths ] scalars = self.scalar_factors() return sum( (1 - scalars[i]) * c_weight.scalar(R.sum(root.associated_coroot() for root in paths_labels[i])) for i in range(len(paths_labels)) )