Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*- r""" Common words
AUTHORS:
- Franco Saliola (2008-12-17): merged into sage - Sebastien Labbe (2008-12-17): merged into sage - Arnaud Bergeron (2008-12-17): merged into sage - Amy Glen (2008-12-17): merged into sage - Sebastien Labbe (2009-12-19): Added S-adic words (:trac:`7543`)
USE:
To see a list of all word constructors, type ``words.`` and then press the tab key. The documentation for each constructor includes information about each word, which provides a useful reference.
REFERENCES:
.. [AC03] \B. Adamczewski, J. Cassaigne, On the transcendence of real numbers with a regular expansion, J. Number Theory 103 (2003) 27--37.
.. [BmBGL07] \A. Blondin-Masse, S. Brlek, A. Glen, and S. Labbe. On the critical exponent of generalized Thue-Morse words. *Discrete Math. Theor. Comput. Sci.* 9 (1):293--304, 2007.
.. [BmBGL09] \A. Blondin-Masse, S. Brlek, A. Garon, and S. Labbe. Christoffel and Fibonacci Tiles, DGCI 2009, Montreal, to appear in LNCS.
.. [Loth02] \M. Lothaire, Algebraic Combinatorics On Words, vol. 90 of Encyclopedia of Mathematics and its Applications, Cambridge University Press, U.K., 2002.
.. [Fogg] Pytheas Fogg, https://www.lirmm.fr/arith/wiki/PytheasFogg/S-adiques.
EXAMPLES::
sage: t = words.ThueMorseWord(); t word: 0110100110010110100101100110100110010110... """ #***************************************************************************** # Copyright (C) 2008 Franco Saliola <saliola@gmail.com>, # Sebastien Labbe <slabqc@gmail.com>, # Arnaud Bergeron <abergeron@gmail.com>, # Amy Glen <amy.glen@gmail.com> # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ #***************************************************************************** from __future__ import print_function
from six.moves import range
from itertools import cycle, count from random import randint from sage.misc.cachefunc import cached_method from sage.rings.all import ZZ, RR from sage.rings.infinity import Infinity from sage.combinat.words.abstract_word import Word_class from sage.combinat.words.word import FiniteWord_list from sage.combinat.words.finite_word import FiniteWord_class, Factorization from sage.combinat.words.words import FiniteWords, InfiniteWords from sage.combinat.words.morphism import WordMorphism from sage.arith.all import gcd from sage.misc.decorators import rename_keyword
def _build_tab(sym, tab, W): r""" Internal function building a coding table for the ``phi_inv_tab`` function.
TESTS::
sage: from sage.combinat.words.word_generators import _build_tab sage: _build_tab(1, [], Words([1, 2])) [1] sage: _build_tab(1, [1], Words([1, 2])) [1, 2] sage: _build_tab(2, [1], Words([1, 2])) [2, 2] sage: _build_tab(2, [1, 2], Words([1, 2])) [2, 2, 1] sage: _build_tab(1, [2, 2], Words([1, 2])) [1, 1, 2] """
class LowerChristoffelWord(FiniteWord_list): r""" Returns the lower Christoffel word of slope `p/q`, where `p` and `q` are relatively prime non-negative integers, over the given two-letter alphabet.
The *Christoffel word of slope `p/q`* is obtained from the Cayley graph of `\ZZ/(p+q)\ZZ` with generator `q` as follows. If `u \rightarrow v` is an edge in the Cayley graph, then `v = u + p \mod{p+q}`. Label the edge `u \rightarrow v` by ``alphabet[1]`` if `u < v` and ``alphabet[0]`` otherwise. The Christoffel word is the word obtained by reading the edge labels along the cycle beginning from 0.
EXAMPLES::
sage: words.LowerChristoffelWord(4,7) word: 00100100101
::
sage: words.LowerChristoffelWord(4,7,alphabet='ab') word: aabaabaabab
TESTS::
sage: words.LowerChristoffelWord(1,0) word: 1 sage: words.LowerChristoffelWord(0,1,'xy') word: x sage: words.LowerChristoffelWord(1,1) word: 01 """
def __init__(self, p, q, alphabet=(0,1), algorithm='cf'): r""" INPUT:
- ``p`` - integer coprime with ``q``. - ``q`` - integer coprime with ``p``. - ``alphabet`` - sequence of two elements (optional, default: (0, 1)). - ``algorithm`` - construction method (optional, default: 'cf'). It can be one of the following:
- ``'linear'`` - linear algorithm in the length of the word. - ``'cf'`` - fast method using continued fraction.
TESTS::
sage: words.ChristoffelWord(9, 4, algorithm='linear') word: 0110110110111 sage: words.ChristoffelWord(9, 4, algorithm='cf') word: 0110110110111 sage: words.ChristoffelWord(4, 9, algorithm='linear') word: 0001001001001 sage: words.ChristoffelWord(4, 9, algorithm='cf') word: 0001001001001
::
sage: words.LowerChristoffelWord(4,8) Traceback (most recent call last): ... ValueError: 4 and 8 are not relatively prime sage: words.LowerChristoffelWord(17, 39, 'xyz') Traceback (most recent call last): ... ValueError: alphabet must contain exactly two distinct elements sage: w = words.LowerChristoffelWord(4,7) sage: w2 = loads(dumps(w)) sage: w == w2 True sage: type(w2) <class 'sage.combinat.words.word_generators.LowerChristoffelWord'> sage: _ = w2.standard_factorization() # hackish test for self.__p and self.__q """ # Compute gcd of p, q; raise TypeError if not 1. # Compute the Christoffel word w = [alphabet[0]] else: else: #do not consider the first zero if p < q else: else: else: raise ValueError('Unknown algorithm (=%s)'%algorithm)
def markoff_number(self): r""" Returns the Markoff number associated to the Christoffel word self.
The *Markoff number* of a Christoffel word `w` is `trace(M(w))/3`, where `M(w)` is the `2\times 2` matrix obtained by applying the morphism: 0 -> matrix(2,[2,1,1,1]) 1 -> matrix(2,[5,2,2,1])
EXAMPLES::
sage: w0 = words.LowerChristoffelWord(4,7) sage: w1, w2 = w0.standard_factorization() sage: (m0,m1,m2) = (w.markoff_number() for w in (w0,w1,w2)) sage: (m0,m1,m2) (294685, 13, 7561) sage: m0**2 + m1**2 + m2**2 == 3*m0*m1*m2 True """
def standard_factorization(self): r""" Returns the standard factorization of the Christoffel word ``self``.
The *standard factorization* of a Christoffel word `w` is the unique factorization of `w` into two Christoffel words.
EXAMPLES::
sage: w = words.LowerChristoffelWord(5,9) sage: w word: 00100100100101 sage: w1, w2 = w.standard_factorization() sage: w1 word: 001 sage: w2 word: 00100100101
::
sage: w = words.LowerChristoffelWord(51,37) sage: w1, w2 = w.standard_factorization() sage: w1 word: 0101011010101101011 sage: w2 word: 0101011010101101011010101101010110101101... sage: w1 * w2 == w True """ LowerChristoffelWord(w2.count(1),w2.count(0))])
def __reduce__(self): r""" EXAMPLES::
sage: from sage.combinat.words.word_generators import LowerChristoffelWord sage: w = LowerChristoffelWord(5,7) sage: w.__reduce__() (<class 'sage.combinat.words.word_generators.LowerChristoffelWord'>, (5, 7, {0, 1})) """
class WordGenerator(object): r""" Constructor of several famous words.
EXAMPLES::
sage: words.ThueMorseWord() word: 0110100110010110100101100110100110010110...
::
sage: words.FibonacciWord() word: 0100101001001010010100100101001001010010...
::
sage: words.ChristoffelWord(5, 8) word: 0010010100101
::
sage: words.RandomWord(10, 4) # not tested random word: 1311131221
::
sage: words.CodingOfRotationWord(alpha=0.618, beta=0.618) word: 1010110101101101011010110110101101101011...
::
sage: tm = WordMorphism('a->ab,b->ba') sage: fib = WordMorphism('a->ab,b->a') sage: tmword = words.ThueMorseWord([0, 1]) sage: from itertools import repeat sage: words.s_adic(tmword, repeat('a'), {0:tm, 1:fib}) word: abbaababbaabbaabbaababbaababbaabbaababba...
.. NOTE::
To see a list of all word constructors, type ``words.`` and then hit the TAB key. The documentation for each constructor includes information about each word, which provides a useful reference.
TESTS::
sage: from sage.combinat.words.word_generators import WordGenerator sage: words2 = WordGenerator() sage: type(loads(dumps(words2))) <class 'sage.combinat.words.word_generators.WordGenerator'> """ def ThueMorseWord(self, alphabet=(0, 1), base=2): r""" Returns the (Generalized) Thue-Morse word over the given alphabet.
There are several ways to define the Thue-Morse word `t`. We use the following definition: `t[n]` is the sum modulo `m` of the digits in the given base expansion of `n`.
See [BmBGL07]_, [Brlek89]_, and [MH38]_.
INPUT:
- ``alphabet`` - (default: (0, 1) ) any container that is suitable to build an instance of OrderedAlphabet (list, tuple, str, ...)
- ``base`` - an integer (default : 2) greater or equal to 2
EXAMPLES:
Thue-Morse word::
sage: t = words.ThueMorseWord(); t word: 0110100110010110100101100110100110010110...
Thue-Morse word on other alphabets::
sage: t = words.ThueMorseWord('ab'); t word: abbabaabbaababbabaababbaabbabaabbaababba...
::
sage: t = words.ThueMorseWord(['L1', 'L2']) sage: t[:8] word: L1,L2,L2,L1,L2,L1,L1,L2
Generalized Thue Morse word::
sage: words.ThueMorseWord(alphabet=(0,1,2), base=2) word: 0112122012202001122020012001011212202001... sage: t = words.ThueMorseWord(alphabet=(0,1,2), base=5); t word: 0120112012201200120112012120122012001201... sage: t[100:130].critical_exponent() 10/3
TESTS::
sage: words.ThueMorseWord(alphabet='ab', base=1) Traceback (most recent call last): ... ValueError: base (=1) and len(alphabet) (=2) must be at least 2
REFERENCES:
.. [Brlek89] Brlek, S. 1989. «Enumeration of the factors in the Thue-Morse word», *Discrete Appl. Math.*, vol. 24, p. 83--96.
.. [MH38] Morse, M., et G. A. Hedlund. 1938. «Symbolic dynamics», *American Journal of Mathematics*, vol. 60, p. 815--866. """
def _ThueMorseWord_nth_digit(self, n, alphabet=(0,1), base=2): r""" Returns the `n`-th letter of the (Generalized) Thue-Morse word.
The `n`-th digit of the Thue-Morse word can be defined as the number of bits in the 2-complement representation of the position modulo 2 which is what this function uses. The running time is `O(\log n)` where `n` is the position desired.
The `n`-th digit of the Generalized Thue Morse word can be defined as the sum of the digits of `n` written in the given base mod `m`, where `m` is the length of the given alphabet.
INPUT:
- ``n`` - integer, the position - ``alphabet`` - an alphabet (default : (0, 1) ) of size at least 2 - ``base`` - an integer (default : 2) greater or equal to 2
OUTPUT:
0 or 1 -- the digit at the position letter -- the letter of alphabet at the position
TESTS::
sage: from sage.combinat.words.word_generators import WordGenerator sage: WordGenerator()._ThueMorseWord_nth_digit(0) 0 sage: WordGenerator()._ThueMorseWord_nth_digit(3) 0 sage: WordGenerator()._ThueMorseWord_nth_digit(32) 1 sage: WordGenerator()._ThueMorseWord_nth_digit(6, 'abc', base = 7) 'a'
Negative input::
sage: words._ThueMorseWord_nth_digit(-7) Traceback (most recent call last): ... NotImplementedError: nth digit of Thue-Morse word is not implemented for negative value of n """ raise ValueError("base (=%s) and len(alphabet) (=%s) must be at least 2"%(base, m)) else:
def FibonacciWord(self, alphabet=(0, 1), construction_method="recursive"): r""" Returns the Fibonacci word on the given two-letter alphabet.
INPUT:
- ``alphabet`` -- any container of length two that is suitable to build an instance of OrderedAlphabet (list, tuple, str, ...)
- ``construction_method`` -- can be any of the following: "recursive", "fixed point", "function" (see below for definitions).
Recursive construction: the Fibonacci word is the limit of the following sequence of words: `S_0 = 0`, `S_1 = 01`, `S_n = S_{n-1} S_{n-2}` for `n \geq 2`.
Fixed point construction: the Fibonacci word is the fixed point of the morphism: `0 \mapsto 01` and `1 \mapsto 0`. Hence, it can be constructed by the following read-write process:
#. beginning at the first letter of `01`, #. if the next letter is `0`, append `01` to the word; #. if the next letter is `1`, append `1` to the word; #. move to the next letter of the word.
Function: Over the alphabet `\{1, 2\}`, the n-th letter of the Fibonacci word is `\lfloor (n+2) \varphi \rfloor - \lfloor (n+1) \varphi \rfloor` where `\varphi=(1+\sqrt{5})/2` is the golden ratio.
EXAMPLES::
sage: w = words.FibonacciWord(construction_method="recursive"); w word: 0100101001001010010100100101001001010010...
::
sage: v = words.FibonacciWord(construction_method="recursive", alphabet='ab'); v word: abaababaabaababaababaabaababaabaababaaba...
::
sage: u = words.FibonacciWord(construction_method="fixed point"); u word: 0100101001001010010100100101001001010010...
::
sage: words.FibonacciWord(construction_method="fixed point", alphabet=[4, 1]) word: 4144141441441414414144144141441441414414...
::
sage: words.FibonacciWord([0,1], 'function') word: 0100101001001010010100100101001001010010... sage: words.FibonacciWord('ab', 'function') word: abaababaabaababaababaabaababaabaababaaba...
TESTS::
sage: from math import floor, sqrt sage: golden_ratio = (1 + sqrt(5))/2.0 sage: a = golden_ratio / (1 + 2*golden_ratio) sage: wn = lambda n : int(floor(a*(n+2)) - floor(a*(n+1))) sage: f = Words([0,1])(wn); f word: 0100101001001010010100100101001001010010... sage: f[:10000] == w[:10000] True sage: f[:10000] == u[:10000] #long time True sage: words.FibonacciWord("abc") Traceback (most recent call last): ... TypeError: alphabet does not contain two distinct elements """
datatype='iter')
else: raise NotImplementedError
def _FibonacciWord_RecursiveConstructionIterator(self,alphabet=(0,1)): r""" Iterates over the symbols of the Fibonacci word, as defined by the following recursive construction: the Fibonacci word is the limit of the sequence `S_0 = 0`, `S_1 = 01`, `S_n = S_{n-1} S_{n-2}` for `n \geq 2`.
TESTS::
sage: from sage.combinat.words.word_generators import WordGenerator sage: from itertools import islice sage: it = WordGenerator()._FibonacciWord_RecursiveConstructionIterator() sage: list(islice(it,13)) [0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1] """ else:
def FixedPointOfMorphism(self, morphism, first_letter): r""" Returns the fixed point of the morphism beginning with ``first_letter``.
A *fixed point* of a morphism `\varphi` is a word `w` such that `\varphi(w) = w`.
INPUT:
- ``morphism`` -- endomorphism prolongable on ``first_letter``. It must be something that WordMorphism's constructor understands (dict, str, ...).
- ``first_letter`` -- the first letter of the fixed point
OUTPUT:
The fixed point of the morphism beginning with ``first_letter``
EXAMPLES::
sage: mu = {0:[0,1], 1:[1,0]} sage: tm = words.FixedPointOfMorphism(mu,0); tm word: 0110100110010110100101100110100110010110... sage: TM = words.ThueMorseWord() sage: tm[:1000] == TM[:1000] True
::
sage: mu = {0:[0,1], 1:[0]} sage: f = words.FixedPointOfMorphism(mu,0); f word: 0100101001001010010100100101001001010010... sage: F = words.FibonacciWord(); F word: 0100101001001010010100100101001001010010... sage: f[:1000] == F[:1000] True
::
sage: fp = words.FixedPointOfMorphism('a->abc,b->,c->','a'); fp word: abc """
def CodingOfRotationWord(self, alpha, beta, x=0, alphabet=(0,1)): r""" Returns the infinite word obtained from the coding of rotation of parameters `(\alpha,\beta, x)` over the given two-letter alphabet.
The *coding of rotation* corresponding to the parameters `(\alpha,\beta, x)` is the symbolic sequence `u = (u_n)_{n\geq 0}` defined over the binary alphabet `\{0, 1\}` by `u_n = 1` if `x+n\alpha\in[0, \beta[` and `u_n = 0` otherwise. See [AC03]_.
EXAMPLES::
sage: alpha = 0.45 sage: beta = 0.48 sage: words.CodingOfRotationWord(0.45, 0.48) word: 1101010101001010101011010101010010101010...
::
sage: words.CodingOfRotationWord(0.45, 0.48, alphabet='xy') word: yyxyxyxyxyxxyxyxyxyxyyxyxyxyxyxxyxyxyxyx...
TESTS::
sage: words.CodingOfRotationWord(0.51,0.43,alphabet=[1,0,2]) Traceback (most recent call last): ... TypeError: alphabet does not contain two distinct elements """
def _CodingOfRotationWord_function(self, n, alpha, beta, x=0, alphabet=(0,1)): r""" Internal function that returns the symbol in position `n` of the coding of rotation word corresponding to the parameters `\alpha`, `\beta`, and `x`.
TESTS::
sage: alpha, beta = 0.45, 0.48 sage: words._CodingOfRotationWord_function(3, alpha, beta) 1 sage: words._CodingOfRotationWord_function(10, alpha, beta) 0 sage: words._CodingOfRotationWord_function(17, alpha, beta) 0 """ fracH += 1 else:
@rename_keyword(cf='slope') def CharacteristicSturmianWord(self, slope, alphabet=(0, 1), bits=None): r""" Returns the characteristic Sturmian word (also called standard Sturmian word) of given slope.
Over a binary alphabet `\{a,b\}`, the characteristic Sturmian word `c_\alpha` of irrational slope `\alpha` is the infinite word satisfying `s_{\alpha,0} = ac_\alpha` and `s'_{\alpha,0} = bc_\alpha`, where `s_{\alpha,0}` and `s'_{\alpha,0}` are respectively the lower and upper mechanical words with slope `\alpha` and intercept `0`. Equivalently, for irrational `\alpha`, `c_\alpha = s_{\alpha,\alpha} = s'_{\alpha,\alpha}`.
Let `\alpha = [0, d_1 + 1, d_2, d_3, \ldots]` be the continued fraction expansion of `\alpha`. It has been shown that the characteristic Sturmian word of slope `\alpha` is also the limit of the sequence: `s_0 = b, s_1 = a, \ldots, s_{n+1} = s_n^{d_n} s_{n-1}` for `n > 0`.
See Section 2.1 of [Loth02]_ for more details.
INPUT:
- ``slope`` - the slope of the word. It can be one of the following :
- real number in `]0, 1[`
- iterable over the continued fraction expansion of a real number in `]0, 1[`
- ``alphabet`` - any container of length two that is suitable to build an instance of OrderedAlphabet (list, tuple, str, ...)
- ``bits`` - integer (optional and considered only if ``slope`` is a real number) the number of bits to consider when computing the continued fraction.
OUTPUT:
word
ALGORITHM:
Let `[0, d_1 + 1, d_2, d_3, \ldots]` be the continued fraction expansion of `\alpha`. Then, the characteristic Sturmian word of slope `\alpha` is the limit of the sequence: `s_0 = b`, `s_1 = a` and `s_{n+1} = s_n^{d_n} s_{n-1}` for `n > 0`.
EXAMPLES:
From real slope::
sage: words.CharacteristicSturmianWord(1/golden_ratio^2) word: 0100101001001010010100100101001001010010... sage: words.CharacteristicSturmianWord(4/5) word: 11110 sage: words.CharacteristicSturmianWord(5/14) word: 01001001001001 sage: words.CharacteristicSturmianWord(pi-3) word: 0000001000000100000010000001000000100000...
From an iterator of the continued fraction expansion of a real::
sage: def cf(): ....: yield 0 ....: yield 2 ....: while True: yield 1 sage: F = words.CharacteristicSturmianWord(cf()); F word: 0100101001001010010100100101001001010010... sage: Fib = words.FibonacciWord(); Fib word: 0100101001001010010100100101001001010010... sage: F[:10000] == Fib[:10000] True
The alphabet may be specified::
sage: words.CharacteristicSturmianWord(cf(), 'rs') word: rsrrsrsrrsrrsrsrrsrsrrsrrsrsrrsrrsrsrrsr...
The characteristic sturmian word of slope `(\sqrt{3}-1)/2`::
sage: words.CharacteristicSturmianWord((sqrt(3)-1)/2) word: 0100100101001001001010010010010100100101...
The same word defined from the continued fraction expansion of `(\sqrt{3}-1)/2`::
sage: from itertools import cycle, chain sage: it = chain([0], cycle([2, 1])) sage: words.CharacteristicSturmianWord(it) word: 0100100101001001001010010010010100100101...
The first terms of the standard sequence of the characteristic sturmian word of slope `(\sqrt{3}-1)/2`::
sage: words.CharacteristicSturmianWord([0,2]) word: 01 sage: words.CharacteristicSturmianWord([0,2,1]) word: 010 sage: words.CharacteristicSturmianWord([0,2,1,2]) word: 01001001 sage: words.CharacteristicSturmianWord([0,2,1,2,1]) word: 01001001010 sage: words.CharacteristicSturmianWord([0,2,1,2,1,2]) word: 010010010100100100101001001001 sage: words.CharacteristicSturmianWord([0,2,1,2,1,2,1]) word: 0100100101001001001010010010010100100101...
TESTS::
sage: words.CharacteristicSturmianWord([1,1,1],'xyz') Traceback (most recent call last): ... TypeError: alphabet does not contain two distinct elements
::
sage: words.CharacteristicSturmianWord(5/4) Traceback (most recent call last): ... ValueError: The argument slope (=5/4) must be in ]0,1[.
::
sage: words.CharacteristicSturmianWord(1/golden_ratio^2, bits=30) doctest:...: DeprecationWarning: the argument 'bits' is deprecated See http://trac.sagemath.org/14567 for details. word: 0100101001001010010100100101001001010010... sage: _.length() +Infinity
::
sage: a = words.LowerMechanicalWord(1/pi)[1:] sage: b = words.UpperMechanicalWord(1/pi)[1:] sage: c = words.CharacteristicSturmianWord(1/pi) sage: n = 500; a[:n] == b[:n] == c[:n] True
::
sage: alpha = random() sage: c = words.CharacteristicSturmianWord(alpha) sage: l = words.LowerMechanicalWord(alpha)[1:] sage: u = words.UpperMechanicalWord(alpha)[1:] sage: i = 10000; j = i + 500; c[i:j] == l[i:j] == u[i:j] True
::
sage: a, b = 207, 232 sage: u = words.ChristoffelWord(a, b) sage: v = words.CharacteristicSturmianWord(a/(a+b)) sage: v.length() 439 sage: u[1:-1] == v[:-2] True """
else: else: raise TypeError("slope (=%s) must be a real number"%slope + "or an iterable.") datatype='iter')
def _CharacteristicSturmianWord_LetterIterator(self, cf, alphabet=(0,1)): r""" Returns an iterator over the symbols of the characteristic Sturmian word of slope ``cf``.
INPUT:
- ``cf`` - iterator, the continued fraction expansion of a real number in `]0, 1[`. - ``alphabet`` - the alphabet (optional, default ``(0,1)``) of the output
OUTPUT:
iterator of letters
ALGORITHM:
Let `[0, d_1 + 1, d_2, d_3, \ldots]` be the continued fraction expansion of `\alpha`. Then, the characteristic Sturmian word of slope `\alpha` is the limit of the sequence: `s_0 = 1`, `s_1 = 0` and `s_{n+1} = s_n^{d_n} s_{n-1}` for `n > 0`.
EXAMPLES::
sage: continued_fraction(1/golden_ratio^2)[:8] [0; 2, 1, 1, 1, 1, 2] sage: cf = iter(_) sage: Word(words._CharacteristicSturmianWord_LetterIterator(cf)) word: 0100101001001010010100100101001010
::
sage: alpha = (sqrt(3)-1)/2 sage: continued_fraction(alpha)[:10] [0; 2, 1, 2, 1, 2, 1, 2, 1, 2] sage: cf = iter(_) sage: Word(words._CharacteristicSturmianWord_LetterIterator(cf)) word: 0100100101001001001010010010010100100101... """ raise ValueError("The first term of the continued fraction expansion must be zero.") raise ValueError("The second term of the continued fraction expansion must be larger or equal to 1.") else:
def KolakoskiWord(self, alphabet=(1,2)): r""" Returns the Kolakoski word over the given alphabet and starting with the first letter of the alphabet.
Let `A = \{a,b\}` be an alphabet, where `a` and `b` are two distinct positive integers. The Kolakoski word `K_{a,b}` over `A` and starting with `a` is the unique infinite word `w` such that `w = \Delta(w)`, where `\Delta(w)` is the word encoding the runs of `w` (see ``delta()`` method on words for more details).
Note that `K_{a,b} \neq K_{b,a}`. On the other hand, the words `K_{a,b}` and `K_{b,a}` are the unique two words over `A` that are fixed by `\Delta`.
INPUT:
- ``alphabet`` - (default: (1,2)) an iterable of two positive integers
OUTPUT:
infinite word
EXAMPLES:
The usual Kolakoski word::
sage: w = words.KolakoskiWord() sage: w word: 1221121221221121122121121221121121221221... sage: w.delta() word: 1221121221221121122121121221121121221221...
The other Kolakoski word on the same alphabet::
sage: w = words.KolakoskiWord(alphabet = (2,1)) sage: w word: 2211212212211211221211212211211212212211... sage: w.delta() word: 2211212212211211221211212211211212212211...
It is naturally generalized to any two integers alphabet::
sage: w = words.KolakoskiWord(alphabet = (2,5)) sage: w word: 2255222225555522552255225555522222555552... sage: w.delta() word: 2255222225555522552255225555522222555552...
TESTS::
sage: for i in range(1,10): ....: for j in range(1,10): ....: if i != j: ....: w = words.KolakoskiWord(alphabet=(i,j)) ....: assert w[:50] == w.delta()[:50]
::
sage: words.KolakoskiWord((0, 2)) Traceback (most recent call last): ... ValueError: The alphabet (=(0, 2)) must consist of two distinct positive integers
REFERENCES:
.. [Kolakoski66] William Kolakoski, proposal 5304, American Mathematical Monthly 72 (1965), 674; for a partial solution, see "Self Generating Runs," by Necdet Üçoluk, Amer. Math. Mon. 73 (1966), 681-2. """
def _KolakoskiWord_iterator(self, a=1, b=2): r""" Returns an iterator over the Kolakoski word over ``{a,b}`` and starting with ``a``.
Let `A = \{a,b\}` be an alphabet, where `a` and `b` are two distinct positive integers. The Kolakoski word `K_{a,b}` over `A` and starting with `a` is the unique infinite word `w` such that `w = \Delta(w)`, where `\Delta(w)` is the word encoding the runs of `w` (see ``delta()`` method on words for more details).
Note that `K_{a,b} \neq K_{b,a}`. On the other hand, the words `K_{a,b}` and `K_{b,a}` are the unique two words over `A` that are fixed by `\Delta`.
INPUT:
- ``a`` - positive integer (default: 1), the first letter occurring in the returned Kolakoski word. - ``b`` - positive integer (default: 2), the second and last letter occuring in the returned Kolakoski word.
OUTPUT:
iterator
EXAMPLES:
The first ten letters of `K_{3,5}`::
sage: iter = words._KolakoskiWord_iterator(3, 5) sage: Word(iter)[:10] word: 3335553335
See ``words.KolakoskiWord()`` for more documentation. """ # First, we need to treat the basis case # Letters swap function # Now we are ready to go in the recursive part
def LowerMechanicalWord(self, alpha, rho=0, alphabet=None): r""" Returns the lower mechanical word with slope `\alpha` and intercept `\rho`
The lower mechanical word `s_{\alpha,\rho}` with slope `\alpha` and intercept `\rho` is defined by `s_{\alpha,\rho}(n) = \lfloor\alpha(n+1) + \rho\rfloor - \lfloor\alpha n + \rho\rfloor`. [Loth02]_
INPUT:
- ``alpha`` -- real number such that `0 \leq\alpha\leq 1`
- ``rho`` -- real number (optional, default: 0)
- ``alphabet`` -- iterable of two elements or ``None`` (optional, default: ``None``)
OUTPUT:
infinite word
EXAMPLES::
sage: words.LowerMechanicalWord(1/golden_ratio^2) word: 0010010100100101001010010010100100101001... sage: words.LowerMechanicalWord(1/5) word: 0000100001000010000100001000010000100001... sage: words.LowerMechanicalWord(1/pi) word: 0001001001001001001001000100100100100100...
TESTS::
sage: m = words.LowerMechanicalWord(1/golden_ratio^2)[1:] sage: s = words.CharacteristicSturmianWord(1/golden_ratio^2) sage: m[:500] == s[:500] True
Check that this returns a word in an alphabet (:trac:`10054`)::
sage: words.UpperMechanicalWord(1/golden_ratio^2).parent() Infinite words over {0, 1} """ raise ValueError("Parameter alpha (=%s) must be in [0,1]."%alpha)
else: raise TypeError("size of alphabet (=%s) must be two"%card)
def UpperMechanicalWord(self, alpha, rho=0, alphabet=None): r""" Returns the upper mechanical word with slope `\alpha` and intercept `\rho`
The upper mechanical word `s'_{\alpha,\rho}` with slope `\alpha` and intercept `\rho` is defined by `s'_{\alpha,\rho}(n) = \lceil\alpha(n+1) + \rho\rceil - \lceil\alpha n + \rho\rceil`. [Loth02]_
INPUT:
- ``alpha`` -- real number such that `0 \leq\alpha\leq 1`
- ``rho`` -- real number (optional, default: 0)
- ``alphabet`` -- iterable of two elements or ``None`` (optional, default: ``None``)
OUTPUT:
infinite word
EXAMPLES::
sage: words.UpperMechanicalWord(1/golden_ratio^2) word: 1010010100100101001010010010100100101001... sage: words.UpperMechanicalWord(1/5) word: 1000010000100001000010000100001000010000... sage: words.UpperMechanicalWord(1/pi) word: 1001001001001001001001000100100100100100...
TESTS::
sage: m = words.UpperMechanicalWord(1/golden_ratio^2)[1:] sage: s = words.CharacteristicSturmianWord(1/golden_ratio^2) sage: m[:500] == s[:500] True
Check that this returns a word in an alphabet (:trac:`10054`)::
sage: words.UpperMechanicalWord(1/golden_ratio^2).parent() Infinite words over {0, 1} """ raise ValueError("Parameter alpha (=%s) must be in [0,1]."%alpha)
else: alphabet = build_alphabet(alphabet) card = alphabet.cardinality() if card != 2: raise TypeError("size of alphabet (=%s) must be two"%card) s = lambda n: alphabet[ceil(alpha*(n+1) + rho) - ceil(alpha*n + rho)]
def StandardEpisturmianWord(self, directive_word): r""" Returns the standard episturmian word (or epistandard word) directed by directive_word. Over a 2-letter alphabet, this function gives characteristic Sturmian words.
An infinite word `w` over a finite alphabet `A` is said to be *standard episturmian* (or *epistandard*) iff there exists an infinite word `x_1x_2x_3\cdots` over `A` (called the *directive word* of `w`) such that `w` is the limit as `n` goes to infinity of `Pal(x_1\cdots x_n)`, where `Pal` is the iterated palindromic closure function.
Note that an infinite word is *episturmian* if it has the same set of factors as some epistandard word.
See for instance [DJP01]_, [JP02]_, and [GJ07]_.
INPUT:
- ``directive_word`` - an infinite word or a period of a periodic infinite word
EXAMPLES::
sage: Fibonacci = words.StandardEpisturmianWord(Words('ab')('ab')); Fibonacci word: abaababaabaababaababaabaababaabaababaaba... sage: Tribonacci = words.StandardEpisturmianWord(Words('abc')('abc')); Tribonacci word: abacabaabacababacabaabacabacabaabacababa... sage: S = words.StandardEpisturmianWord(Words('abcd')('aabcabada')); S word: aabaacaabaaabaacaabaabaacaabaaabaacaabaa... sage: S = words.StandardEpisturmianWord(Fibonacci); S word: abaabaababaabaabaababaabaababaabaabaabab... sage: S[:25] word: abaabaababaabaabaababaaba sage: S = words.StandardEpisturmianWord(Tribonacci); S word: abaabacabaabaabacabaababaabacabaabaabaca... sage: words.StandardEpisturmianWord(123) Traceback (most recent call last): ... TypeError: directive_word is not a word, so it cannot be used to build an episturmian word sage: words.StandardEpisturmianWord(Words('ab')) Traceback (most recent call last): ... TypeError: directive_word is not a word, so it cannot be used to build an episturmian word
REFERENCES:
.. [JP02] \J. Justin, G. Pirillo, Episturmian words and episturmian morphisms, Theoret. Comput. Sci. 276 (2002) 281--313.
.. [GJ07] \A. Glen, J. Justin, Episturmian words: a survey, Preprint, 2007, :arxiv:`0801.1655`. """ self._StandardEpisturmianWord_LetterIterator(directive_word), \ datatype='iter')
def _StandardEpisturmianWord_LetterIterator(self, directive_word): r""" Internal iterating over the symbols of the standard episturmian word defined by the (directive) word directive_word.
An infinite word `w` over a finite alphabet `A` is standard episturmian (or epistandard) iff there exists an infinite word `x_1x_2x_3\ldots` over `A` (called the directive word of `w`) such that `w` is the limit as `n` goes to infinity of `Pal(x_1x_2\cdots x_n)`, where `Pal` is the iterated palindromic closure function.
INPUT:
- ``directive_word`` - an infinite word or a finite word. If directive_word is finite, then it is repeated to give an infinite word.
TESTS::
sage: import itertools sage: it = words._StandardEpisturmianWord_LetterIterator(Word('ab')) sage: list(itertools.islice(it, 13)) ['a', 'b', 'a', 'a', 'b', 'a', 'b', 'a', 'a', 'b', 'a', 'a', 'b'] """ else: d = iter(directive_word) else:
def MinimalSmoothPrefix(self, n): r""" This function finds and returns the minimal smooth prefix of length ``n``.
See [BMP07]_ for a definition.
INPUT:
- ``n`` -- the desired length of the prefix
OUTPUT:
word -- the prefix
.. NOTE::
Be patient, this function can take a really long time if asked for a large prefix.
EXAMPLES::
sage: words.MinimalSmoothPrefix(10) word: 1212212112
REFERENCES:
.. [BMP07] \S. Brlek, G. Melançon, G. Paquin, Properties of the extremal infinite smooth words, Discrete Math. Theor. Comput. Sci. 9 (2007) 33--49. """ else:
def RandomWord(self, n, m=2, alphabet=None): """ Returns a random word of length `n` over the given `m`-letter alphabet.
INPUT:
- ``n`` - integer, the length of the word - ``m`` - integer (default 2), the size of the output alphabet - ``alphabet`` - (default is `\{0,1,...,m-1\}`) any container of length m that is suitable to build an instance of OrderedAlphabet (list, tuple, str, ...)
EXAMPLES::
sage: words.RandomWord(10) # random results word: 0110100101 sage: words.RandomWord(10, 4) # random results word: 0322313320 sage: words.RandomWord(100, 7) # random results word: 2630644023642516442650025611300034413310... sage: words.RandomWord(100, 7, range(-3,4)) # random results word: 1,3,-1,-1,3,2,2,0,1,-2,1,-1,-3,-2,2,0,3,0,-3,0,3,0,-2,-2,2,0,1,-3,2,-2,-2,2,0,2,1,-2,-3,-2,-1,0,... sage: words.RandomWord(100, 5, "abcde") # random results word: acebeaaccdbedbbbdeadeebbdeeebeaaacbadaac... sage: words.RandomWord(17, 5, "abcde") # random results word: dcacbbecbddebaadd
TESTS::
sage: words.RandomWord(2,3,"abcd") Traceback (most recent call last): ... TypeError: alphabet does not contain 3 distinct elements """
LowerChristoffelWord = LowerChristoffelWord
ChristoffelWord = LowerChristoffelWord
def UpperChristoffelWord(self, p, q, alphabet=(0,1)): r""" Returns the upper Christoffel word of slope `p/q`, where `p` and `q` are relatively prime non-negative integers, over the given alphabet.
The *upper Christoffel word of slope `p/q`* is equal to the reversal of the lower Christoffel word of slope `p/q`. Equivalently, if `xuy` is the lower Christoffel word of slope `p/q`, where `x` and `y` are letters, then `yux` is the upper Christoffel word of slope `p/q` (because `u` is a palindrome).
INPUT:
- ``alphabet`` - any container of length two that is suitable to build an instance of OrderedAlphabet (list, tuple, str, ...)
EXAMPLES::
sage: words.UpperChristoffelWord(1,0) word: 1
::
sage: words.UpperChristoffelWord(0,1) word: 0
::
sage: words.UpperChristoffelWord(1,1) word: 10
::
sage: words.UpperChristoffelWord(4,7) word: 10100100100
TESTS::
sage: words.UpperChristoffelWord(51,43,"abc") Traceback (most recent call last): ... ValueError: alphabet must contain exactly two distinct elements """
@cached_method def _fibonacci_tile(self, n, q_0=None, q_1=3): r""" Returns the word `q_n` defined by the recurrence below.
The sequence `(q_n)_{n\in\NN}` is defined by `q_0=\varepsilon`, `q_1=3` and
.. MATH::
q_n = \begin{cases} q_{n-1}q_{n-2} & \text{if} n\equiv 2 \mod 3, \\ q_{n-1}\bar{q_{n-2}} & \text{if} n\equiv 0,1 \mod 3. \end{cases}
where the operator `\bar{\,}` exchanges the `1` and `3`.
INPUT:
- ``n`` - non negative integer - ``q_0`` - first initial value (default: None) It can be None, 0, 1, 2 or 3. - ``q_1`` - second initial value (default: 3) It can be None, 0, 1, 2 or 3.
EXAMPLES::
sage: for i in range(10): words._fibonacci_tile(i) word: word: 3 word: 3 word: 31 word: 311 word: 31131 word: 31131133 word: 3113113313313 word: 311311331331331131133 word: 3113113313313311311331331331131131
REFERENCES:
[BmBGL09]_ """ else:
def fibonacci_tile(self, n): r""" Returns the `n`-th Fibonacci Tile [BmBGL09]_.
EXAMPLES::
sage: for i in range(3): words.fibonacci_tile(i) Path: 3210 Path: 323030101212 Path: 3230301030323212323032321210121232121010... """
def dual_fibonacci_tile(self, n): r""" Returns the `n`-th dual Fibonacci Tile [BmBGL09]_.
EXAMPLES::
sage: for i in range(4): words.dual_fibonacci_tile(i) Path: 3210 Path: 32123032301030121012 Path: 3212303230103230321232101232123032123210... Path: 3212303230103230321232101232123032123210... """
def _s_adic_iterator(self, sequence, letters): r""" Returns the iterator over the `s`-adic infinite word obtained from a sequence of morphisms applied on letters where the hypothesis of nested prefixes is used.
DEFINITION (from [Fogg]_):
Let `w` be a infinite word over an alphabet `A = A_0`. A standard representation of $w$ is obtained from a sequence of substitutions `\sigma_k : A_{k+1} \to A_k` and a sequence of letters `a_k \in A_k` such that:
.. MATH::
\lim_{k\to\infty} \sigma_0 \circ \sigma_1 \circ \cdots \sigma_k(a_k).
Given a set of substitutions `S`, we say that the representation is `S`-adic standard if the substitutions are chosen in `S`.
INPUT:
- ``sequence`` - An iterable sequence of morphisms. It may be finite or infinite. - ``letters`` - An iterable sequence of letters. The image of the (i+1)-th letter under the (i+1)-th morphism must start with the i-th letter.
OUTPUT:
iterator of letters
EXAMPLES:
Let's define three morphisms and compute the first nested succesive prefixes of the `s`-adic word::
sage: m1 = WordMorphism('e->gh,f->hg') sage: m2 = WordMorphism('c->ef,d->e') sage: m3 = WordMorphism('a->cd,b->dc') sage: Word(words._s_adic_iterator([m1],'e')) word: gh sage: Word(words._s_adic_iterator([m1,m2],'ec')) word: ghhg sage: Word(words._s_adic_iterator([m1,m2,m3],'eca')) word: ghhggh
If the letters don't satisfy the hypothesis of the algorithm, an error is raised::
sage: Word(words._s_adic_iterator([m1,m2,m3],'ecb')) Traceback (most recent call last): ... ValueError: The hypothesis of the algorithm used is not satisfied: the image of the 3-th letter (=b) under the 3-th morphism (=a->cd, b->dc) should start with the 2-th letter (=c).
Two examples of infinite `s`-adic words::
sage: tm = WordMorphism('a->ab,b->ba') sage: fib = WordMorphism('a->ab,b->a') sage: from itertools import repeat sage: Word(words._s_adic_iterator(repeat(tm),repeat('a'))) word: abbabaabbaababbabaababbaabbabaabbaababba... sage: Word(words._s_adic_iterator(repeat(fib),repeat('a'))) word: abaababaabaababaababaabaababaabaababaaba...
A less trivial infinite `s`-adic word::
sage: m = WordMorphism({4:tm,5:fib}) sage: tmword = words.ThueMorseWord([4,5]) sage: w = m(tmword) sage: Word(words._s_adic_iterator(w, repeat('a'))) word: abbaababbaabbaabbaababbaababbaabbaababba...
The morphism `\sigma: a \mapsto ba, b \mapsto b` cannot satisfy the hypothesis of the algorithm (nested prefixes)::
sage: sigma = WordMorphism('a->ba,b->b') sage: Word(words._s_adic_iterator(repeat(sigma),repeat('a'))) Traceback (most recent call last): ... ValueError: The hypothesis of the algorithm used is not satisfied: the image of the 2-th letter (=a) under the 2-th morphism (=a->ba, b->b) should start with the 1-th letter (=a).
AUTHORS:
- Sebastien Labbe (2009-12-18): initial version """
def s_adic(self, sequence, letters, morphisms=None): r""" Returns the `s`-adic infinite word obtained from a sequence of morphisms applied on a letter.
DEFINITION (from [Fogg]_):
Let `w` be a infinite word over an alphabet `A = A_0`. A standard representation of `w` is obtained from a sequence of substitutions `\sigma_k : A_{k+1} \to A_k` and a sequence of letters `a_k \in A_k` such that:
.. MATH::
\lim_{k\to\infty} \sigma_0 \circ \sigma_1 \circ \cdots \sigma_k(a_k).
Given a set of substitutions `S`, we say that the representation is `S`-adic standard if the substitutions are chosen in `S`.
INPUT:
- ``sequence`` - An iterable sequence of indices or of morphisms. It may be finite or infinite. If ``sequence`` is infinite, the image of the `(i+1)`-th letter under the `(i+1)`-th morphism must start with the `i`-th letter.
- ``letters`` - A letter or a sequence of letters.
- ``morphisms`` - dict, list, callable or ``None`` (optional, default ``None``) an object that maps indices to morphisms. If ``None``, then ``sequence`` must consist of morphisms.
OUTPUT:
A word.
EXAMPLES:
Let's define three morphisms and compute the first nested succesive prefixes of the `s`-adic word::
sage: m1 = WordMorphism('e->gh,f->hg') sage: m2 = WordMorphism('c->ef,d->e') sage: m3 = WordMorphism('a->cd,b->dc') sage: words.s_adic([m1],'e') word: gh sage: words.s_adic([m1,m2],'ec') word: ghhg sage: words.s_adic([m1,m2,m3],'eca') word: ghhggh
When the given sequence of morphism is finite, one may simply give the last letter, i.e. ``'a'``, instead of giving all of them, i.e. ``'eca'``::
sage: words.s_adic([m1,m2,m3],'a') word: ghhggh sage: words.s_adic([m1,m2,m3],'b') word: ghghhg
If the letters don't satisfy the hypothesis of the algorithm (nested prefixes), an error is raised::
sage: words.s_adic([m1,m2,m3],'ecb') Traceback (most recent call last): ... ValueError: The hypothesis of the algorithm used is not satisfied: the image of the 3-th letter (=b) under the 3-th morphism (=a->cd, b->dc) should start with the 2-th letter (=c).
Let's define the Thue-Morse morphism and the Fibonacci morphism which will be used below to illustrate more examples and let's import the ``repeat`` tool from the ``itertools``::
sage: tm = WordMorphism('a->ab,b->ba') sage: fib = WordMorphism('a->ab,b->a') sage: from itertools import repeat
Two trivial examples of infinite `s`-adic words::
sage: words.s_adic(repeat(tm),repeat('a')) word: abbabaabbaababbabaababbaabbabaabbaababba...
::
sage: words.s_adic(repeat(fib),repeat('a')) word: abaababaabaababaababaabaababaabaababaaba...
A less trivial infinite `s`-adic word::
sage: t = words.ThueMorseWord([tm,fib]) sage: words.s_adic(t, repeat('a')) word: abbaababbaabbaabbaababbaababbaabbaababba...
The same thing using a sequence of indices::
sage: tmword = words.ThueMorseWord([0,1]) sage: words.s_adic(tmword, repeat('a'), [tm,fib]) word: abbaababbaabbaabbaababbaababbaabbaababba...
The correspondance of the indices may be given as a dict::
sage: words.s_adic(tmword, repeat('a'), {0:tm,1:fib}) word: abbaababbaabbaabbaababbaababbaabbaababba...
because dict are more versatile for indices::
sage: tmwordTF = words.ThueMorseWord('TF') sage: words.s_adic(tmwordTF, repeat('a'), {'T':tm,'F':fib}) word: abbaababbaabbaabbaababbaababbaabbaababba...
or by a callable::
sage: f = lambda n: tm if n == 0 else fib sage: words.s_adic(words.ThueMorseWord(), repeat('a'), f) word: abbaababbaabbaabbaababbaababbaabbaababba...
Random infinite `s`-adic words::
sage: from sage.misc.prandom import randint sage: def it(): ....: while True: yield randint(0,1) sage: words.s_adic(it(), repeat('a'), [tm,fib]) word: abbaabababbaababbaabbaababbaabababbaabba... sage: words.s_adic(it(), repeat('a'), [tm,fib]) word: abbaababbaabbaababbaababbaabbaababbaabba... sage: words.s_adic(it(), repeat('a'), [tm,fib]) word: abaaababaabaabaaababaabaaababaaababaabaa...
An example where the sequences cycle on two morphisms and two letters::
sage: G = WordMorphism('a->cd,b->dc') sage: H = WordMorphism('c->ab,d->ba') sage: from itertools import cycle sage: words.s_adic([G,H],'ac') word: cddc sage: words.s_adic(cycle([G,H]),cycle('ac')) word: cddcdccddccdcddcdccdcddccddcdccddccdcddc...
The morphism `\sigma: a\mapsto ba, b\mapsto b` can't satisfy the hypothesis of the nested prefixes, but one may compute arbitrarily long finite words having the limit `\sigma^\omega(a)`::
sage: sigma = WordMorphism('a->ba,b->b') sage: words.s_adic(repeat(sigma),repeat('a')) Traceback (most recent call last): ... ValueError: The hypothesis of the algorithm used is not satisfied: the image of the 2-th letter (=a) under the 2-th morphism (=a->ba, b->b) should start with the 1-th letter (=a). sage: words.s_adic([sigma],'a') word: ba sage: words.s_adic([sigma,sigma],'a') word: bba sage: words.s_adic([sigma]*3,'a') word: bbba sage: words.s_adic([sigma]*4,'a') word: bbbba sage: words.s_adic([sigma]*5,'a') word: bbbbba sage: words.s_adic([sigma]*6,'a') word: bbbbbba sage: words.s_adic([sigma]*7,'a') word: bbbbbbba
The following examples illustrates an `S`-adic word defined over an infinite set `S` of morphisms `x_h`::
sage: x = lambda h:WordMorphism({1:[2],2:[3]+[1]*(h+1),3:[3]+[1]*h}) sage: for h in [0,1,2,3]: ....: print("{} {}".format(h, x(h))) 0 1->2, 2->31, 3->3 1 1->2, 2->311, 3->31 2 1->2, 2->3111, 3->311 3 1->2, 2->31111, 3->3111 sage: w = Word(lambda n : valuation(n+1, 2) ); w word: 0102010301020104010201030102010501020103... sage: s = words.s_adic(w, repeat(3), x); s word: 3232232232322322322323223223232232232232... sage: prefixe = s[:10000] sage: list(map(prefixe.number_of_factors, range(15))) [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] sage: [_[i+1] - _[i] for i in range(len(_)-1)] [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
TESTS::
sage: tm = WordMorphism('a->ab,b->ba') sage: fib = WordMorphism('a->ab,b->a') sage: w = words.s_adic([fib,tm,tm,fib,tm,fib]*3,'a') sage: w word: abaaabaababaabaaababaaababaaabaababaabaa... sage: w.length() 32400 sage: w.parent() Finite words over {'a', 'b'} sage: type(w) <class 'sage.combinat.words.word.FiniteWord_iter_with_caching'>
::
sage: words.s_adic([fib,tm,tm,fib,tm,fib],'aaaaaaa') word: abaaabaababaabaaababaaababaaabaababa
::
sage: words.s_adic([0,1,0,1,0,1,0,1],'a',[tm,fib]) word: abbaabababbaabbaababbaababbaabababbaabba...
::
sage: words.s_adic([fib,fib],'bb') Traceback (most recent call last): ... ValueError: The hypothesis of the algorithm used is not satisfied: the image of the 2-th letter (=b) under the 2-th morphism (=a->ab, b->a) should start with the 1-th letter (=b).
Test on different letters::
sage: tm = WordMorphism({0:[0,1], 1:[1,0]}) sage: fib = WordMorphism({0:[0,1], 1:[0]}) sage: f = lambda n: tm if n == 0 else fib sage: words.s_adic(words.ThueMorseWord(), repeat(0), f) word: 0110010110011001100101100101100110010110...
Testing the message error for the third argument::
sage: words.s_adic(words.ThueMorseWord(), repeat(0), 5) Traceback (most recent call last): ... TypeError: morphisms (=5) must be None, callable or provide a __getitem__ method.
AUTHORS:
- Sebastien Labbe (2009-12-18): initial version """ else:
and hasattr(letters, "__len__") and len(letters) == 1:
#kwds['check'] = False
def PalindromicDefectWord(self, k=1, alphabet='ab'): r""" Return the finite word `w = a b^k a b^{k-1} a a b^{k-1} a b^{k} a`.
As described by Brlek, Hamel, Nivat and Reutenauer in [BHNR04]_, this finite word `w` is such that the infinite periodic word `w^{\omega}` has palindromic defect ``k``.
INPUT:
- ``k`` -- positive integer (optional, default: 1)
- ``alphabet`` -- iterable (optional, default: ``'ab'``) of size two
OUTPUT:
finite word
EXAMPLES::
sage: words.PalindromicDefectWord(10) word: abbbbbbbbbbabbbbbbbbbaabbbbbbbbbabbbbbbb...
::
sage: w = words.PalindromicDefectWord(3) sage: w word: abbbabbaabbabbba sage: w.defect() 0 sage: (w^2).defect() 3 sage: (w^3).defect() 3
On other alphabets::
sage: words.PalindromicDefectWord(3, alphabet='cd') word: cdddcddccddcdddc sage: words.PalindromicDefectWord(3, alphabet=['c', 3]) word: c333c33cc33c333c
TESTS::
sage: k = 25 sage: (words.PalindromicDefectWord(k)^2).defect() 25
If k is negative or zero, then we get the same word::
sage: words.PalindromicDefectWord(0) word: aaaaaa sage: words.PalindromicDefectWord(-3) word: aaaaaa """
def BaumSweetWord(self): r""" Returns the Baum-Sweet Word.
The Baum-Sweet Sequence is an infinite word over the alphabet `\{0,1\}` defined by the following string substitution rules:
`00 \rightarrow 0000`
`01 \rightarrow 1001`
`10 \rightarrow 0100`
`11 \rightarrow 1101`
The substitution rule above can be considered as a morphism on the submonoid of `\{0,1\}` generated by `\{00,01,10,11\}` (which is a free monoid on these generators).
It is also defined as the concatenation of the terms from the Baum-Sweet Sequence:
.. MATH::
b_n = \begin{cases} 0, & \text{if } n = 0 \\ 1, & \text{if } m \text{ is even} \\ b_{\frac{m-1}{2}}, & \text{if } m \text{ is odd} \end{cases}
where `n=m4^k` and `m` is not divisible by 4 if `m \neq 0`.
The individual terms of the Baum-Sweet Sequence are also given by:
.. MATH::
b_n = \begin{cases} 1, & \text{if the binary representation of} n \text{ contains no block of consecutive 0's of odd length}\\ 0, & \text{otherwise}\\ \end{cases}\\
for `n > 0` with `b_0 = 1`.
For more information see: :wikipedia:`Baum-Sweet_sequence`.
EXAMPLES:
Baum-Sweet Word::
sage: w = words.BaumSweetWord(); w word: 1101100101001001100100000100100101001001...
Block Definition::
sage: w = words.BaumSweetWord() sage: f = lambda n: '1' if all(len(x)%2==0 for x in bin(n)[2:].split('1')) else '0' sage: all(f(i) == w[i] for i in range(1,100)) True """
words = WordGenerator() |