class MatrixCocycle(object): r""" Matrix cocycle INPUT: - ``gens`` -- list, tuple or dict; the matrices. Keys 0,...,n-1 are used for list and tuple. - ``cone`` -- dict or matrix or None (default: None); the cone for each matrix generators. If it is a matrix, then it serves as the cone for all matrices. The cone is defined by the columns of the matrix. If None, then the cone is the identity matrix. - ``language`` -- regular language or None (default: None); if None, the language is the full shift. EXAMPLES:: sage: from slabbe.matrix_cocycle import MatrixCocycle sage: B1 = matrix(3, [1,0,0, 0,1,0, 0,1,1]) sage: B2 = matrix(3, [1,0,0, 0,0,1, 0,1,1]) sage: B3 = matrix(3, [0,1,0, 0,0,1, 1,0,1]) sage: gens = {'1':B1, '2':B2, '3':B3} sage: cone = matrix(3, [1,1,1,0,1,1,0,0,1]) sage: MatrixCocycle(gens, cone) Cocycle with 3 gens over Language of finite words over alphabet ['1', '2', '3'] """ def __init__(self, gens, cone=None, language=None): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import MatrixCocycle sage: gens = {'A':matrix(3, [1,0,0, 0,1,0, 0,1,1])} sage: cone = identity_matrix(3) sage: MatrixCocycle(gens, cone) Cocycle with 1 gens over Language of finite words over alphabet ['A'] """ if isinstance(gens, dict): self._gens = gens elif isinstance(gens, (list, tuple)): self._gens = dict(enumerate(gens)) else: raise ValueError("gens must be a list, tuple or a dict") if cone is None: ID = self.identity_matrix() self._cone_dict = {letter:ID for letter in self._gens.keys()} elif isinstance(cone, dict): self._cone_dict = cone else: self._cone_dict = {letter:cone for letter in self._gens.keys()} if language is None: self._language = Language(sorted(self._gens.keys())) else: self._language = language def __repr__(self): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import MatrixCocycle sage: gens = {'A':matrix(3, [1,0,0, 0,1,0, 0,1,1])} sage: cone = identity_matrix(3) sage: MatrixCocycle(gens, cone) Cocycle with 1 gens over Language of finite words over alphabet ['A'] """ s = "Cocycle with {} gens over {}" return s.format(len(self._gens), self._language) def gens(self): return self._gens def gens_inverses(self): r""" Return a dictionary of the inverses of the generators. EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: coc = cocycles.Brun() sage: coc.gens_inverses().keys() [321, 132, 231, 213, 312, 123] sage: coc.gens_inverses().values() [ [ 1 -1 0] [ 1 0 0] [ 1 0 -1] [ 1 0 0] [ 1 0 0] [ 1 0 0] [ 0 1 0] [ 0 1 -1] [ 0 1 0] [ 0 1 0] [-1 1 0] [ 0 1 0] [ 0 0 1], [ 0 0 1], [ 0 0 1], [-1 0 1], [ 0 0 1], [ 0 -1 1] ] If possible, the ring is the Integer ring:: sage: coc = cocycles.Reverse() sage: coc.gens_inverses().values() [ [ 1 -1 -1] [ 1 0 0] [ 1 0 0] [-1/2 1/2 1/2] [ 0 1 0] [-1 1 -1] [ 0 1 0] [ 1/2 -1/2 1/2] [ 0 0 1], [ 0 0 1], [-1 -1 1], [ 1/2 1/2 -1/2] ] sage: [m.parent() for m in _] [Full MatrixSpace of 3 by 3 dense matrices over Integer Ring, Full MatrixSpace of 3 by 3 dense matrices over Integer Ring, Full MatrixSpace of 3 by 3 dense matrices over Integer Ring, Full MatrixSpace of 3 by 3 dense matrices over Rational Field] """ from sage.rings.integer_ring import ZZ D = {} for k,v in self.gens().iteritems(): M = v.inverse() try: M_ZZ = M.change_ring(ZZ) except TypeError: pass else: M = M_ZZ D[k] = M return D def cone_dict(self): return self._cone_dict def cone(self, key): return self._cone_dict[key] def language(self): return self._language @cached_method def identity_matrix(self): return self._gens.values()[0].parent().one() def word_to_matrix(self, w): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: C = cocycles.Sorted_ARP() sage: C.word_to_matrix(Word()) [1 0 0] [0 1 0] [0 0 1] """ return prod((self._gens[a] for a in w), z=self.identity_matrix()) def n_words_iterator(self, n): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.Sorted_ARP() sage: list(ARP.n_words_iterator(1)) [word: A1, word: A2, word: A3, word: P1, word: P2, word: P3] """ return self._language.words_of_length_iterator(n) def n_matrices_iterator(self, n): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.Sorted_ARP() sage: A,B = zip(*list(ARP.n_matrices_iterator(1))) sage: A (word: A1, word: A2, word: A3, word: P1, word: P2, word: P3) sage: B ( [1 0 0] [1 0 0] [0 1 0] [0 1 0] [0 0 1] [0 0 1] [0 1 0] [0 0 1] [0 0 1] [0 1 1] [1 0 1] [0 1 1] [1 1 1], [1 1 1], [1 1 1], [1 1 1], [1 1 1], [1 1 1] ) """ for w in self.n_words_iterator(n): yield w, self.word_to_matrix(w) def n_matrices_eigenvalues_iterator(self,n): r""" Return the eigenvalues of the matrices of level n. EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.ARP() sage: list(ARP.n_matrices_eigenvalues_iterator(1)) [(word: 1, [1, 1, 1]), (word: 2, [1, 1, 1]), (word: 3, [1, 1, 1]), (word: 123, [1, 1, 1]), (word: 132, [1, 1, 1]), (word: 213, [1, 1, 1]), (word: 231, [1, 1, 1]), (word: 312, [1, 1, 1]), (word: 321, [1, 1, 1])] :: sage: B = cocycles.Sorted_Brun() sage: list(B.n_matrices_eigenvalues_iterator(1)) [(word: 1, [1, 1, 1]), (word: 2, [1, -0.618033988749895?, 1.618033988749895?]), (word: 3, [1.465571231876768?, -0.2327856159383841? - 0.7925519925154479?*I, -0.2327856159383841? + 0.7925519925154479?*I])] """ for w,m in self.n_matrices_iterator(n): yield w, m.eigenvalues() def n_matrices_pinching_iterator(self,n): r""" Return the pinching matrices of level n. EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.ARP() sage: list(ARP.n_matrices_pinching_iterator(0)) [] sage: list(ARP.n_matrices_pinching_iterator(1)) [] sage: list(ARP.n_matrices_pinching_iterator(2)) [] sage: L = list(ARP.n_matrices_pinching_iterator(3)) sage: L[0] ( [4 5 2] [2 3 1] word: 1,2,213, [1 1 1] ) """ for w,m in self.n_matrices_iterator(n): p = m.charpoly() d = p.discriminant() if p.is_irreducible() and d > 0 and not d.is_square(): yield w, m def n_matrices_eigenvectors(self,n, verbose=False): r""" Return the left and right eigenvectors of the matrices of level n. EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: C = cocycles.ARP() sage: C.n_matrices_eigenvectors(1) [(word: 1, (1.0, 0.0, 0.0), (0.0, 0.0, 1.0)), (word: 2, (0.0, 1.0, 0.0), (1.0, 0.0, 0.0)), (word: 3, (0.0, 0.0, 1.0), (1.0, 0.0, 0.0)), (word: 123, (0.0, 0.0, 1.0), (1.0, 0.0, 0.0)), (word: 132, (0.0, 1.0, 0.0), (1.0, 0.0, 0.0)), (word: 213, (0.0, 0.0, 1.0), (0.0, 1.0, 0.0)), (word: 231, (1.0, 0.0, 0.0), (0.0, 1.0, 0.0)), (word: 312, (0.0, 1.0, 0.0), (0.0, 0.0, 1.0)), (word: 321, (1.0, 0.0, 0.0), (0.0, 0.0, 1.0))] """ R = [] for w,m in self.n_matrices_iterator(n): try: a,v_right = perron_right_eigenvector(m) b,v_left = perron_right_eigenvector(m.transpose()) except ValueError: print "problem with :\n",m else: R.append((w, v_right,v_left)) if verbose: print "indices of matrices:", w print m print "eigenvectors:", v_right, v_left return R @cached_method def n_matrices_non_pisot(self,n, verbose=False): r""" Return the list of non pisot matrices (as list of indices of base matrices). EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.Sorted_ARP() sage: ARP.n_matrices_non_pisot(1) [word: A1, word: A2] sage: ARP.n_matrices_non_pisot(2) # long time (1s) [word: A1,A1, word: A1,A2, word: A2,A1, word: A2,A2] sage: ARP.n_matrices_non_pisot(3) # long time (11s) [word: A1,A1,A1, word: A1,A1,A2, word: A1,A2,A1, word: A1,A2,A2, word: A2,A1,A1, word: A2,A1,A2, word: A2,A2,A1, word: A2,A2,A2] sage: len(ARP.n_matrices_non_pisot(4)) # long time 16 :: sage: from slabbe.matrix_cocycle import cocycles sage: B = cocycles.Sorted_Brun() sage: B.n_matrices_non_pisot(2) [word: 11, word: 12, word: 21, word: 22] sage: B.n_matrices_non_pisot(3) [word: 111, word: 112, word: 121, word: 122, word: 211, word: 212, word: 221, word: 222] """ return [w for w in self.n_words_iterator(n) if not self.is_pisot(w)] def n_matrices_semi_norm_iterator(self, n, p=2): r""" EXAMPLES: For the 1-norm, all matrices contracts the hyperplane:: sage: from slabbe.matrix_cocycle import cocycles sage: C = cocycles.ARP() sage: it = C.n_matrices_semi_norm_iterator(1, p=1) sage: for _ in range(5): print next(it) # tolerance 0.0001 (word: 1, 1.0, False) (word: 2, 1.0, False) (word: 3, 1.0, False) (word: 123, 0.9999885582839877, False) (word: 132, 0.9999854006354785, False) For the 2-norm, AR matrices do not contract:: sage: it = C.n_matrices_semi_norm_iterator(1, p=2) sage: for w,s,b in it: print w,s,b # long time (6s) A1 1.30656296488 False A2 1.30656296486 False A3 1.30656296475 False P12 0.99999999996 False P13 0.999999999967 False P21 0.999999999967 False P23 0.999999999997 False P31 0.999999999769 False P32 0.999999999839 False When, the 1-norm is < 1, the product is pisot:: sage: it = C.n_matrices_semi_norm_iterator(2, p=1) sage: for w,s,b in it: print w,s,b # long time A1,A1 1.0 False A1,A2 1.0 False A1,A3 1.0 False A1,P12 0.999998922557 False A1,P13 0.999997464905 False A1,P21 0.999993244882 False A1,P23 0.999999150973 True A1,P31 0.999994030522 False A1,P32 0.999998046513 True A2,A1 1.0 False A2,A2 1.0 False A2,A3 1.0 False A2,P12 0.99999375291 False A2,P13 0.999995591588 True ... P31,A3 0.999988326888 False P31,P12 0.749998931902 True P31,P23 0.799999157344 True P31,P32 0.749993104833 True P32,A1 0.999997170005 True P32,A3 0.99999420509 False P32,P13 0.666665046248 True P32,P21 0.666665629351 True P32,P31 0.666664488371 True """ if n == 0: raise NotImplementedError for w,m in self.n_matrices_iterator(n): cone = m*self.cone(w[-1]) yield w, semi_norm_cone(m.transpose(), cone, p=p), self.is_pisot(w) def n_matrices_distorsion_iterator(self, n, p=1): r""" Return the the distorsion of the n-cylinders. EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: T = cocycles.Sorted_ARP() sage: it =T.n_matrices_distorsion_iterator(1) sage: list(it) [(word: A1, 2), (word: A2, 2), (word: A3, 2), (word: P1, 3), (word: P2, 3), (word: P3, 3)] """ for w,m in self.n_matrices_iterator(n): yield w, distorsion(m, p=p) def n_cylinders_iterator(self, n): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: C = cocycles.ARP() sage: it = C.n_cylinders_iterator(1) sage: for w,cyl in it: print "{}\n{}".format(w,cyl) 1 [1 1 1] [0 1 0] [0 0 1] 2 [1 0 0] [1 1 1] [0 0 1] 3 [1 0 0] [0 1 0] [1 1 1] 123 [1 0 1] [1 1 1] [1 1 2] 132 [1 1 0] [1 2 1] [1 1 1] 213 [1 1 1] [0 1 1] [1 1 2] 231 [2 1 1] [1 1 0] [1 1 1] 312 [1 1 1] [1 2 1] [0 1 1] 321 [2 1 1] [1 1 1] [1 0 1] """ if n == 0: raise NotImplementedError for w,m in self.n_matrices_iterator(n): yield w, m*self.cone(w[-1]) def n_cylinders_edges(self, n): r""" Return the set of edges of the n-cylinders. EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.ARP() sage: ARP.n_cylinders_edges(1) {frozenset({(1, 1, 0), (1, 1, 1)}), frozenset({(0, 1, 0), (1, 1, 0)}), frozenset({(1, 1, 1), (2, 1, 1)}), frozenset({(0, 0, 1), (1, 0, 1)}), frozenset({(0, 1, 0), (0, 1, 1)}), frozenset({(0, 1, 1), (1, 0, 1)}), frozenset({(1, 0, 0), (1, 1, 0)}), frozenset({(1, 1, 0), (2, 1, 1)}), frozenset({(1, 0, 1), (1, 1, 2)}), frozenset({(1, 1, 0), (1, 2, 1)}), frozenset({(1, 0, 1), (2, 1, 1)}), frozenset({(0, 0, 1), (0, 1, 1)}), frozenset({(1, 0, 1), (1, 1, 1)}), frozenset({(0, 1, 1), (1, 2, 1)}), frozenset({(0, 1, 1), (1, 1, 2)}), frozenset({(1, 0, 0), (1, 0, 1)}), frozenset({(1, 1, 1), (1, 2, 1)}), frozenset({(1, 0, 1), (1, 1, 0)}), frozenset({(0, 1, 1), (1, 1, 1)}), frozenset({(0, 1, 1), (1, 1, 0)}), frozenset({(1, 1, 1), (1, 1, 2)})} """ from sage.rings.finite_rings.integer_mod_ring import Integers edges = set() for w,cyl in self.n_cylinders_iterator(n): cols = cyl.columns() indices = Integers(len(cols)) edges.update(frozenset((cols[i], cols[i+1])) for i in indices) return edges def is_pisot(self, w): r""" """ m = self.word_to_matrix(w) S = sorted((abs(e) for e in m.eigenvalues()), reverse=True) return S[0] > 1 and S[1] < 1 def non_pisot_automaton(self, n): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: C = cocycles.ARP() sage: A = C.non_pisot_automaton(2) sage: A Automaton with 2 states sage: A.graph().plot(edge_labels=True) # not tested """ L = [] for i in range(n): L.extend(self.n_matrices_non_pisot(i)) alphabet = self._language._alphabet F = FiniteLanguage(alphabet, L) A = F.minimal_automaton() return A #G = A.graph() #to_remove = set(A.states()) - set(A.final_states()) #G.delete_vertices(to_remove) #return G def distorsion_max(self, n, p=1): r""" EXAMPLES: Non borné:: sage: from slabbe.matrix_cocycle import cocycles sage: T = cocycles.Sorted_ARP() sage: T.distorsion_max(1, p=oo) 1 sage: T.distorsion_max(2, p=oo) 3 sage: T.distorsion_max(3, p=oo) 5 sage: T.distorsion_max(4, p=oo) 7 """ return max(d for (w,d) in self.n_matrices_distorsion_iterator(n, p=p)) def distorsion_argmax(self, n, p=1): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.Sorted_ARP() sage: ARP.distorsion_argmax(1) ( [1 0 0] [1 1 0] word: A1, [3 2 1] ) """ it = self.n_cylinders_iterator(n) key = lambda (w,m):distorsion(m, p=p) return max(it, key=key) def plot_n_cylinders(self, n, labels=True): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: C = cocycles.Sorted_ARP() sage: G = C.plot_n_cylinders(3) """ from sage.plot.graphics import Graphics from sage.plot.polygon import polygon from sage.plot.text import text from matrices import M3to2 G = Graphics() for w,cyl in self.n_cylinders_iterator(n): columns = cyl.columns() G += polygon((M3to2*col/col.norm(1) for col in columns), fill=False) if labels: sum_cols = sum(columns) G += text("{}".format(w), M3to2*sum_cols/sum_cols.norm(1)) return G def plot_n_matrices_eigenvectors(self, n, side='right', color_index=0, draw_line=False): r""" INPUT: - ``n`` -- integer, length - ``side`` -- ``'left'`` or ``'right'``, drawing left or right eigenvectors - ``color_index`` -- 0 for first letter, -1 for last letter - ``draw_line`` -- boolean EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.ARP() sage: G = ARP.plot_n_matrices_eigenvectors(2) """ from sage.plot.graphics import Graphics from sage.plot.point import point from sage.plot.line import line from sage.plot.text import text from sage.plot.colors import hue from sage.modules.free_module_element import vector from matrices import M3to2 R = self.n_matrices_eigenvectors(n) L = [(w, M3to2*(a/sum(a)), M3to2*(b/sum(b))) for (w,a,b) in R] G = Graphics() alphabet = self._language._alphabet color_ = dict( (letter, hue(i/float(len(alphabet)))) for i,letter in enumerate(alphabet)) for letter in alphabet: L_filtered = [(w,p1,p2) for (w,p1,p2) in L if w[color_index] == letter] words,rights,lefts = zip(*L_filtered) if side == 'right': G += point(rights, color=color_[letter], legend_label=letter) elif side == 'left': G += point(lefts, color=color_[letter], legend_label=letter) else: raise ValueError("side(=%s) should be left or right" % side) if draw_line: for (a,b) in L: G += line([a,b], color='black', linestyle=":") G += line([M3to2*vector(a) for a in [(1,0,0), (0,1,0), (0,0,1), (1,0,0)]]) title = "%s eigenvectors, colored by letter w[%s] of cylinder w" % (side, color_index) G += text(title, (0.5, 1.05), axis_coords=True) G.axes(False) return G def plot_pisot_conjugates(self, n): r""" EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: B = cocycles.Sorted_Brun() sage: G = B.plot_pisot_conjugates(5) # long time (8s) Image envoyee a Timo (6 mai 2014):: sage: G = sum(B.plot_pisot_conjugates(i) for i in [1..6]) #not tested """ from sage.plot.point import points Lreal = [] Limag = [] for w,s in self.n_matrices_eigenvalues_iterator(n): a,b,c = sorted(s, key=abs) if a.imag() == 0 and b.imag() == 0: Lreal.append((a,b)) else: Limag.append((a.real(),a.imag())) Limag.append((b.real(),b.imag())) return points(Lreal) + points(Limag, color='red') def tikz_n_cylinders(self, n, labels=None, scale=1): r""" INPUT: - ``labels`` -- None, True or False (default: None), if None, it takes value True if n is 1. EXAMPLES:: sage: from slabbe.matrix_cocycle import cocycles sage: ARP = cocycles.ARP() sage: t = ARP.tikz_n_cylinders(1, labels=True, scale=4) sage: t \documentclass[tikz]{standalone} \usepackage{amsmath} \begin{document} \begin{tikzpicture} [scale=4] \draw (0.0000, -0.5000) -- (0.0000, 0.0000); \draw (0.0000, -0.5000) -- (0.8660, -0.5000); \draw (0.0000, 0.0000) -- (-0.2165, -0.1250); ... ... 23 lines not printed (1317 characters in total) ... ... \node at (-0.1443, 0.1667) {$213$}; \node at (-0.2165, 0.0417) {$231$}; \node at (0.0722, -0.2083) {$312$}; \node at (-0.0722, -0.2083) {$321$}; \end{tikzpicture} \end{document} :: sage: from sage.misc.temporary_file import tmp_filename sage: filename = tmp_filename('temp','.pdf') sage: _ = t.pdf(filename) """ if labels is None: labels = True if n == 1 else False lines = [] lines.append(r"\begin{tikzpicture}") lines.append("[scale={}]".format(scale)) from matrices import M3to2 for (u,v) in self.n_cylinders_edges(n): u = rounded_string_vector(M3to2 * u / u.norm(1), digits=4) v = rounded_string_vector(M3to2 * v / v.norm(1), digits=4) lines.append(r"\draw {} -- {};".format(u,v)) if labels: for w,cyl in self.n_cylinders_iterator(n): u = sum(c / c.norm(1) for c in cyl.columns()) u = rounded_string_vector(M3to2 * u / u.norm(1), digits=4) lines.append(r"\node at {} {{${}$}};".format(u, w)) lines.append(r"\end{tikzpicture}") from slabbe import TikzPicture return TikzPicture("\n".join(lines), use_sage_preamble=False)