def coproduct_on_basis(self, x): r""" Return the coproduct of `F_{\sigma}` for `\sigma` a permutation. EXAMPLES:: sage: A = algebras.FQSym(QQ).F() sage: x = A([1]) sage: ascii_art(A.coproduct(A.one())) # indirect doctest 1 # 1 sage: ascii_art(A.coproduct(x)) # indirect doctest 1 # F + F # 1 [1] [1] sage: A = algebras.FQSym(QQ).F() sage: x, y, z = A([1]), A([2,1]), A([3,2,1]) sage: A.coproduct(z) F[] # F[3, 2, 1] + F[1] # F[2, 1] + F[2, 1] # F[1] + F[3, 2, 1] # F[] """ if not len(x): return self.one().tensor(self.one()) return sum( self(Word(x[:i]).standard_permutation()).tensor( self(Word(x[i:]).standard_permutation())) for i in range(len(x) + 1))
def __iter__(self): """ TESTS:: sage: WeightedIntegerVectors(7, [2,2]).list() [] sage: WeightedIntegerVectors(3, [2,1,1]).list() [[1, 0, 1], [1, 1, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0]] :: sage: ivw = [ WeightedIntegerVectors(k, [1,1,1]) for k in range(11) ] sage: iv = [ IntegerVectors(k, 3) for k in range(11) ] sage: all( [ sorted(iv[k].list()) == sorted(ivw[k].list()) for k in range(11) ] ) True :: sage: ivw = [ WeightedIntegerVectors(k, [2,3,7]) for k in range(11) ] sage: all( [ i.cardinality() == len(i.list()) for i in ivw] ) True """ if len(self._weights) == 0: if self._n == 0: yield [] return perm = Word(self._weights).standard_permutation() l = [x for x in sorted(self._weights)] for x in self._recfun(self._n, l): yield perm.action(x)
def to_word(self, use_str=True, upper_case_as_inverse=True): """ Convert ``self`` to a word. A free group element is a reduced words in the generators and their inverses. This is naturally a finite word. Some choices have to be done: 1. ``use_str==True``: the letters of the words are strings (possibly a single character, but not necessarily). ``use_str==False``: the letters of the word are the generators and their inverses themselves, that is to say the letters are FreeGroupElement. 2. ``upper_case_as_inverse==True``: the inverse of a generator is written as the same string in upper case. This is used only with the ``use_string==True`` option. INPUT: - ``use_str`` -- (default: ``True``) use strings rather than :class:`FreeGroupElement` as letters - ``upper_case_as_inverse`` -- (default: ``True``) use upper case letters as inverses OUTPUT: A finite :class:`~sage.combinat.words.finite_word.FiniteWord_class`. EXAMPLES:: sage: from train_track import * sage: F = FreeGroup(3) sage: w = F([1,-2,1,3,-1]) sage: w.to_word() word: x0,X1,x0,x2,X0 sage: type(w.to_word()[1]) <... 'str'> sage: w.to_word(use_str=False) word: x0,x1^-1,x0,x2,x0^-1 sage: w.to_word(use_str=False)[1] == w[1] True """ from sage.combinat.words.word import Word if use_str and upper_case_as_inverse: wt = self.Tietze() A = self.parent().variable_names() w = [] for a in wt: if a > 0: w.append(A[a - 1]) else: w.append(A[-a - 1].upper()) return Word(w) if use_str: return Word([str(a) for a in self]) return Word(list(self))
def spanning_tree(self, verbose=False): """ A spanning tree. OUPUT: a dictionnary that maps each vertex to an edge-path from the origin vertex. SEE ALSO: ``maximal_tree()`` that returns a list of edges of a spanning tree. WARNING: ``self`` must be connected. """ A = self._alphabet tree = {self.initial_vertex(A[0]): Word()} done = False while not done: done = True for a in A.positive_letters(): vi = self.initial_vertex(a) vt = self.terminal_vertex(a) if vi in tree and vt not in tree: tree[vt] = self.reduce_path(tree[vi] * Word([a])) done = False elif vt in tree and vi not in tree: tree[vi] = self.reduce_path(tree[vt] * Word([A.inverse_letter(a)])) done = False return tree
def _create_rep_gen_set(self, n, p): """ A helper function. Creates the gen_set for has_irred_rep(). """ import itertools alphabet = [_to_word(self.gen(i)) for i in range(self.ngens())] power_word = [alphabet[i]**n for i in range(self.ngens())] for i in range(1, p + 1): for words in itertools.product(alphabet, repeat=i): yield_word = True word = Word('') for w in words: word = word * w rewrite = [] empty_word = Word('') for rel in self._reduce: if rel[0].is_factor(word): yield_word = False break if yield_word: for pw in power_word: if pw.is_factor(word): yield_word = False break if yield_word: yield word
def subdivide(self, edge_list): """ Subdvides each of the edges in ``edge_list`` into two edges. An edge may appear several time in the list: it will be subdivided as many times. OUTPUT: A dictionnary that maps an old edge to a path in the new graph. """ A = self._alphabet result_map = dict((e, Word([e])) for e in A) new_edges = A.add_new_letters(len(edge_list)) new_vertices = self.new_vertices(len(edge_list)) for i, e in enumerate(edge_list): ee = A.inverse_letter(e) ne = result_map[e][-1] v = new_vertices[i] vt = self.terminal_vertex(result_map[e][-1]) f = new_edges[i][0] ff = new_edges[i][1] self.set_terminal_vertex(ne, v) self.add_edge(v, vt, [f, ff]) result_map[e] = result_map[e] * Word([f]) result_map[ee] = Word([ff]) * result_map[ee] return result_map
def rips_machine_moves(self,side=None,holes=None,orientation=None,verbose=False): r""" Return the list of possible moves of the Rips machine. A move is an automorphism of the free group. Assume that self is the convex core of an automorphism phi. Let psi be an automorphism given by ``self.rips_machine_moces(side=1)``, then ``ConvexCore(phi*psi)`` is obtained from ``self`` by a move of the Rips machine. On the other side: if ``psi`` is given by ``self.rips_machine_moces(side=0)``, then ``ConvexCore(psi.inverse()*phi)`` is given by a move of the Rips machine. INPUT: - `side` (default 0): side of Rips moves to find, either 0, 1 or None for both sides. OUPUT: - A list of free group automorphisms. """ if holes is None: holes=self.rips_machine_holes(side=side,orientation=orientation,verbose=verbose and verbose>1 and verbose-1) result=[] for i,hole in enumerate(holes): e,b,start_letters,end_letters=hole A=self.tree(side=b[1]).alphabet() hole_map=dict((a,Word([a])) for a in A.positive_letters()) bb=A.inverse_letter(b[0]) if bb in start_letters: if verbose: print i,":",hole print "surgery:",e[2],",",(bb,b[1]) for x in end_letters: if A.is_positive_letter(x): hole_map[x]=Word([bb])*hole_map[x] else: xx=A.inverse_letter(x) hole_map[xx]=hole_map[xx]*Word([b[0]]) else: if verbose: print i,":",hole print "surgery:",e[2],",",b for x in start_letters: if A.is_positive_letter(x): hole_map[x]=Word([bb])*hole_map[x] else: xx=A.inverse_letter(x) hole_map[xx]=hole_map[xx]*Word([b[0]]) result.append(hole_map) return result
def list(self): """ TESTS:: sage: WeightedIntegerVectors(7, [2,2]).list() [] sage: WeightedIntegerVectors(3, [2,1,1]).list() [[1, 0, 1], [1, 1, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0]] :: sage: ivw = [ WeightedIntegerVectors(k, [1,1,1]) for k in range(11) ] sage: iv = [ IntegerVectors(k, 3) for k in range(11) ] sage: all( [ sorted(iv[k].list()) == sorted(ivw[k].list()) for k in range(11) ] ) True :: sage: ivw = [ WeightedIntegerVectors(k, [2,3,7]) for k in range(11) ] sage: all( [ i.cardinality() == len(i.list()) for i in ivw] ) True """ if len(self.weight) == 0: if self.n == 0: return [[]] else: return [] perm = Word(self.weight).standard_permutation() l = [x for x in sorted(self.weight)] return [perm.action(_) for _ in self._recfun(self.n, l)]
def list(self): """ TESTS:: sage: WeightedIntegerVectors(7, [2,2]).list() [] sage: WeightedIntegerVectors(3, [2,1,1]).list() [[1, 0, 1], [1, 1, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0]] :: sage: ivw = [ WeightedIntegerVectors(k, [1,1,1]) for k in range(11) ] sage: iv = [ IntegerVectors(k, 3) for k in range(11) ] sage: all( [ sorted(iv[k].list()) == sorted(ivw[k].list()) for k in range(11) ] ) True :: sage: ivw = [ WeightedIntegerVectors(k, [2,3,7]) for k in range(11) ] sage: all( [ i.cardinality() == len(i.list()) for i in ivw] ) True """ if len(self.weight) == 0: if self.n == 0: return [[]] else: return [] perm = Word(self.weight).standard_permutation() l = [x for x in sorted(self.weight)] return [perm.action(_) for _ in self._recfun(self.n,l)]
def blow_up_vertices(self, germ_components): """ Blow-up ``self`` according to classes of germs given in ``germ_components``. INPUT: ``germ_components`` a list of classes of germs outgoing from a vertex. OUTPUT: A dictionnay that maps an old edge to the path in the new graph. """ A = self.alphabet() result_map = dict((a, Word([a])) for a in A) new_vertices = self.new_vertices(len(germ_components)) new_edges = A.add_new_letters(len(germ_components)) for i, c in enumerate(germ_components): v0 = self.initial_vertex(c[0]) vc = new_vertices[i] ec = new_edges[i] self.add_edge(v0, vc, ec) for a in c: self.set_initial_vertex(a, vc) result_map[a] = Word([ec[0]]) * result_map[a] aa = A.inverse_letter(a) result_map[aa] = result_map[aa] * Word([ec[1]]) return result_map
def spin_rec(t, nexts, current, part, weight, length): """ Routine used for constructing the spin polynomial. INPUT: - ``weight`` - list of non-negative integers - ``length`` - the length of the ribbons we're tiling with - ``t`` - the variable EXAMPLES:: sage: from sage.combinat.ribbon_tableau import spin_rec sage: sp = SkewPartition sage: t = ZZ['t'].gen() sage: spin_rec(t, [], [[[], [3, 3]]], sp([[2, 2, 2], []]), [2], 3) [t^4] sage: spin_rec(t, [[0], [t^4]], [[[2, 1, 1, 1, 1], [0, 3]], [[2, 2, 2], [3, 0]]], sp([[2, 2, 2, 2, 1], []]), [2, 1], 3) [t^5] sage: spin_rec(t, [], [[[], [3, 3, 0]]], sp([[3, 3], []]), [2], 3) [t^2] sage: spin_rec(t, [[t^4], [t^3], [t^2]], [[[2, 2, 2], [0, 0, 3]], [[3, 2, 1], [0, 3, 0]], [[3, 3], [3, 0, 0]]], sp([[3, 3, 3], []]), [2, 1], 3) [t^6 + t^4 + t^2] sage: spin_rec(t, [[t^5], [t^4], [t^6 + t^4 + t^2]], [[[2, 2, 2, 2, 1], [0, 0, 3]], [[3, 3, 1, 1, 1], [0, 3, 0]], [[3, 3, 3], [3, 0, 0]]], sp([[3, 3, 3, 2, 1], []]), [2, 1, 1], 3) [2*t^7 + 2*t^5 + t^3] """ from sage.combinat.words.word import Word R = ZZ['t'] if current == []: return [R(0)] tmp = [] partp = part[0].conjugate() #compute the contribution of the ribbons added at #the current node for perms in [current[i][1] for i in range(len(current))]: perm = [ partp[i] + len(partp) - (i + 1) - perms[i] for i in range(len(partp)) ] perm.reverse() perm = Word(perm).standard_permutation() tmp.append((weight[-1] * (length - 1) - perm.number_of_inversions())) if nexts != []: return [ sum([ sum([t**tmp[i] * nexts[i][j] for j in range(len(nexts[i]))]) for i in range(len(tmp)) ]) ] else: return [sum([t**tmp[i] for i in range(len(tmp))])]
def splitting(i, A): """ The ``MarkedMetricGraph`` that corresponds to the splitting ``F(A)=F(A[:i])*F(A[i:])``. This is a graph with two vertices linked by an edge e and a loop for each letter in A. Letters in A[:i] are attached to the first vertex while letters in A[:i] are attached to the second vertex. All loops have length 0, the splitting edge ``e`` has length 1. INPUT: - ``i`` -- integer correspond to the splitting index - ``A`` -- an alphabet OUTPUT: The ``MarkedMetricGraph`` corresponding to splitting. EXAMPLES:: sage: from train_track import * sage: from train_track.marked_graph import MarkedMetricGraph sage: A = AlphabetWithInverses(5) sage: print(MarkedMetricGraph.splitting(2,A)) Marked graph: a: 0->0, b: 0->0, c: 1->1, d: 1->1, e: 1->1, f: 0->1 Marking: a->a, b->b, c->fcF, d->fdF, e->feF Length: a:0, b:0, c:0, d:0, e:0, f:1 """ graph = dict() length = dict() marking = dict() B = A.copy() [e, ee] = B.add_new_letter() for j in range(i): a = A[j] graph[a] = (0, 0) length[a] = 0 marking[a] = Word([a]) for j in range(i, len(A)): a = A[j] graph[a] = (1, 1) length[a] = 0 marking[a] = Word([e, a, ee]) graph[e] = (0, 1) length[e] = 1 return MarkedMetricGraph(graph, marking, length, B, A)
def spin_rec(t, nexts, current, part, weight, length): """ Routine used for constructing the spin polynomial. INPUT: - ``weight`` - list of non-negative integers - ``length`` - the length of the ribbons we're tiling with - ``t`` - the variable EXAMPLES:: sage: from sage.combinat.ribbon_tableau import spin_rec sage: sp = SkewPartition sage: t = ZZ['t'].gen() sage: spin_rec(t, [], [[[], [3, 3]]], sp([[2, 2, 2], []]), [2], 3) [t^4] sage: spin_rec(t, [[0], [t^4]], [[[2, 1, 1, 1, 1], [0, 3]], [[2, 2, 2], [3, 0]]], sp([[2, 2, 2, 2, 1], []]), [2, 1], 3) [t^5] sage: spin_rec(t, [], [[[], [3, 3, 0]]], sp([[3, 3], []]), [2], 3) [t^2] sage: spin_rec(t, [[t^4], [t^3], [t^2]], [[[2, 2, 2], [0, 0, 3]], [[3, 2, 1], [0, 3, 0]], [[3, 3], [3, 0, 0]]], sp([[3, 3, 3], []]), [2, 1], 3) [t^6 + t^4 + t^2] sage: spin_rec(t, [[t^5], [t^4], [t^6 + t^4 + t^2]], [[[2, 2, 2, 2, 1], [0, 0, 3]], [[3, 3, 1, 1, 1], [0, 3, 0]], [[3, 3, 3], [3, 0, 0]]], sp([[3, 3, 3, 2, 1], []]), [2, 1, 1], 3) [2*t^7 + 2*t^5 + t^3] """ from sage.combinat.words.word import Word R = ZZ['t'] if current == []: return [R(0)] tmp = [] partp = part[0].conjugate() #compute the contribution of the ribbons added at #the current node for perms in [current[i][1] for i in range(len(current))]: perm = [partp[i] + len(partp)-(i+1)-perms[i] for i in range(len(partp))] perm.reverse() perm = Word(perm).standard_permutation() tmp.append( (weight[-1]*(length-1)-perm.number_of_inversions()) ) if nexts != []: return [sum([sum([t**tmp[i]*nexts[i][j] for j in range(len(nexts[i]))]) for i in range(len(tmp))])] else: return [sum([t**tmp[i] for i in range(len(tmp))])]
def succ_product_on_basis(self, x, y): r""" Return the `\succ` product of two permutations. This is the shifted shuffle of `x` and `y` with the additional condition that the first letter of the result comes from `y`. The usual symbol for this operation is `\succ`. .. SEEALSO:: - :meth:`product_on_basis`, :meth:`prec_product_on_basis` EXAMPLES:: sage: A = algebras.FQSym(QQ).F() sage: x = Permutation([1,2]) sage: A.succ_product_on_basis(x, x) F[3, 1, 2, 4] + F[3, 1, 4, 2] + F[3, 4, 1, 2] sage: y = Permutation([]) sage: A.succ_product_on_basis(x, y) == 0 True sage: A.succ_product_on_basis(y, x) == A(x) True TESTS:: sage: u = A.one().support()[0] sage: A.succ_product_on_basis(u, u) Traceback (most recent call last): ... ValueError: products | < | and | > | are not defined """ if not y: if not x: raise ValueError( "products | < | and | > | are not defined") else: return self.zero() basis = self.basis() if not x: return basis[y] K = basis.keys() n = len(x) shy = Word([a + n for a in y]) shy0 = shy[0] return self.sum(basis[K([shy0] + list(u))] for u in Word(x).shuffle(Word(shy[1:])))
def subdivide(self, edge_list): """ Subdvides each of the edges in ``edge_list`` into two edges. An edge may appear several time in the list: it will be subdivided as many times. INPUT: - ``edge_list`` -- list of edges OUTPUT: A dictionnary that maps an old edge to a path in the new graph. EXAMPLES:: sage: from train_track.inverse_graph import GraphWithInverses sage: G = GraphWithInverses([[0,0,'a'],[0,1,'b'],[1,1,'c']]) sage: G.subdivide(['a','c']) {'A': word: DA, 'B': word: B, 'C': word: EC, 'a': word: ad, 'b': word: b, 'c': word: ce} sage: print(G) a: 0->2, b: 0->1, c: 1->3, d: 2->0, e: 3->1 """ A = self._alphabet result_map = dict((e, Word([e])) for e in A) new_edges = A.add_new_letters(len(edge_list)) new_vertices = self.new_vertices(len(edge_list)) for i, e in enumerate(edge_list): ee = A.inverse_letter(e) ne = result_map[e][-1] v = new_vertices[i] vt = self.terminal_vertex(result_map[e][-1]) f = new_edges[i][0] ff = new_edges[i][1] self.set_terminal_vertex(ne, v) self.add_edge(v, vt, [f, ff]) result_map[e] = result_map[e] * Word([f]) result_map[ee] = Word([ff]) * result_map[ee] return result_map
def reverse_sorting_permutation( t): # TODO: put "stable sorting" as keyword somewhere r""" Return a permutation `p` such that is decreasing INPUT: - `t` -- a list/tuple/... of numbers OUTPUT: a minimal permutation `p` such that `w \circ p` is sorted decreasingly EXAMPLES:: sage: t = [3, 3, 1, 2] sage: s = reverse_sorting_permutation(t); s [1, 2, 4, 3] sage: [t[s[i]-1] for i in range(len(t))] [3, 3, 2, 1] sage: t = [4, 2, 3, 2, 1, 3] sage: s = reverse_sorting_permutation(t); s [1, 3, 6, 2, 4, 5] sage: [t[s[i]-1] for i in range(len(t))] [4, 3, 3, 2, 2, 1] """ return ~(Word([-i for i in t]).standard_permutation())
def to_word(self, alphabet=[1, 2, 3]): r""" Return the billiard word. INPUT: - ``alphabet`` - list EXAMPLES:: sage: from slabbe import BilliardCube sage: b = BilliardCube((1,pi,sqrt(2))) sage: b.to_word() word: 2321232212322312232123221322231223212322... :: sage: B = BilliardCube((sqrt(3),sqrt(5),sqrt(7))) sage: B.to_word() word: 3213213231232133213213231232132313231232... """ steps = map(vector, ((1, 0, 0), (0, 1, 0), (0, 0, 1))) for step in steps: step.set_immutable() coding = dict(zip(steps, alphabet)) it = (coding[step] for step in self.step_iterator()) #WP = WordPaths(alphabet, [(1,0,0),(0,1,0),(0,0,1)]) return Word(it, alphabet=alphabet)
def rose_marked_graph(alphabet): """ The rose on ``alphabet`` marked with the identity. INPUT: - ``alphabet`` -- an alphabet OUTPUT: The rose on ``alphabet`` marked with the identity. EXAMPLES:: sage: from train_track import * sage: from train_track.inverse_graph import GraphWithInverses sage: from train_track.marked_graph import MarkedGraph sage: print(MarkedGraph.rose_marked_graph(AlphabetWithInverses(2))) Marked graph: a: 0->0, b: 0->0 Marking: a->a, b->b """ marking = dict((a, Word([a])) for a in alphabet.positive_letters()) return MarkedGraph(graph=GraphWithInverses.rose_graph(alphabet), marking=marking, marking_alphabet=alphabet)
def __iter__(self): """ TESTS:: sage: WeightedIntegerVectors(7, [2,2]).list() [] sage: WeightedIntegerVectors(3, [2,1,1]).list() [[1, 0, 1], [1, 1, 0], [0, 0, 3], [0, 1, 2], [0, 2, 1], [0, 3, 0]] :: sage: ivw = [ WeightedIntegerVectors(k, [1,1,1]) for k in range(11) ] sage: iv = [ IntegerVectors(k, 3) for k in range(11) ] sage: all(sorted(map(list, iv[k])) == sorted(map(list, ivw[k])) for k in range(11)) True :: sage: ivw = [ WeightedIntegerVectors(k, [2,3,7]) for k in range(11) ] sage: all(i.cardinality() == len(i.list()) for i in ivw) True """ if not self._weights: if self._n == 0: yield self.element_class(self, []) return perm = Word(self._weights).standard_permutation() perm = [len(self._weights) - i for i in perm] l = [x for x in sorted(self._weights, reverse=True)] for x in iterator_fast(self._n, l): yield self.element_class(self, [x[i] for i in perm])
def to_labelling_permutation(self): r""" Returns the labelling of the support Dyck path of the parking function. INPUT: - ``self`` -- parking function word OUTPUT: - returns the labelling of the Dyck path EXAMPLES:: sage: PF = ParkingFunction([6, 1, 5, 2, 2, 1, 5]) sage: PF.to_labelling_permutation() [2, 6, 4, 5, 3, 7, 1] :: sage: ParkingFunction([3,1,1,4]).to_labelling_permutation() [2, 3, 1, 4] sage: ParkingFunction([4,1,1,1]).to_labelling_permutation() [2, 3, 4, 1] sage: ParkingFunction([2,1,4,1]).to_labelling_permutation() [2, 4, 1, 3] """ from sage.combinat.words.word import Word return Word(self).standard_permutation().inverse()
def __iter__(self): """ TESTS:: sage: [ p for p in OrderedSetPartitions([1,2,3,4], [2,1,1]) ] [[{1, 2}, {3}, {4}], [{1, 2}, {4}, {3}], [{1, 3}, {2}, {4}], [{1, 4}, {2}, {3}], [{1, 3}, {4}, {2}], [{1, 4}, {3}, {2}], [{2, 3}, {1}, {4}], [{2, 4}, {1}, {3}], [{3, 4}, {1}, {2}], [{2, 3}, {4}, {1}], [{2, 4}, {3}, {1}], [{3, 4}, {2}, {1}]] """ comp = self.c lset = [x for x in self._set] l = len(self.c) dcomp = [-1] + comp.descents(final_descent=True) p = [] for j in range(l): p += [j + 1] * comp[j] for x in permutation.Permutations(p): res = permutation.Permutation(range( 1, len(lset))) * Word(x).standard_permutation().inverse() res = [lset[x - 1] for x in res] yield self.element_class( self, [Set(res[dcomp[i] + 1:dcomp[i + 1] + 1]) for i in range(l)])
def discrete_curve(nb_equipes, max_points=100, K=1, R=2, base=2, verbose=False): r""" INPUT: - ``nb_equipes`` -- integer - ``max_points`` -- integer - ``K`` -- the value at ``p = nb_equipes`` - ``R`` -- real (default: ``2``), curve parameter - ``base`` -- 2 - ``verbose`` - bool EXEMPLES:: sage: from slabbe.ranking_scale import discrete_curve sage: A = discrete_curve(64, 100,verbose=True) First difference sequence is [1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 3, 3, 2, 3, 3, 3, 4, 4, 4] :: sage: A = discrete_curve(64, 100) sage: B = discrete_curve(32, 50) sage: C = discrete_curve(16, 25) :: sage: A [100, 96, 92, 88, 85, 82, 79, 77, 74, 71, 69, 67, 64, 62, 60, 58, 56, 54, 52, 50, 49, 47, 45, 44, 42, 41, 39, 38, 36, 35, 33, 32, 31, 29, 28, 27, 26, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 8, 7, 6, 5, 5, 4, 3, 2, 2, 1] sage: B [50, 46, 43, 40, 37, 35, 33, 31, 29, 27, 25, 23, 21, 20, 18, 17, 16, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1] sage: C [25, 22, 19, 17, 15, 13, 11, 9, 8, 7, 6, 5, 4, 3, 2, 1] :: sage: A = discrete_curve(64+2, 100) #Movember, Bye Bye, Cdf, MA sage: B = discrete_curve(32+2, 70) # la flotte sage: C = discrete_curve(16+2, 40) # october fest, funenuf, la viree """ from sage.misc.functional import round from sage.rings.integer_ring import ZZ from sage.combinat.words.word import Word fn_normalise = curve(nb_equipes, max_points, K=K, R=R, base=base) L = [ZZ(round(fn_normalise(p=i))) for i in range(1, nb_equipes + 1)] if verbose: print "First difference sequence is" print list(Word(L).reversal().finite_differences()) return L
def lie_polynomial(self, w): """ Return the Lie polynomial associated to the Lyndon word ``w``. If ``w`` is not Lyndon, then return the product of Lie polynomials of the Lyndon factorization of ``w``. INPUT: - ``w`` -- a word or an element of the free monoid EXAMPLES:: sage: F = FreeAlgebra(QQ, 3, 'x,y,z') sage: M.<x,y,z> = FreeMonoid(3) sage: F.lie_polynomial(x*y) x*y - y*x sage: F.lie_polynomial(y*x) y*x sage: F.lie_polynomial(x^2*y*x) x^2*y*x - x*y*x^2 sage: F.lie_polynomial(y*z*x*z*x*z) y*z*x*z*x*z - y*z*x*z^2*x - y*z^2*x^2*z + y*z^2*x*z*x - z*y*x*z*x*z + z*y*x*z^2*x + z*y*z*x^2*z - z*y*z*x*z*x TESTS: We test some corner cases and alternative inputs:: sage: F.lie_polynomial(Word('xy')) x*y - y*x sage: F.lie_polynomial('xy') x*y - y*x sage: F.lie_polynomial(M.one()) 1 sage: F.lie_polynomial(Word([])) 1 sage: F.lie_polynomial('') 1 """ if not w: return self.one() M = self._basis_keys if len(w) == 1: return self(M(w)) ret = self.one() # We have to be careful about order here. # Since the Lyndon factors appear from left to right # we must multiply from left to right as well. for factor in Word(w).lyndon_factorization(): if len(factor) == 1: ret = ret * self(M(factor)) continue x,y = factor.standard_factorization() x = M(x) y = M(y) ret = ret * (self(x * y) - self(y * x)) return ret
def __contains__(self, x): """ EXAMPLES:: sage: from sage.combinat.words.shuffle_product import ShuffleProduct_w1w2 sage: W = Words("abcd") sage: w = W("ab") sage: u = W("cd") sage: S = ShuffleProduct_w1w2(w,u) sage: w*u in S True sage: all(w.is_subword_of(x) for x in S) True sage: w in S False We check that :trac:`14121` is solved:: sage: w = W('ab') sage: x = W('ac') sage: x*w in w.shuffle(x) True """ from sage.combinat.words.word import Word if not isinstance(x, Word_class): return False if x.length() != self._w1.length() + self._w2.length(): return False w1 = list(self._w1) w2 = list(self._w2) wx = list(x) for _ in range(len(wx)): try: letter = wx.pop(0) except IndexError: return False if w1 and w2 and letter == w1[0] == w2[0]: return (Word(wx) in self._w1[1:].shuffle(self._w2) or Word(wx) in self._w1.shuffle(self._w2[1:])) if w1 and letter == w1[0]: w1.pop(0) elif w2 and letter == w2[0]: w2.pop(0) else: return False return not wx
def spanning_tree(self, verbose=False): """ Return a spanning tree of ``self``. The origin vertex of the spanning tree is the initial vertex of the edge labeled by ``self._alphabet[0]``. OUPUT: a dictionnary that maps each vertex to an edge-path from the origin vertex. .. SEEALSO:: :meth:`maximal_tree()` that returns a list of edges of a spanning tree. .. WARNING:: ``self`` must be connected. EXAMPLES:: sage: from train_track.inverse_graph import GraphWithInverses sage: G = GraphWithInverses([[0,0,'a'],[0,1,'b'],[1,0,'c']]) sage: G.spanning_tree() {0: word: , 1: word: b} """ A = self._alphabet tree = {self.initial_vertex(A[0]): Word()} done = False while not done: done = True for a in A.positive_letters(): vi = self.initial_vertex(a) vt = self.terminal_vertex(a) if vi in tree and vt not in tree: tree[vt] = self.reduce_path(tree[vi] * Word([a])) done = False elif vt in tree and vi not in tree: tree[vi] = self.reduce_path(tree[vt] * Word([A.inverse_letter(a)])) done = False return tree
def contract_forest(self, forest): """ Contract the forest. Each tree of the forest is contracted to the initial vertex of its first edge. INPUT: ``forest`` is a list of disjoint subtrees each given as lists of edges. OUTPUT: A dictionnary that maps an old edge to its image in the new graph. """ A = self._alphabet edge_map = dict((e, Word([e])) for e in A) vertex_map = {} for tree in forest: first = True for e in tree: if first: vtree = self.initial_vertex(e) first = False vertex_map[self.initial_vertex(e)] = vtree vertex_map[self.terminal_vertex(e)] = vtree edge_map[e] = Word([]) edge_map[A.inverse_letter(e)] = Word([]) self.remove_edge(e) for e in A: v = self.initial_vertex(e) if v in vertex_map and v != vertex_map[v]: self.set_initial_vertex(e, vertex_map[self.initial_vertex(e)]) for v in vertex_map: if v != vertex_map[v]: self.remove_vertex(v) return edge_map
def blow_up_vertices(self, germ_components): """ Blow-up ``self`` according to classes of germs given in ``germ_components``. INPUT: - ``germ_components`` -- a list of classes of germs outgoing from a vertex OUTPUT: A dictionnary that maps an old edge to the path in the new graph. EXAMPLES:: sage: from train_track import * sage: from train_track.inverse_graph import GraphWithInverses sage: G = GraphWithInverses.rose_graph(AlphabetWithInverses(2)) sage: G.blow_up_vertices([['a','A'],['b'],['B']]) {'A': word: cAC, 'B': word: eBD, 'a': word: caC, 'b': word: dbE} sage: print(G) a: 1->1, b: 2->3, c: 0->1, d: 0->2, e: 0->3 """ A = self.alphabet() result_map = dict((a, Word([a])) for a in A) new_vertices = self.new_vertices(len(germ_components)) new_edges = A.add_new_letters(len(germ_components)) for i, c in enumerate(germ_components): v0 = self.initial_vertex(c[0]) vc = new_vertices[i] ec = new_edges[i] self.add_edge(v0, vc, ec) for a in c: self.set_initial_vertex(a, vc) result_map[a] = Word([ec[0]]) * result_map[a] aa = A.inverse_letter(a) result_map[aa] = result_map[aa] * Word([ec[1]]) return result_map
def subdivide_domain(self, e): """ Subdivide an edge in the domain graph. The edge ``e`` is subdivided into ``l`` edges where ``l`` is the length of the image of ``e`` by ``self``. Update the edge map of ``self``. Intended to be used by Stalling's folding algorithm to get an immersion. SEE ALSO:: GraphWithInverses.subdivide_edge() """ G = self.domain() A = G.alphabet() result_map = dict((a, Word([a])) for a in A) w = self.image(e) new_edges = A.add_new_letters(len(w) - 1) new_vertices = G.new_vertices(len(w) - 1) d = {(a, self.image(a)) for a in A.positive_letters()} for i, a in enumerate(new_edges): v = new_vertices[i] if i == 0: vi = G.initial_vertex(e) vt = G.terminal_vertex(e) f = new_edges[i][0] ee = A.inverse_letter(e) ff = new_edges[i][1] G.set_terminal_vertex(e, v) G.add_edge(v, vt, [f, ff]) result_map[e] = result_map[e] * Word([f]) result_map[ee] = Word([ff]) * result_map[ee] d[a[0]] = w[i + 1] else: vi = self._domain.initial_vertex(new_edges[i - 1][0]) vt = self._domain.terminal_vertex(new_edges[i - 1][0]) f = new_edges[i][0] #ee=A.inverse_letter(e) #already done ff = new_edges[i][1] self._domain.set_terminal_vertex(new_edges[i - 1][0], v) self._domain.add_edge(v, vt, [f, ff]) result_map[e] = result_map[e] * Word([f]) result_map[ee] = Word([ff]) * result_map[ee] d[a[0]] = w[i + 1] # updating self.edge_map after subdivision if A.is_positive_letter(e): d[e] = Word([self.image(e)[0]]) else: d[ee] = Word([self.image(ee)[-1]]) self.set_edge_map(d) return result_map
def skeleton_cycles(self): r""" EXAMPLES:: sage: from slabbe import DoubleRootedTree sage: edges = [(0,5),(1,2),(2,6),(3,2),(4,1),(5,7),(7,1),(8,2),(9,4)] sage: D = DoubleRootedTree(edges, 6, 0) sage: D.skeleton() [0, 5, 7, 1, 2, 6] sage: D.skeleton_cycles() [(0,), (1, 5), (2, 7, 6)] """ skeleton = self.skeleton() sorted_skeleton = sorted(skeleton) w = Word(skeleton) cycles_standard = w.standard_permutation().to_cycles() cycles = [] for cycle_standard in cycles_standard: t = tuple(sorted_skeleton[c-1] for c in cycle_standard) cycles.append(t) return cycles
def is_littlewood_richardson(t, heights): """ Return whether semistandard tableau ``t`` is Littleword-Richardson with respect to ``heights``. A tableau is Littlewood-Richardson with respect to ``heights`` given by `(h_1, h_2, \ldots)` if each subtableau with respect to the alphabets `\{1, 2, \ldots, h_1\}`, `\{h_1+1, \ldots, h_1+h_2\}`, etc. is Yamanouchi. EXAMPLES:: sage: from sage.combinat.lr_tableau import is_littlewood_richardson sage: t = Tableau([[1,1,2,3,4],[2,3,3],[3]]) sage: is_littlewood_richardson(t,[2,2]) False sage: t = Tableau([[1,1,3],[2,3],[4,4]]) sage: is_littlewood_richardson(t,[2,2]) True sage: t = Tableau([[7],[8]]) sage: is_littlewood_richardson(t,[2,3,3]) False sage: is_littlewood_richardson([[2],[3]],[3,3]) False """ from sage.combinat.words.word import Word partial = [ sum(heights[i] for i in range(j)) for j in range(len(heights) + 1) ] try: w = t.to_word() except AttributeError: # Not an instance of Tableau w = sum(reversed(t), []) for i in range(len(heights)): subword = Word([j for j in w if partial[i] + 1 <= j <= partial[i + 1]], alphabet=list(range(partial[i] + 1, partial[i + 1] + 1))) if not subword.is_yamanouchi(): return False return True
def reading_word(self): """ Return the reading word of ``self``, obtained by reading the boxes entries of self from right to left, starting in the upper right. EXAMPLES:: sage: a = AugmentedLatticeDiagramFilling([[1,6],[2],[3,4,2],[],[],[5,5]]) sage: a.reading_word() word: 25465321 """ w = [self[i, j] for i, j in self.reading_order() if j > 0] return Word(w)
def counit(self, S): """ Return the counit of ``S``. EXAMPLES:: sage: F = ShuffleAlgebra(QQ,'ab') sage: S = F.an_element(); S B[word: ] + 2*B[word: a] + 3*B[word: b] + B[word: bab] sage: F.counit(S) 1 """ return S.monomial_coefficients().get(Word(), 0)
def is_littlewood_richardson(t, heights): r""" Return whether semistandard tableau ``t`` is Littleword-Richardson with respect to ``heights``. A tableau is Littlewood-Richardson with respect to ``heights`` given by `(h_1, h_2, \ldots)` if each subtableau with respect to the alphabets `\{1, 2, \ldots, h_1\}`, `\{h_1+1, \ldots, h_1+h_2\}`, etc. is Yamanouchi. EXAMPLES:: sage: from sage.combinat.lr_tableau import is_littlewood_richardson sage: t = Tableau([[1,1,2,3,4],[2,3,3],[3]]) sage: is_littlewood_richardson(t,[2,2]) False sage: t = Tableau([[1,1,3],[2,3],[4,4]]) sage: is_littlewood_richardson(t,[2,2]) True sage: t = Tableau([[7],[8]]) sage: is_littlewood_richardson(t,[2,3,3]) False sage: is_littlewood_richardson([[2],[3]],[3,3]) False """ from sage.combinat.words.word import Word partial = [sum(heights[i] for i in range(j)) for j in range(len(heights)+1)] try: w = t.to_word() except AttributeError: # Not an instance of Tableau w = sum(reversed(t), []) for i in range(len(heights)): subword = Word([j for j in w if partial[i]+1 <= j <= partial[i+1]], alphabet=list(range(partial[i]+1,partial[i+1]+1))) if not subword.is_yamanouchi(): return False return True