From 20ae22e10509eafde5a0f59f6bbd70110773a6ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9bastien=20Labb=C3=A9?= Date: Tue, 5 Jan 2021 16:33:28 +0100 Subject: [PATCH] linking morphic.py file to the sage words library --- src/sage/combinat/words/morphic.py | 98 ++++++++++++++++++++++++++++- src/sage/combinat/words/morphism.py | 76 ++-------------------- src/sage/combinat/words/word.py | 86 +++++++++++++++++++++++++ 3 files changed, 186 insertions(+), 74 deletions(-) diff --git a/src/sage/combinat/words/morphic.py b/src/sage/combinat/words/morphic.py index 5147f0e11cd..c04313efa0e 100644 --- a/src/sage/combinat/words/morphic.py +++ b/src/sage/combinat/words/morphic.py @@ -1,8 +1,25 @@ +# -*- coding: utf-8 -*- +r""" +Morphic words + +This modules implements morphic words (letter-to-letter coding of fixed +point of a morphism). + +AUTHORS: + +- Jana Lepsova (January 2021): initial version + +EXAMPLES: + +Creation of a morphism:: + + sage: n = WordMorphism(...) + +""" + from sage.combinat.words.word_datatypes import WordDatatype from sage.rings.all import Infinity from sage.modules.free_module_element import vector -import itertools - class WordDatatype_morphic(WordDatatype): r""" @@ -60,7 +77,6 @@ def __init__(self, parent, morphism, letter, coding=None): else: self._coding = coding - def representation(self, n): """ EXAMPLES:: @@ -100,10 +116,86 @@ def representation(self, n): letter_k = a k -= 1 return path + def __getitem__(self, key): + """ + EXAMPLES:: + + sage: print('add doc + examples here') + """ letter = self._letter for a in self.representation(key): letter = (self._morphism(letter))[a] if key == 0: return self._coding[letter] return self._coding[letter] + + def __iter__(self): + r""" + Returns an iterator of the letters of the fixed point of ``self`` + starting with ``letter``. + + If w is the iterated word, then this iterator: outputs the elements + of morphism[ w[i] ], appends morphism[ w[i+1] ] to w, increments i. + + INPUT: + + - ``self`` - an endomorphism, must be prolongable on + letter + + - ``letter`` - a letter in the domain of ``self`` + + OUTPUT: + + - iterator of the fixed point + + EXAMPLES:: + + sage: m = WordMorphism('a->abc,b->,c->') + sage: list(m._fixed_point_iterator('a')) + ['a', 'b', 'c'] + sage: print('update the examples here') + + The morphism must be prolongable on the letter or the iterator will + be empty:: + + sage: list(m._fixed_point_iterator('b')) + [] + + The morphism must be an endomorphism:: + + sage: m = WordMorphism('a->ac,b->aac') + sage: list(m._fixed_point_iterator('a')) + Traceback (most recent call last): + ... + KeyError: 'c' + + We check that :trac:`8595` is fixed:: + + sage: s = WordMorphism({('a', 1):[('a', 1), ('a', 2)], ('a', 2):[('a', 1)]}) + sage: it = s._fixed_point_iterator(('a',1)) + sage: next(it) + ('a', 1) + + This shows that ticket :trac:`13668` has been resolved:: + + sage: s = WordMorphism({1:[1,2],2:[2,3],3:[4],4:[5],5:[6],6:[7],7:[8],8:[9],9:[10],10:[1]}) + sage: (s^7).fixed_points() + [word: 1223234234523456234567234567823456789234..., + word: 2,3,4,5,6,7,8,9,10,1,1,2,1,2,2,3,1,2,2,3,2,3,4,1,2,2,3,2,3,4,2,3,4,5,1,2,2,3,2,3,...] + sage: (s^7).reversal().fixed_points() + [] + """ + from itertools import chain + w = iter(self._morphism.image(self._letter)) + while True: + try: + for a in self._morphism.image(next(w)): + yield self._coding[a] + else: + next_w = next(w) + w = chain([next_w], w, self._morphism.image(next_w)) + except StopIteration: + return + + diff --git a/src/sage/combinat/words/morphism.py b/src/sage/combinat/words/morphism.py index cd46090a18a..9f969723bec 100644 --- a/src/sage/combinat/words/morphism.py +++ b/src/sage/combinat/words/morphism.py @@ -89,8 +89,6 @@ # https://www.gnu.org/licenses/ # **************************************************************************** -from itertools import chain - from sage.misc.callable_dict import CallableDict from sage.structure.sage_object import SageObject from sage.misc.cachefunc import cached_method @@ -1729,73 +1727,6 @@ def is_uniform(self, k=None): else: return all(w.length() == k for w in self.images()) - def _fixed_point_iterator(self, letter): - r""" - Returns an iterator of the letters of the fixed point of ``self`` - starting with ``letter``. - - If w is the iterated word, then this iterator: outputs the elements - of morphism[ w[i] ], appends morphism[ w[i+1] ] to w, increments i. - - INPUT: - - - ``self`` - an endomorphism, must be prolongable on - letter - - - ``letter`` - a letter in the domain of ``self`` - - OUTPUT: - - - iterator of the fixed point - - EXAMPLES:: - - sage: m = WordMorphism('a->abc,b->,c->') - sage: list(m._fixed_point_iterator('a')) - ['a', 'b', 'c'] - - The morphism must be prolongable on the letter or the iterator will - be empty:: - - sage: list(m._fixed_point_iterator('b')) - [] - - The morphism must be an endomorphism:: - - sage: m = WordMorphism('a->ac,b->aac') - sage: list(m._fixed_point_iterator('a')) - Traceback (most recent call last): - ... - KeyError: 'c' - - We check that :trac:`8595` is fixed:: - - sage: s = WordMorphism({('a', 1):[('a', 1), ('a', 2)], ('a', 2):[('a', 1)]}) - sage: it = s._fixed_point_iterator(('a',1)) - sage: next(it) - ('a', 1) - - This shows that ticket :trac:`13668` has been resolved:: - - sage: s = WordMorphism({1:[1,2],2:[2,3],3:[4],4:[5],5:[6],6:[7],7:[8],8:[9],9:[10],10:[1]}) - sage: (s^7).fixed_points() - [word: 1223234234523456234567234567823456789234..., - word: 2,3,4,5,6,7,8,9,10,1,1,2,1,2,2,3,1,2,2,3,2,3,4,1,2,2,3,2,3,4,2,3,4,5,1,2,2,3,2,3,...] - sage: (s^7).reversal().fixed_points() - [] - """ - w = iter(self.image(letter)) - while True: - try: - for a in self.image(next(w)): - yield a - else: - next_w = next(w) - w = chain([next_w], w, self.image(next_w)) - except StopIteration: - return - - def fixed_point(self, letter): r""" Returns the fixed point of ``self`` beginning by the given ``letter``. @@ -1887,8 +1818,11 @@ def fixed_point(self, letter): parent = self.codomain() if self.is_growing(letter): - parent = parent.shift() - return parent(self._fixed_point_iterator(letter)) + from sage.combinat.words.word import InfiniteWord_morphic + return InfiniteWord_morphic(parent.shift(), self, letter) + else: + from sage.combinat.words.word import FiniteWord_morphic + return FiniteWord_morphic(parent, self, letter) def fixed_points(self): r""" diff --git a/src/sage/combinat/words/word.py b/src/sage/combinat/words/word.py index 2ab59314376..b7854a8976d 100644 --- a/src/sage/combinat/words/word.py +++ b/src/sage/combinat/words/word.py @@ -34,6 +34,7 @@ WordDatatype_iter, WordDatatype_callable_with_caching, WordDatatype_callable) +from .morphic import WordDatatype_morphic from sage.monoids.free_monoid_element import FreeMonoidElement # TODO. Word needs to be replaced by Word. Consider renaming @@ -687,3 +688,88 @@ class Word_iter(WordDatatype_iter, Word_class): """ pass +##### Morphic Words ##### +class Word_morphic(WordDatatype_morphic, Word_class): + r""" + Morphic word of unknown length (finite or infinite). + + For such word `w`, type ``w.`` and hit TAB key to see the list of + functions defined on `w`. + + Words behave like a Python list : they can be sliced using + square braquets to define for example a prefix or a factor. + + EXAMPLES:: + + sage: w = 34 + sage: w + word: 1149114911491149114911491149114911491149... + + TESTS: + + Pickle is not supported for word of unknown length defined by an iterator:: + + sage: dumps(w) + + sage: + Traceback (most recent call last): + ... + TypeError: can...t...pickle...generator...object... + """ + pass + +class FiniteWord_morphic(WordDatatype_morphic, FiniteWord_class): + r""" + Finite morphic word. + + For such word `w`, type ``w.`` and hit TAB key to see the list of + functions defined on `w`. + + EXAMPLES:: + + sage: print('update') + + TESTS:: + + sage: print('update') + sage: w = 22222 + sage: type(w) + + sage: z = loads(dumps(w)) + sage: w == z + True + sage: type(z) + + """ + pass + +class InfiniteWord_morphic(WordDatatype_morphic, InfiniteWord_class): + r""" + Morphic word of infinite length. + + For such word `w`, type ``w.`` and hit TAB key to see the list of + functions defined on `w`. + + Infinite words behave like a Python list : they can be sliced using + square braquets to define for example a prefix or a factor. + + EXAMPLES:: + + sage: m = WordMorphism('a->ab,b->a') + sage: w = m.fixed_point('a') + sage: w + + TESTS: + + Pickle is not supported for infinite word defined by an iterator:: + + sage: dumps(w) + + + sage: dumps(w) + Traceback (most recent call last): + ... + TypeError: can...t...pickle...generator...object... + """ + pass +