def desubstitute(self, u): r""" EXAMPLES: Unique preimage:: sage: from slabbe.word_morphisms import desubstitute sage: s = WordMorphism({0:[0,1],1:[1,0]}) sage: desubstitute(s, Word([0,1,0,1,1,0])) [word: 001] Non-unique preimage:: sage: s = WordMorphism({0:[0,1],1:[1,0],2:[1,0]}) sage: desubstitute(s, Word([0,1,0,1,1,0])) [word: 001, word: 002] No preimage:: sage: s = WordMorphism({0:[0,1],1:[1,0]}) sage: desubstitute(s, Word([0,1,0,1,1,1])) [] Lot of preimages (computation is done in parallel with Florent's Hivert parallel map reduce code):: sage: s = WordMorphism({0:[0,1],1:[0,1]}) sage: w = Word([0,1]) ^ 10 sage: L = desubstitute(s, w) sage: len(L) 1024 """ morph = self._morph len_u = len(u) def successor(node): L, i = node if i == len_u: return [] else: ui = u[i:] return [(L + [k], i + len(v)) for k, v in morph.iteritems() if v.is_prefix(ui)] roots = [([], 0)] post_process = lambda node: node if node[1] == len_u else None from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet R = RecursivelyEnumeratedSet(roots, successor, structure='forest', post_process=post_process) W = self.domain() map_function = lambda node: [W(node[0])] reduce_function = lambda x, y: x + y reduce_init = [] return R.map_reduce(map_function, reduce_function, reduce_init)
def __iter__(self): r""" Naive algorithm to give the elements of the conjugacy class. .. TODO:: Implement a non-naive algorithm, cf. for instance G. Butler: "An Inductive Schema for Computing Conjugacy Classes in Permutation Groups", Math. of Comp. Vol. 62, No. 205 (1994) EXAMPLES: Groups of permutations:: sage: G = SymmetricGroup(3) sage: g = G((1,2)) sage: C = ConjugacyClass(G,g) sage: sorted(C) [(2,3), (1,2), (1,3)] It works for infinite groups:: sage: a = matrix(ZZ,2,[1,1,0,1]) sage: b = matrix(ZZ,2,[1,0,1,1]) sage: G = MatrixGroup([a,b]) # takes 1s sage: a = G(a) sage: C = ConjugacyClass(G, a) sage: it = iter(C) sage: [next(it) for _ in range(5)] # random (nothing guarantees enumeration order) [ [1 1] [ 2 1] [ 0 1] [ 3 1] [ 3 4] [0 1], [-1 0], [-1 2], [-4 -1], [-1 -1] ] We check that two matrices are in C:: sage: b = G(b) sage: m1 = b * a * ~b sage: m2 = ~b * a * b sage: any(x == m1 for x in C) True sage: any(x == m2 for x in C) True """ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet g = self._representative gens = self._parent.monoid_generators() R = RecursivelyEnumeratedSet([g], lambda y: [c * y * c**-1 for c in gens], structure=None) return R.breadth_first_search_iterator()
def __iter__(self): r""" Naive algorithm to give the elements of the conjugacy class. .. TODO:: Implement a non-naive algorithm, cf. for instance G. Butler: "An Inductive Schema for Computing Conjugacy Classes in Permutation Groups", Math. of Comp. Vol. 62, No. 205 (1994) EXAMPLES: Groups of permutations:: sage: G = SymmetricGroup(3) sage: g = G((1,2)) sage: C = ConjugacyClass(G,g) sage: sorted(C) [(2,3), (1,2), (1,3)] It works for infinite groups:: sage: a = matrix(ZZ,2,[1,1,0,1]) sage: b = matrix(ZZ,2,[1,0,1,1]) sage: G = MatrixGroup([a,b]) # takes 1s sage: a = G(a) sage: C = ConjugacyClass(G, a) sage: it = iter(C) sage: [next(it) for _ in range(5)] [ [1 1] [ 2 1] [ 0 1] [ 3 1] [ 3 4] [0 1], [-1 0], [-1 2], [-4 -1], [-1 -1] ] We check that two matrices are in C:: sage: b = G(b) sage: m1 = b * a * ~b sage: m2 = ~b * a * b sage: any(x == m1 for x in C) True sage: any(x == m2 for x in C) True """ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet g = self._representative gens = self._parent.monoid_generators() R = RecursivelyEnumeratedSet([g], lambda y: [c*y*c**-1 for c in gens], structure=None) return R.breadth_first_search_iterator()
def desubstitute(self, u): r""" EXAMPLES: Unique preimage:: sage: from slabbe.word_morphisms import desubstitute sage: s = WordMorphism({0:[0,1],1:[1,0]}) sage: desubstitute(s, Word([0,1,0,1,1,0])) [word: 001] Non-unique preimage:: sage: s = WordMorphism({0:[0,1],1:[1,0],2:[1,0]}) sage: desubstitute(s, Word([0,1,0,1,1,0])) [word: 001, word: 002] No preimage:: sage: s = WordMorphism({0:[0,1],1:[1,0]}) sage: desubstitute(s, Word([0,1,0,1,1,1])) [] Lot of preimages (computation is done in parallel with Florent's Hivert parallel map reduce code):: sage: s = WordMorphism({0:[0,1],1:[0,1]}) sage: w = Word([0,1]) ^ 10 sage: L = desubstitute(s, w) sage: len(L) 1024 """ morph = self._morph len_u = len(u) def successor(node): L,i = node if i == len_u: return [] else: ui = u[i:] return [(L+[k],i+len(v)) for k,v in morph.iteritems() if v.is_prefix(ui)] roots = [([],0)] post_process = lambda node:node if node[1]==len_u else None from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet R = RecursivelyEnumeratedSet(roots, successor, structure='forest', post_process=post_process) W = self.domain() map_function = lambda node:[W(node[0])] reduce_function = lambda x,y:x+y reduce_init = [] return R.map_reduce(map_function, reduce_function, reduce_init)
def __iter__(self, index_set=None, max_depth=float("inf")): """ Returns the iterator of ``self``. INPUT: - ``index_set`` -- (Default: ``None``) The index set; if ``None`` then use the index set of the crystal - ``max_depth`` -- (Default: infinity) The maximum depth to build EXAMPLES:: sage: C = crystals.LSPaths(['A',2,1],[0,1,0]) sage: sorted([p for p in C.__iter__(max_depth=3)], key=str) [(-Lambda[0] + 2*Lambda[2] - delta,), (-Lambda[0] + Lambda[1] + 1/2*Lambda[2] - delta, Lambda[0] - 1/2*Lambda[2]), (1/2*Lambda[0] + Lambda[1] - Lambda[2] - 1/2*delta, -1/2*Lambda[0] + Lambda[2] - 1/2*delta), (2*Lambda[0] - Lambda[2],), (Lambda[0] - Lambda[1] + Lambda[2],), (Lambda[1],)] sage: [p for p in C.__iter__(index_set=[0, 1], max_depth=3)] [(Lambda[1],), (Lambda[0] - Lambda[1] + Lambda[2],), (-Lambda[0] + 2*Lambda[2] - delta,)] """ if index_set is None: index_set = self.index_set() from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet return RecursivelyEnumeratedSet( self.module_generators, lambda x: [x.f(i) for i in index_set], structure='graded', max_depth=max_depth).breadth_first_search_iterator()
def elements(self): r""" Returns the elements of ``self`` Those are constructed as the elements below the maximal elements of ``self`` in Bruhat order. OUTPUT: a :class:`RecursivelyEnumeratedSet_generic` object EXAMPLES:: sage: PF = WeylGroup(['A',3]).pieri_factors() sage: [w.reduced_word() for w in PF.elements()] [[3, 2, 1], [2, 1], [1], [], [3, 1], [3], [3, 2], [2]] .. SEEALSO:: :meth:`maximal_elements` .. TODO:: Possibly remove this method and instead have this class inherit from :class:`RecursivelyEnumeratedSet_generic`. """ return RecursivelyEnumeratedSet(self.maximal_elements(), attrcall('bruhat_lower_covers'), structure=None, enumeration='naive')
def category_sample(): r""" Return a sample of categories. It is constructed by looking for all concrete category classes declared in ``sage.categories.all``, calling :meth:`Category.an_instance` on those and taking all their super categories. EXAMPLES:: sage: from sage.categories.category import category_sample sage: sorted(category_sample(), key=str) [Category of G-sets for Symmetric group of order 8! as a permutation group, Category of Hecke modules over Rational Field, Category of additive magmas, ..., Category of fields, ..., Category of graded hopf algebras with basis over Rational Field, ..., Category of modular abelian varieties over Rational Field, ..., Category of simplicial complexes, ..., Category of vector spaces over Rational Field, ..., Category of weyl groups,... """ import sage.categories.all abstract_classes_for_categories = [Category] seeds = { cls.an_instance() for cls in sage.categories.all.__dict__.values() if isinstance(cls, type) and issubclass(cls, Category) and cls not in abstract_classes_for_categories } return list(RecursivelyEnumeratedSet(seeds, related_categories))
def connected_component_iterator(self, roots=None): r""" Return an iterator over the connected component of the root. This method overwrites the methods :meth:`slabbe.discrete_subset.DiscreteSubset.connected_component_iterator`, because for billiard words, we go only in one direction in each axis which allows to use a forest structure for the enumeration. INPUT: - ``roots`` - list of some elements immutable in self EXAMPLES:: sage: from slabbe import BilliardCube sage: p = BilliardCube([1,pi,sqrt(7)]) sage: root = vector((0,0,0)) sage: root.set_immutable() sage: it = p.connected_component_iterator(roots=[root]) sage: [next(it) for _ in range(5)] [(0, 0, 0), (0, 1, 0), (0, 1, 1), (0, 2, 1), (1, 2, 1)] :: sage: p = BilliardCube([1,pi,7.45], start=(10.2,20.4,30.8)) sage: it = p.connected_component_iterator() sage: [next(it) for _ in range(5)] [(10.2000000000000, 20.4000000000000, 30.8000000000000), (10.2000000000000, 20.4000000000000, 31.8000000000000), (10.2000000000000, 21.4000000000000, 31.8000000000000), (10.2000000000000, 21.4000000000000, 32.8000000000000), (10.2000000000000, 21.4000000000000, 33.8000000000000)] """ roots = roots if roots else [self.an_element()] if not all(root in self for root in roots): raise ValueError("roots (=%s) must all be in self(=%s)" % (roots, self)) #for root in roots: # root.set_immutable() C = RecursivelyEnumeratedSet(seeds=roots, successors=self.children, structure='forest') return C.breadth_first_search_iterator()
def __iter__(self): """ Returns the iterator of ``self``. EXAMPLES:: sage: KRT = crystals.TensorProductOfKirillovReshetikhinTableaux(['A', 3, 1], [[2,1], [1,1]]) sage: g = KRT.__iter__() sage: next(g) [[2], [3]] (X) [[1]] sage: next(g) [[2], [4]] (X) [[1]] """ index_set = self._cartan_type.classical().index_set() from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet return RecursivelyEnumeratedSet(self.module_generators, lambda x: [x.f(i) for i in index_set], structure=None).naive_search_iterator()
def dominant_maximal_weights(self): r""" Return the dominant maximal weights of ``self``. A weight `\mu` is *maximal* if it has nonzero multiplicity but `\mu + \delta`` has multiplicity zero. There are a finite number of dominant maximal weights. Indeed, [Kac]_ Proposition 12.6 shows that the dominant maximal weights are in bijection with the classical weights in `k \cdot F` where `F` is the fundamental alcove and `k` is the level. The construction used in this method is based on that Proposition. EXAMPLES:: sage: Lambda = RootSystem(['C',3,1]).weight_lattice(extended=true).fundamental_weights() sage: IntegrableRepresentation(2*Lambda[0]).dominant_maximal_weights() (2*Lambda[0], Lambda[0] + Lambda[2] - delta, 2*Lambda[1] - delta, Lambda[1] + Lambda[3] - 2*delta, 2*Lambda[2] - 2*delta, 2*Lambda[3] - 3*delta) """ k = self.level() Lambda = self._P.fundamental_weights() def next_level(wt): return [ wt + Lambda[i] for i in self._index_set_classical if (wt + Lambda[i]).level() <= k ] R = RecursivelyEnumeratedSet([self._P.zero()], next_level) candidates = [x + (k - x.level()) * Lambda[0] for x in list(R)] ret = [] delta = self._Q.null_root() for x in candidates: if self._from_weight_helper(self._Lam - x, check=True): t = 0 while self.m(self.from_weight(x - t * delta)) == 0: t += 1 ret.append(x - t * delta) return tuple(ret)
def ideal(self, gens, side="twosided"): r""" Return the ``side``-sided ideal generated by ``gens``. This brute force implementation recursively multiplies the elements of ``gens`` by the distinguished generators of this semigroup. .. SEEALSO:: :meth:`semigroup_generators` INPUT: - ``gens`` -- a list (or iterable) of elements of ``self`` - ``side`` -- [default: "twosided"] "left", "right" or "twosided" EXAMPLES:: sage: S = FiniteSemigroups().example() sage: sorted(S.ideal([S('cab')], side="left")) ['abc', 'abcd', 'abdc', 'acb', 'acbd', 'acdb', 'adbc', 'adcb', 'bac', 'bacd', 'badc', 'bca', 'bcad', 'bcda', 'bdac', 'bdca', 'cab', 'cabd', 'cadb', 'cba', 'cbad', 'cbda', 'cdab', 'cdba', 'dabc', 'dacb', 'dbac', 'dbca', 'dcab', 'dcba'] sage: list(S.ideal([S('cab')], side="right")) ['cab', 'cabd'] sage: sorted(S.ideal([S('cab')], side="twosided")) ['abc', 'abcd', 'abdc', 'acb', 'acbd', 'acdb', 'adbc', 'adcb', 'bac', 'bacd', 'badc', 'bca', 'bcad', 'bcda', 'bdac', 'bdca', 'cab', 'cabd', 'cadb', 'cba', 'cbad', 'cbda', 'cdab', 'cdba', 'dabc', 'dacb', 'dbac', 'dbca', 'dcab', 'dcba'] sage: sorted(S.ideal([S('cab')])) ['abc', 'abcd', 'abdc', 'acb', 'acbd', 'acdb', 'adbc', 'adcb', 'bac', 'bacd', 'badc', 'bca', 'bcad', 'bcda', 'bdac', 'bdca', 'cab', 'cabd', 'cadb', 'cba', 'cbad', 'cbda', 'cdab', 'cdba', 'dabc', 'dacb', 'dbac', 'dbca', 'dcab', 'dcba'] """ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet return RecursivelyEnumeratedSet(gens, self.succ_generators(side=side))
def __iter__(self): r""" Return an iterator over the elements of ``self``. This brute force implementation recursively multiplies together the distinguished semigroup generators. .. SEEALSO:: :meth:`semigroup_generators` EXAMPLES:: sage: S = FiniteSemigroups().example(alphabet=('x','y')) sage: it = S.__iter__() sage: list(it) ['x', 'y', 'yx', 'xy'] """ from sage.sets.recursively_enumerated_set import RecursivelyEnumeratedSet return iter(RecursivelyEnumeratedSet(self.semigroup_generators(), self.succ_generators(side="right"), enumeration='breadth'))
def branch(self, i=None, weyl_character_ring=None, sequence=None, depth=5): r""" Return the branching rule on ``self``. Removing any node from the extended Dynkin diagram of the affine Lie algebra results in the Dynkin diagram of a classical Lie algebra, which is therefore a Lie subalgebra. For example removing the `0` node from the Dynkin diagram of type ``[X, r, 1]`` produces the classical Dynkin diagram of ``[X, r]``. Thus for each `i` in the index set, we may restrict ``self`` to the corresponding classical subalgebra. Of course ``self`` is an infinite dimensional representation, but each weight `\mu` is assigned a grading by the number of times the simple root `\alpha_i` appears in `\Lambda-\mu`. Thus the branched representation is graded and we get sequence of finite-dimensional representations which this method is able to compute. OPTIONAL: - ``i`` -- (default: 0) an element of the index set - ``weyl_character_ring`` -- a WeylCharacterRing - ``sequence`` -- a dictionary - ``depth`` -- (default: 5) an upper bound for `k` determining how many terms to give In the default case where `i = 0`, you do not need to specify anything else, though you may want to increase the depth if you need more terms. EXAMPLES:: sage: Lambda = RootSystem(['A',2,1]).weight_lattice(extended=true).fundamental_weights() sage: V = IntegrableRepresentation(2*Lambda[0]) sage: b = V.branch(); b [A2(0,0), A2(1,1), A2(0,0) + 2*A2(1,1) + A2(2,2), 2*A2(0,0) + 2*A2(0,3) + 4*A2(1,1) + 2*A2(3,0) + 2*A2(2,2), 4*A2(0,0) + 3*A2(0,3) + 10*A2(1,1) + 3*A2(3,0) + A2(1,4) + 6*A2(2,2) + A2(4,1), 6*A2(0,0) + 9*A2(0,3) + 20*A2(1,1) + 9*A2(3,0) + 3*A2(1,4) + 12*A2(2,2) + 3*A2(4,1) + A2(3,3)] If the parameter ``weyl_character_ring`` is omitted, the ring may be recovered as the parent of one of the branched coefficients:: sage: A2 = b[0].parent(); A2 The Weyl Character Ring of Type A2 with Integer Ring coefficients If `i` is not zero then you should specify the :class:`WeylCharacterRing` that you are branching to. This is determined by the Dynkin diagram:: sage: Lambda = RootSystem(['B',3,1]).weight_lattice(extended=true).fundamental_weights() sage: V = IntegrableRepresentation(Lambda[0]) sage: V.cartan_type().dynkin_diagram() O 0 | | O---O=>=O 1 2 3 B3~ In this example, we observe that removing the `i=2` node from the Dynkin diagram produces a reducible diagram of type ``A1xA1xA1``. Thus we have a branching to `\mathfrak{sl}(2) \times \mathfrak{sl}(2) \times \mathfrak{sl}(2)`:: sage: A1xA1xA1 = WeylCharacterRing("A1xA1xA1",style="coroots") sage: V.branch(i=2,weyl_character_ring=A1xA1xA1) [A1xA1xA1(1,0,0), A1xA1xA1(0,1,2), A1xA1xA1(1,0,0) + A1xA1xA1(1,2,0) + A1xA1xA1(1,0,2), A1xA1xA1(2,1,2) + A1xA1xA1(0,1,0) + 2*A1xA1xA1(0,1,2), 3*A1xA1xA1(1,0,0) + 2*A1xA1xA1(1,2,0) + A1xA1xA1(1,2,2) + 2*A1xA1xA1(1,0,2) + A1xA1xA1(1,0,4) + A1xA1xA1(3,0,0), A1xA1xA1(2,1,0) + 3*A1xA1xA1(2,1,2) + 2*A1xA1xA1(0,1,0) + 5*A1xA1xA1(0,1,2) + A1xA1xA1(0,1,4) + A1xA1xA1(0,3,2)] If the nodes of the two Dynkin diagrams are not in the same order, you must specify an additional parameter, ``sequence`` which gives a dictionary to the affine Dynkin diagram to the classical one. EXAMPLES:: sage: Lambda = RootSystem(['F',4,1]).weight_lattice(extended=true).fundamental_weights() sage: V = IntegrableRepresentation(Lambda[0]) sage: V.cartan_type().dynkin_diagram() O---O---O=>=O---O 0 1 2 3 4 F4~ sage: A1xC3=WeylCharacterRing("A1xC3",style="coroots") sage: A1xC3.dynkin_diagram() O 1 O---O=<=O 2 3 4 A1xC3 Observe that removing the `i=1` node from the ``F4~`` Dynkin diagram gives the ``A1xC3`` diagram, but the roots are in a different order. The nodes `0, 2, 3, 4` of ``F4~`` correspond to ``1, 4, 3, 2`` of ``A1xC3`` and so we encode this in a dictionary:: sage: V.branch(i=1,weyl_character_ring=A1xC3,sequence={0:1,2:4,3:3,4:2}) # long time [A1xC3(1,0,0,0), A1xC3(0,0,0,1), A1xC3(1,0,0,0) + A1xC3(1,2,0,0), A1xC3(2,0,0,1) + A1xC3(0,0,0,1) + A1xC3(0,1,1,0), 2*A1xC3(1,0,0,0) + A1xC3(1,0,1,0) + 2*A1xC3(1,2,0,0) + A1xC3(1,0,2,0) + A1xC3(3,0,0,0), 2*A1xC3(2,0,0,1) + A1xC3(2,1,1,0) + A1xC3(0,1,0,0) + 3*A1xC3(0,0,0,1) + 2*A1xC3(0,1,1,0) + A1xC3(0,2,0,1)] The branch method gives a way of computing the graded dimension of the integrable representation:: sage: Lambda = RootSystem("A1~").weight_lattice(extended=true).fundamental_weights() sage: V=IntegrableRepresentation(Lambda[0]) sage: r = [x.degree() for x in V.branch(depth=15)]; r [1, 3, 4, 7, 13, 19, 29, 43, 62, 90, 126, 174, 239, 325, 435, 580] sage: oeis(r) # optional -- internet 0: A029552: Expansion of phi(x) / f(-x) in powers of x where phi(), f() are Ramanujan theta functions. """ if i is None: i = self._cartan_type.special_node() if i == self._cartan_type.special_node() or self._cartan_type.type( ) == 'A': if weyl_character_ring is None: weyl_character_ring = WeylCharacterRing( self._cartan_type.classical(), style="coroots") if weyl_character_ring.cartan_type( ) != self._cartan_type.classical(): raise ValueError( "Cartan type of WeylCharacterRing must be %s" % self.cartan_type().classical()) elif weyl_character_ring is None: raise ValueError( "the argument weyl_character_ring cannot be omitted if i != 0") if sequence is None: sequence = {} for j in self._index_set: if j < i: sequence[j] = j + 1 elif j > i: sequence[j] = j def next_level(x): ret = [] for j in self._index_set: t = list(x[0]) t[j] += 1 t = tuple(t) m = self.m(t) if m > 0 and t[i] <= depth: ret.append((t, m)) return ret hwv = (tuple([0 for j in self._index_set]), 1) terms = RecursivelyEnumeratedSet([hwv], next_level) fw = weyl_character_ring.fundamental_weights() P = self.weight_lattice() ret = [] for l in range(depth + 1): lterms = [x for x in terms if x[0][i] == l] ldict = {} for x in lterms: mc = P(self.to_weight(x[0])).monomial_coefficients() contr = sum(fw[sequence[j]] * mc.get(j, 0) for j in self._index_set if j != i).coerce_to_sl() if contr in ldict: ldict[contr] += x[1] else: ldict[contr] = x[1] ret.append(weyl_character_ring.char_from_weights(ldict)) return ret