1 | | r""" |
2 | | Substitutions over unit cube faces (Rauzy fractals) |
3 | | |
4 | | This module implements the `E_1^*(\sigma)` substitution |
5 | | associated with a one-dimensional substitution `\sigma`, |
6 | | that acts on unit faces of dimension `(d-1)` in `\RR^d`. |
7 | | |
8 | | This module defines the following classes and functions: |
9 | | |
10 | | - ``Face`` - a class to model a face |
11 | | |
12 | | - ``Patch`` - a class to model a finite set of faces |
13 | | |
14 | | - ``E1Star`` - a class to model the `E_1^*(\sigma)` application |
15 | | defined by the substitution sigma |
16 | | |
17 | | See the documentation of these objects for more information. |
18 | | |
19 | | The convention for the choice of the unit faces and the |
20 | | definition of `E_1^*(\sigma)` varies from article to article. |
21 | | Here, unit faces are defined by |
22 | | |
23 | | .. MATH:: |
24 | | |
25 | | \begin{array}{ccc} |
26 | | \,[x, 1]^* & = & \{x + \lambda e_2 + \mu e_3 : \lambda, \mu \in [0,1]\} \\ |
27 | | \,[x, 2]^* & = & \{x + \lambda e_1 + \mu e_3 : \lambda, \mu \in [0,1]\} \\ |
28 | | \,[x, 3]^* & = & \{x + \lambda e_1 + \mu e_2 : \lambda, \mu \in [0,1]\} |
29 | | \end{array} |
30 | | |
31 | | and the dual substitution `E_1^*(\sigma)` is defined by |
32 | | |
33 | | .. MATH:: |
34 | | |
35 | | E_1^*(\sigma)([x,i]^*) = |
36 | | \bigcup_{k = 1,2,3} \; \bigcup_{s | \sigma(k) = pis} |
37 | | [M^{-1}(x + \ell(s)), k]^*, |
38 | | |
39 | | where `\ell(s)` is the abelianized of `s`, and `M` is the matrix of `\sigma`. |
40 | | |
41 | | AUTHORS: |
42 | | |
43 | | - Franco Saliola (2009): initial version |
44 | | - Vincent Delecroix, Timo Jolivet, Stepan Starosta, Sebastien Labbe (2010-05): redesign |
45 | | - Timo Jolivet (2010-08, 2010-09, 2011): redesign |
46 | | |
47 | | REFERENCES: |
48 | | |
49 | | .. [AI] P. Arnoux, S. Ito, |
50 | | Pisot substitutions and Rauzy fractals, |
51 | | Bull. Belg. Math. Soc. 8 (2), 2001, pp. 181--207 |
52 | | |
53 | | .. [SAI] Y. Sano, P. Arnoux, S. Ito, |
54 | | Higher dimensional extensions of substitutions and their dual maps, |
55 | | J. Anal. Math. 83, 2001, pp. 183--206 |
56 | | |
57 | | EXAMPLES: |
58 | | |
59 | | We start by drawing a simple three-face patch:: |
60 | | |
61 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
62 | | sage: x = [Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)] |
63 | | sage: P = Patch(x) |
64 | | sage: P |
65 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
66 | | sage: P.plot() #not tested |
67 | | |
68 | | We apply a substitution to this patch, and draw the result:: |
69 | | |
70 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
71 | | sage: E = E1Star(sigma) |
72 | | sage: E(P) |
73 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*] |
74 | | sage: E(P).plot() #not tested |
75 | | |
76 | | .. NOTE:: |
77 | | |
78 | | - The type of a face is given by an integer in ``[1, ..., d]`` |
79 | | where ``d`` is the length of the vector of the face. |
80 | | |
81 | | - The alphabet of the domain and the codomain of `\sigma` must be |
82 | | equal, and they must be of the form ``[1, ..., d]``, where ``d`` |
83 | | is a positive integer corresponding to the length of the vectors |
84 | | of the faces on which `E_1^*(\sigma)` will act. |
85 | | |
86 | | :: |
87 | | |
88 | | sage: P = Patch([Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)]) |
89 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
90 | | sage: E = E1Star(sigma) |
91 | | sage: E(P) |
92 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*] |
93 | | |
94 | | The application of an ``E1Star`` substitution assigns to each new face the color of its preimage. |
95 | | The ``repaint`` method allows us to repaint the faces of a patch. |
96 | | A single color can also be assigned to every face, by specifying a list of a single color:: |
97 | | |
98 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
99 | | sage: P = E(P, 5) |
100 | | sage: P.repaint(['green']) |
101 | | sage: P.plot() #not tested |
102 | | |
103 | | A list of colors allows us to color the faces sequentially:: |
104 | | |
105 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
106 | | sage: P = E(P) |
107 | | sage: P.repaint(['red', 'yellow', 'green', 'blue', 'black']) |
108 | | sage: P = E(P, 3) |
109 | | sage: P.plot() #not tested |
110 | | |
111 | | All the color schemes from ``matplotlib.cm.datad.keys()`` can be used:: |
112 | | |
113 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
114 | | sage: P.repaint(cmap='summer') |
115 | | sage: P = E(P, 3) |
116 | | sage: P.plot() #not tested |
117 | | sage: P.repaint(cmap='hsv') |
118 | | sage: P = E(P, 2) |
119 | | sage: P.plot() #not tested |
120 | | |
121 | | It is also possible to specify a dictionary to color the faces according to their type:: |
122 | | |
123 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
124 | | sage: P = E(P, 5) |
125 | | sage: P.repaint({1:(0.7, 0.7, 0.7), 2:(0.5,0.5,0.5), 3:(0.3,0.3,0.3)}) |
126 | | sage: P.plot() #not tested |
127 | | sage: P.repaint({1:'red', 2:'yellow', 3:'green'}) |
128 | | sage: P.plot() #not tested |
129 | | |
130 | | Let us look at a nice big patch in 3D:: |
131 | | |
132 | | sage: sigma = WordMorphism({1:[1,2], 2:[3], 3:[1]}) |
133 | | sage: E = E1Star(sigma) |
134 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
135 | | sage: P = P + P.translate([-1,1,0]) |
136 | | sage: P = E(P, 11) |
137 | | sage: P.plot3d() #not tested |
138 | | |
139 | | Plotting with TikZ pictures is possible:: |
140 | | |
141 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
142 | | sage: s = P.plot_tikz() |
143 | | sage: print s #not tested |
144 | | \begin{tikzpicture} |
145 | | [x={(-0.216506cm,-0.125000cm)}, y={(0.216506cm,-0.125000cm)}, z={(0.000000cm,0.250000cm)}] |
146 | | \definecolor{facecolor}{rgb}{0.000,1.000,0.000} |
147 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
148 | | (0, 0, 0) -- (0, 0, 1) -- (1, 0, 1) -- (1, 0, 0) -- cycle; |
149 | | \definecolor{facecolor}{rgb}{1.000,0.000,0.000} |
150 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
151 | | (0, 0, 0) -- (0, 1, 0) -- (0, 1, 1) -- (0, 0, 1) -- cycle; |
152 | | \definecolor{facecolor}{rgb}{0.000,0.000,1.000} |
153 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
154 | | (0, 0, 0) -- (1, 0, 0) -- (1, 1, 0) -- (0, 1, 0) -- cycle; |
155 | | \end{tikzpicture} |
156 | | |
157 | | Plotting patches made of unit segments instead of unit faces:: |
158 | | |
159 | | sage: P = Patch([Face([0,0], 1), Face([0,0], 2)]) |
160 | | sage: E = E1Star(WordMorphism({1:[1,2],2:[1]})) |
161 | | sage: F = E1Star(WordMorphism({1:[1,1,2],2:[2,1]})) |
162 | | sage: E(P,5).plot() |
163 | | sage: F(P,3).plot() |
164 | | |
165 | | Everything works in any dimension (except for the plotting features |
166 | | which only work in dimension two or three):: |
167 | | |
168 | | sage: P = Patch([Face((0,0,0,0),1), Face((0,0,0,0),4)]) |
169 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1,4], 4:[1]}) |
170 | | sage: E = E1Star(sigma) |
171 | | sage: E(P) |
172 | | Patch: [[(0, 0, 0, 0), 3]*, [(0, 0, 0, 0), 4]*, [(0, 0, 1, -1), 3]*, [(0, 1, 0, -1), 2]*, [(1, 0, 0, -1), 1]*] |
173 | | |
174 | | :: |
175 | | |
176 | | sage: sigma = WordMorphism({1:[1,2],2:[1,3],3:[1,4],4:[1,5],5:[1,6],6:[1,7],7:[1,8],8:[1,9],9:[1,10],10:[1,11],11:[1,12],12:[1]}) |
177 | | sage: E = E1Star(sigma) |
178 | | sage: E |
179 | | E_1^*(WordMorphism: 1->12, 10->1,11, 11->1,12, 12->1, 2->13, 3->14, 4->15, 5->16, 6->17, 7->18, 8->19, 9->1,10) |
180 | | sage: P = Patch([Face((0,0,0,0,0,0,0,0,0,0,0,0),t) for t in [1,2,3]]) |
181 | | sage: for x in sorted(list(E(P)), key=lambda x : (x.vector(),x.type())): print x |
182 | | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 1]* |
183 | | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 2]* |
184 | | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 12]* |
185 | | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1), 11]* |
186 | | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1), 10]* |
187 | | [(0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1), 9]* |
188 | | [(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1), 8]* |
189 | | [(0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1), 7]* |
190 | | [(0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -1), 6]* |
191 | | [(0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1), 5]* |
192 | | [(0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1), 4]* |
193 | | [(0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, -1), 3]* |
194 | | [(0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1), 2]* |
195 | | [(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1), 1]* |
196 | | """ |
197 | | #***************************************************************************** |
198 | | # Copyright (C) 2010 Franco Saliola <saliola@gmail.com> |
199 | | # Vincent Delecroix <20100.delecroix@gmail.com> |
200 | | # Timo Jolivet <timo.jolivet@gmail.com> |
201 | | # Stepan Starosta <stepan.starosta@gmail.com> |
202 | | # Sebastien Labbe <slabqc at gmail.com> |
203 | | # |
204 | | # Distributed under the terms of the GNU General Public License (GPL) |
205 | | # as published by the Free Software Foundation; either version 2 of |
206 | | # the License, or (at your option) any later version. |
207 | | # http://www.gnu.org/licenses/ |
208 | | #***************************************************************************** |
209 | | from sage.misc.functional import det |
210 | | from sage.structure.sage_object import SageObject |
211 | | from sage.combinat.words.morphism import WordMorphism |
212 | | from sage.matrix.constructor import matrix |
213 | | from sage.modules.free_module_element import vector |
214 | | from sage.plot.plot import Graphics |
215 | | from sage.plot.plot import rainbow |
216 | | from sage.plot.colors import Color |
217 | | from sage.plot.polygon import polygon |
218 | | from sage.plot.line import line |
219 | | from sage.rings.integer_ring import ZZ |
220 | | from sage.misc.latex import LatexExpr |
221 | | from sage.misc.lazy_attribute import lazy_attribute |
222 | | from sage.misc.cachefunc import cached_method |
223 | | |
224 | | # matplotlib color maps, loaded on-demand |
225 | | cm = None |
226 | | |
227 | | class Face(SageObject): |
228 | | r""" |
229 | | A class to model a unit face of arbitrary dimension. |
230 | | |
231 | | A unit face in dimension `d` is represented by |
232 | | a `d`-dimensional vector ``v`` and a type ``t`` in `\{1, \ldots, d\}`. |
233 | | The type of the face corresponds to the canonical unit vector |
234 | | to which the face is orthogonal. |
235 | | The optional ``color`` argument is used in plotting functions. |
236 | | |
237 | | INPUT: |
238 | | |
239 | | - ``v`` - tuple of integers |
240 | | - ``t`` - integer in ``[1, ..., len(v)]``, type of the face. The face of type `i` |
241 | | is orthogonal to the canonical vector `e_i`. |
242 | | - ``color`` - color (optional, default: ``None``) color of the face, |
243 | | used for plotting only. If ``None``, its value is guessed from the |
244 | | face type. |
245 | | |
246 | | EXAMPLES:: |
247 | | |
248 | | sage: from sage.combinat.e_one_star import Face |
249 | | sage: f = Face((0,2,0), 3) |
250 | | sage: f.vector() |
251 | | (0, 2, 0) |
252 | | sage: f.type() |
253 | | 3 |
254 | | |
255 | | :: |
256 | | |
257 | | sage: f = Face((0,2,0), 3, color=(0.5, 0.5, 0.5)) |
258 | | sage: f.color() |
259 | | RGB color (0.5, 0.5, 0.5) |
260 | | """ |
261 | | def __init__(self, v, t, color=None): |
262 | | r""" |
263 | | Face constructor. See class doc for more information. |
264 | | |
265 | | EXAMPLES:: |
266 | | |
267 | | sage: from sage.combinat.e_one_star import Face |
268 | | sage: f = Face((0,2,0), 3) |
269 | | sage: f.vector() |
270 | | (0, 2, 0) |
271 | | sage: f.type() |
272 | | 3 |
273 | | |
274 | | TEST: |
275 | | |
276 | | We test that types can be given by an int (see #10699):: |
277 | | |
278 | | sage: f = Face((0,2,0), int(1)) |
279 | | """ |
280 | | self._vector = (ZZ**len(v))(v) |
281 | | self._vector.set_immutable() |
282 | | |
283 | | if not((t in ZZ) and 1 <= t <= len(v)): |
284 | | raise ValueError, 'The type must be an integer between 1 and len(v)' |
285 | | self._type = t |
286 | | |
287 | | if color is None: |
288 | | if self._type == 1: |
289 | | color = Color((1,0,0)) |
290 | | elif self._type == 2: |
291 | | color = Color((0,1,0)) |
292 | | elif self._type == 3: |
293 | | color = Color((0,0,1)) |
294 | | else: |
295 | | color = Color() |
296 | | self._color = Color(color) |
297 | | |
298 | | def __repr__(self): |
299 | | r""" |
300 | | String representation of a face. |
301 | | |
302 | | EXAMPLES:: |
303 | | |
304 | | sage: from sage.combinat.e_one_star import Face |
305 | | sage: f = Face((0,0,0,3), 3) |
306 | | sage: f |
307 | | [(0, 0, 0, 3), 3]* |
308 | | |
309 | | :: |
310 | | |
311 | | sage: f = Face((0,0,0,3), 3) |
312 | | sage: f |
313 | | [(0, 0, 0, 3), 3]* |
314 | | """ |
315 | | return "[%s, %s]*"%(self.vector(), self.type()) |
316 | | |
317 | | def __eq__(self, other): |
318 | | r""" |
319 | | Equality of faces. |
320 | | |
321 | | EXAMPLES:: |
322 | | |
323 | | sage: from sage.combinat.e_one_star import Face |
324 | | sage: f = Face((0,0,0,3), 3) |
325 | | sage: g = Face((0,0,0,3), 3) |
326 | | sage: f == g |
327 | | True |
328 | | """ |
329 | | return (isinstance(other, Face) and |
330 | | self.vector() == other.vector() and |
331 | | self.type() == other.type() ) |
332 | | |
333 | | def __cmp__(self, other): |
334 | | r""" |
335 | | Compare self and other, returning -1, 0, or 1, depending on if |
336 | | self < other, self == other, or self > other, respectively. |
337 | | |
338 | | The vectors of the faces are first compared, |
339 | | and the types of the faces are compared if the vectors are equal. |
340 | | |
341 | | EXAMPLES:: |
342 | | |
343 | | sage: from sage.combinat.e_one_star import Face |
344 | | sage: Face([-2,1,0], 2) < Face([-1,2,2],3) |
345 | | True |
346 | | sage: Face([-2,1,0], 2) < Face([-2,1,0],3) |
347 | | True |
348 | | sage: Face([-2,1,0], 2) < Face([-2,1,0],2) |
349 | | False |
350 | | """ |
351 | | v1 = self.vector() |
352 | | v2 = other.vector() |
353 | | t1 = self.type() |
354 | | t2 = other.type() |
355 | | |
356 | | if v1 < v2: |
357 | | return -1 |
358 | | elif v1 > v2: |
359 | | return 1 |
360 | | else: |
361 | | return t1.__cmp__(t2) |
362 | | |
363 | | def __hash__(self): |
364 | | r""" |
365 | | EXAMPLES:: |
366 | | |
367 | | sage: from sage.combinat.e_one_star import Face |
368 | | sage: f = Face((0,0,0,3), 3) |
369 | | sage: g = Face((0,0,0,3), 3) |
370 | | sage: hash(f) == hash(g) |
371 | | True |
372 | | """ |
373 | | return hash((self.vector(), self.type())) |
374 | | |
375 | | def __add__(self, other): |
376 | | r""" |
377 | | Addition of self with a Face, a Patch or a finite iterable of faces. |
378 | | |
379 | | INPUT: |
380 | | |
381 | | - ``other`` - a Patch or a Face or a finite iterable of faces |
382 | | |
383 | | EXAMPLES:: |
384 | | |
385 | | sage: from sage.combinat.e_one_star import Face, Patch |
386 | | sage: f = Face([0,0,0], 3) |
387 | | sage: g = Face([0,1,-1], 2) |
388 | | sage: f + g |
389 | | Patch: [[(0, 0, 0), 3]*, [(0, 1, -1), 2]*] |
390 | | sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2)]) |
391 | | sage: f + P |
392 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
393 | | |
394 | | Adding a finite iterable of faces:: |
395 | | |
396 | | sage: from sage.combinat.e_one_star import Face |
397 | | sage: f = Face([0,0,0], 3) |
398 | | sage: f + [f,f] |
399 | | Patch: [[(0, 0, 0), 3]*] |
400 | | """ |
401 | | if isinstance(other, Face): |
402 | | return Patch([self, other]) |
403 | | else: |
404 | | return Patch(other).union(self) |
405 | | |
406 | | def vector(self): |
407 | | r""" |
408 | | Return the vector of the face. |
409 | | |
410 | | EXAMPLES:: |
411 | | |
412 | | sage: from sage.combinat.e_one_star import Face |
413 | | sage: f = Face((0,2,0), 3) |
414 | | sage: f.vector() |
415 | | (0, 2, 0) |
416 | | """ |
417 | | return self._vector |
418 | | |
419 | | def type(self): |
420 | | r""" |
421 | | Returns the type of the face. |
422 | | |
423 | | EXAMPLES:: |
424 | | |
425 | | sage: from sage.combinat.e_one_star import Face |
426 | | sage: f = Face((0,2,0), 3) |
427 | | sage: f.type() |
428 | | 3 |
429 | | |
430 | | :: |
431 | | |
432 | | sage: f = Face((0,2,0), 3) |
433 | | sage: f.type() |
434 | | 3 |
435 | | """ |
436 | | return self._type |
437 | | |
438 | | def color(self, color=None): |
439 | | r""" |
440 | | Returns or change the color of the face. |
441 | | |
442 | | INPUT: |
443 | | |
444 | | - ``color`` - string, rgb tuple, color (optional, default: ``None``) |
445 | | the new color to assign to the face. If ``None``, it returns the |
446 | | color of the face. |
447 | | |
448 | | OUTPUT: |
449 | | |
450 | | color |
451 | | |
452 | | EXAMPLES:: |
453 | | |
454 | | sage: from sage.combinat.e_one_star import Face |
455 | | sage: f = Face((0,2,0), 3) |
456 | | sage: f.color() |
457 | | RGB color (0.0, 0.0, 1.0) |
458 | | sage: f.color('red') |
459 | | sage: f.color() |
460 | | RGB color (1.0, 0.0, 0.0) |
461 | | |
462 | | """ |
463 | | if color is None: |
464 | | return self._color |
465 | | else: |
466 | | self._color = Color(color) |
467 | | |
468 | | def _plot(self, projmat, face_contour, opacity): |
469 | | r""" |
470 | | Returns a 2D graphic object representing the face. |
471 | | |
472 | | INPUT: |
473 | | |
474 | | - ``projmat`` - 2*3 projection matrix (used only for faces in three dimensions) |
475 | | - ``face_contour`` - dict, maps the face type to vectors describing |
476 | | the contour of unit faces (used only for faces in three dimensions) |
477 | | - ``opacity`` - the alpha value for the color of the face |
478 | | |
479 | | OUTPUT: |
480 | | |
481 | | 2D graphic object |
482 | | |
483 | | EXAMPLES:: |
484 | | |
485 | | sage: from sage.combinat.e_one_star import Face |
486 | | sage: f = Face((0,0,3), 3) |
487 | | sage: projmat = matrix(2, [-1.7320508075688772*0.5, 1.7320508075688772*0.5, 0, -0.5, -0.5, 1]) |
488 | | sage: face_contour = {} |
489 | | sage: face_contour[1] = map(vector, [(0,0,0),(0,1,0),(0,1,1),(0,0,1)]) |
490 | | sage: face_contour[2] = map(vector, [(0,0,0),(0,0,1),(1,0,1),(1,0,0)]) |
491 | | sage: face_contour[3] = map(vector, [(0,0,0),(1,0,0),(1,1,0),(0,1,0)]) |
492 | | sage: G = f._plot(projmat, face_contour, 0.75) |
493 | | |
494 | | :: |
495 | | |
496 | | sage: f = Face((0,0), 2) |
497 | | sage: f._plot(None, None, 1) |
498 | | """ |
499 | | v = self.vector() |
500 | | t = self.type() |
501 | | G = Graphics() |
502 | | |
503 | | if len(v) == 2: |
504 | | if t == 1: |
505 | | G += line([v, v + vector([0,1])], rgbcolor=self.color(), thickness=1.5, alpha=opacity) |
506 | | elif t == 2: |
507 | | G += line([v, v + vector([1,0])], rgbcolor=self.color(), thickness=1.5, alpha=opacity) |
508 | | |
509 | | elif len(v) == 3: |
510 | | G += polygon([projmat*(u+v) for u in face_contour[t]], alpha=opacity, |
511 | | thickness=1, rgbcolor=self.color()) |
512 | | |
513 | | else: |
514 | | raise NotImplementedError, "Plotting is implemented only for patches in two or three dimensions." |
515 | | |
516 | | return G |
517 | | |
518 | | def _plot3d(self, face_contour): |
519 | | r""" |
520 | | 3D reprensentation of a unit face (Jmol). |
521 | | |
522 | | INPUT: |
523 | | |
524 | | - ``face_contour`` - dict, maps the face type to vectors describing |
525 | | the contour of unit faces |
526 | | |
527 | | EXAMPLES:: |
528 | | |
529 | | sage: from sage.combinat.e_one_star import Face |
530 | | sage: f = Face((0,0,3), 3) |
531 | | sage: face_contour = {1: map(vector, [(0,0,0),(0,1,0),(0,1,1),(0,0,1)]), 2: map(vector, [(0,0,0),(0,0,1),(1,0,1),(1,0,0)]), 3: map(vector, [(0,0,0),(1,0,0),(1,1,0),(0,1,0)])} |
532 | | sage: G = f._plot3d(face_contour) #not tested |
533 | | """ |
534 | | v = self.vector() |
535 | | t = self.type() |
536 | | c = self.color() |
537 | | G = polygon([u+v for u in face_contour[t]], rgbcolor=c) |
538 | | return G |
539 | | |
540 | | class Patch(SageObject): |
541 | | r""" |
542 | | A class to model a collection of faces. A patch is represented by an immutable set of Faces. |
543 | | |
544 | | .. NOTE:: |
545 | | |
546 | | The dimension of a patch is the length of the vectors of the faces in the patch, |
547 | | which is assumed to be the same for every face in the patch. |
548 | | |
549 | | .. NOTE:: |
550 | | |
551 | | Since version 4.7.1, Patches are immutable, except for the colors of the faces, |
552 | | which are not taken into account for equality tests and hash functions. |
553 | | |
554 | | INPUT: |
555 | | |
556 | | - ``faces`` - finite iterable of faces |
557 | | - ``face_contour`` - dict (optional, default:``None``) maps the face |
558 | | type to vectors describing the contour of unit faces. If None, |
559 | | defaults contour are assumed for faces of type 1, 2, 3 or 1, 2, 3. |
560 | | Used in plotting methods only. |
561 | | |
562 | | EXAMPLES:: |
563 | | |
564 | | sage: from sage.combinat.e_one_star import Face, Patch |
565 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
566 | | sage: P |
567 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
568 | | |
569 | | :: |
570 | | |
571 | | sage: face_contour = {} |
572 | | sage: face_contour[1] = map(vector, [(0,0,0),(0,1,0),(0,1,1),(0,0,1)]) |
573 | | sage: face_contour[2] = map(vector, [(0,0,0),(0,0,1),(1,0,1),(1,0,0)]) |
574 | | sage: face_contour[3] = map(vector, [(0,0,0),(1,0,0),(1,1,0),(0,1,0)]) |
575 | | sage: Patch([Face((0,0,0),t) for t in [1,2,3]], face_contour=face_contour) |
576 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
577 | | """ |
578 | | def __init__(self, faces, face_contour=None): |
579 | | r""" |
580 | | Constructor of a patch (set of faces). See class doc for more information. |
581 | | |
582 | | EXAMPLES:: |
583 | | |
584 | | sage: from sage.combinat.e_one_star import Face, Patch |
585 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
586 | | sage: P |
587 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
588 | | |
589 | | TEST: |
590 | | |
591 | | We test that colors are not anymore mixed up between Patches (see #11255):: |
592 | | |
593 | | sage: P = Patch([Face([0,0,0],2)]) |
594 | | sage: Q = Patch(P) |
595 | | sage: next(iter(P)).color() |
596 | | RGB color (0.0, 1.0, 0.0) |
597 | | sage: next(iter(Q)).color('yellow') |
598 | | sage: next(iter(P)).color() |
599 | | RGB color (0.0, 1.0, 0.0) |
600 | | |
601 | | """ |
602 | | self._faces = frozenset(Face(f.vector(), f.type(), f.color()) for f in faces) |
603 | | |
604 | | try: |
605 | | f0 = next(iter(self._faces)) |
606 | | except StopIteration: |
607 | | self._dimension = None |
608 | | else: |
609 | | self._dimension = len(f0.vector()) |
610 | | |
611 | | if not face_contour is None: |
612 | | self._face_contour = face_contour |
613 | | |
614 | | else: |
615 | | self._face_contour = { |
616 | | 1: map(vector, [(0,0,0),(0,1,0),(0,1,1),(0,0,1)]), |
617 | | 2: map(vector, [(0,0,0),(0,0,1),(1,0,1),(1,0,0)]), |
618 | | 3: map(vector, [(0,0,0),(1,0,0),(1,1,0),(0,1,0)]) |
619 | | } |
620 | | |
621 | | def __eq__(self, other): |
622 | | r""" |
623 | | Equality test for Patch. |
624 | | |
625 | | INPUT: |
626 | | |
627 | | - ``other`` - an object |
628 | | |
629 | | EXAMPLES:: |
630 | | |
631 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
632 | | sage: P = Patch([Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)]) |
633 | | sage: Q = Patch([Face((0,1,0),1), Face((0,0,0),3)]) |
634 | | sage: P == P |
635 | | True |
636 | | sage: P == Q |
637 | | False |
638 | | sage: P == 4 |
639 | | False |
640 | | |
641 | | :: |
642 | | |
643 | | sage: s = WordMorphism({1:[1,3], 2:[1,2,3], 3:[3]}) |
644 | | sage: t = WordMorphism({1:[1,2,3], 2:[2,3], 3:[3]}) |
645 | | sage: P = Patch([Face((0,0,0), 1), Face((0,0,0), 2), Face((0,0,0), 3)]) |
646 | | sage: E1Star(s)(P) == E1Star(t)(P) |
647 | | False |
648 | | sage: E1Star(s*t)(P) == E1Star(t)(E1Star(s)(P)) |
649 | | True |
650 | | """ |
651 | | return (isinstance(other, Patch) and self._faces == other._faces) |
652 | | |
653 | | def __hash__(self): |
654 | | r""" |
655 | | Hash function of Patch. |
656 | | |
657 | | EXAMPLES:: |
658 | | |
659 | | sage: from sage.combinat.e_one_star import Face, Patch |
660 | | sage: x = [Face((0,0,0),t) for t in [1,2,3]] |
661 | | sage: P = Patch(x) |
662 | | sage: hash(P) #random |
663 | | -4839605361791007520 |
664 | | |
665 | | TEST: |
666 | | |
667 | | We test that two equal patches have the same hash (see #11255):: |
668 | | |
669 | | sage: P = Patch([Face([0,0,0],1), Face([0,0,0],2)]) |
670 | | sage: Q = Patch([Face([0,0,0],2), Face([0,0,0],1)]) |
671 | | sage: P == Q |
672 | | True |
673 | | sage: hash(P) == hash(Q) |
674 | | True |
675 | | |
676 | | Changing the color does not affect the hash value:: |
677 | | |
678 | | sage: p = Patch([Face((0,0,0), t) for t in [1,2,3]]) |
679 | | sage: H1 = hash(p) |
680 | | sage: p.repaint(['blue']) |
681 | | sage: H2 = hash(p) |
682 | | sage: H1 == H2 |
683 | | True |
684 | | """ |
685 | | return hash(self._faces) |
686 | | |
687 | | def __len__(self): |
688 | | r""" |
689 | | Returns the number of faces contained in the patch. |
690 | | |
691 | | OUPUT: |
692 | | |
693 | | integer |
694 | | |
695 | | EXAMPLES:: |
696 | | |
697 | | sage: from sage.combinat.e_one_star import Face, Patch |
698 | | sage: x = [Face((0,0,0),t) for t in [1,2,3]] |
699 | | sage: P = Patch(x) |
700 | | sage: len(P) #indirect doctest |
701 | | 3 |
702 | | """ |
703 | | return len(self._faces) |
704 | | |
705 | | def __iter__(self): |
706 | | r""" |
707 | | Return an iterator over the faces of the patch. |
708 | | |
709 | | OUTPUT: |
710 | | |
711 | | iterator |
712 | | |
713 | | EXAMPLES:: |
714 | | |
715 | | sage: from sage.combinat.e_one_star import Face, Patch |
716 | | sage: x = [Face((0,0,0),t) for t in [1,2,3]] |
717 | | sage: P = Patch(x) |
718 | | sage: it = iter(P) |
719 | | sage: type(it.next()) |
720 | | <class 'sage.combinat.e_one_star.Face'> |
721 | | sage: type(it.next()) |
722 | | <class 'sage.combinat.e_one_star.Face'> |
723 | | sage: type(it.next()) |
724 | | <class 'sage.combinat.e_one_star.Face'> |
725 | | sage: type(it.next()) |
726 | | Traceback (most recent call last): |
727 | | ... |
728 | | StopIteration |
729 | | """ |
730 | | return iter(self._faces) |
731 | | |
732 | | def __add__(self, other): |
733 | | r""" |
734 | | Addition of patches (union). |
735 | | |
736 | | INPUT: |
737 | | |
738 | | - ``other`` - a Patch or a Face or a finite iterable of faces |
739 | | |
740 | | EXAMPLES:: |
741 | | |
742 | | sage: from sage.combinat.e_one_star import Face, Patch |
743 | | sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2)]) |
744 | | sage: Q = P.translate([1,-1,0]) |
745 | | sage: P + Q |
746 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, -1, 0), 1]*, [(1, -1, 0), 2]*] |
747 | | sage: P + Face([0,0,0],3) |
748 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
749 | | sage: P + [Face([0,0,0],3), Face([1,1,1],2)] |
750 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(1, 1, 1), 2]*] |
751 | | """ |
752 | | return self.union(other) |
753 | | |
754 | | def __sub__(self, other): |
755 | | r""" |
756 | | Subtraction of patches (difference). |
757 | | |
758 | | INPUT: |
759 | | |
760 | | - ``other`` - a Patch or a Face or a finite iterable of faces |
761 | | |
762 | | EXAMPLES:: |
763 | | |
764 | | sage: from sage.combinat.e_one_star import Face, Patch |
765 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
766 | | sage: P - Face([0,0,0],2) |
767 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 3]*] |
768 | | sage: P - P |
769 | | Patch: [] |
770 | | """ |
771 | | return self.difference(other) |
772 | | |
773 | | def __repr__(self): |
774 | | r""" |
775 | | String representation of a patch. |
776 | | |
777 | | Displays all the faces if there less than 20, |
778 | | otherwise displays only the number of faces. |
779 | | |
780 | | EXAMPLES:: |
781 | | |
782 | | sage: from sage.combinat.e_one_star import Face, Patch |
783 | | sage: x = [Face((0,0,0),t) for t in [1,2,3]] |
784 | | sage: P = Patch(x) |
785 | | sage: P |
786 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
787 | | |
788 | | :: |
789 | | |
790 | | sage: x = [Face((0,0,a),1) for a in range(25)] |
791 | | sage: P = Patch(x) |
792 | | sage: P |
793 | | Patch of 25 faces |
794 | | """ |
795 | | if len(self) <= 20: |
796 | | L = list(self) |
797 | | L.sort(key=lambda x : (x.vector(),x.type())) |
798 | | return "Patch: %s"%L |
799 | | else: |
800 | | return "Patch of %s faces"%len(self) |
801 | | |
802 | | def add(self, other): |
803 | | r""" |
804 | | Returns a Patch consisting of the union of self and other. |
805 | | |
806 | | INPUT: |
807 | | |
808 | | - ``other`` - a Patch or a Face or a finite iterable of faces |
809 | | |
810 | | EXAMPLES:: |
811 | | |
812 | | sage: from sage.combinat.e_one_star import Face, Patch |
813 | | sage: P = Patch([Face((0,0,0),1), Face((0,0,0),2)]) |
814 | | sage: P.add(Face((1,2,3), 3)) #not tested |
815 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*] |
816 | | sage: P.add([Face((1,2,3), 3), Face((2,3,3), 2)]) #not tested |
817 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*, [(2, 3, 3), 2]*] |
818 | | """ |
819 | | from sage.misc.misc import deprecation |
820 | | deprecation("Objects sage.combinat.e_one_star.Patch " + \ |
821 | | "are immutable since Sage-4.7.1. " + \ |
822 | | "Use the usual addition (P + Q) or use the Patch.union method.") |
823 | | return self.union(other) |
824 | | |
825 | | def union(self, other): |
826 | | r""" |
827 | | Returns a Patch consisting of the union of self and other. |
828 | | |
829 | | INPUT: |
830 | | |
831 | | - ``other`` - a Patch or a Face or a finite iterable of faces |
832 | | |
833 | | EXAMPLES:: |
834 | | |
835 | | sage: from sage.combinat.e_one_star import Face, Patch |
836 | | sage: P = Patch([Face((0,0,0),1), Face((0,0,0),2)]) |
837 | | sage: P.union(Face((1,2,3), 3)) |
838 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*] |
839 | | sage: P.union([Face((1,2,3), 3), Face((2,3,3), 2)]) |
840 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*, [(2, 3, 3), 2]*] |
841 | | """ |
842 | | if isinstance(other, Face): |
843 | | return Patch(self._faces.union([other])) |
844 | | else: |
845 | | return Patch(self._faces.union(other)) |
846 | | |
847 | | def difference(self, other): |
848 | | r""" |
849 | | Returns the difference of self and other. |
850 | | |
851 | | INPUT: |
852 | | |
853 | | - ``other`` - a finite iterable of faces or a single face |
854 | | |
855 | | EXAMPLES:: |
856 | | |
857 | | sage: from sage.combinat.e_one_star import Face, Patch |
858 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
859 | | sage: P.difference(Face([0,0,0],2)) |
860 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 3]*] |
861 | | sage: P.difference(P) |
862 | | Patch: [] |
863 | | """ |
864 | | if isinstance(other, Face): |
865 | | return Patch(self._faces.difference([other])) |
866 | | else: |
867 | | return Patch(self._faces.difference(other)) |
868 | | |
869 | | def dimension(self): |
870 | | r""" |
871 | | Returns the dimension of the vectors of the faces of self |
872 | | |
873 | | It returns ``None`` if self is the empty patch. |
874 | | |
875 | | The dimension of a patch is the length of the vectors of the faces in the patch, |
876 | | which is assumed to be the same for every face in the patch. |
877 | | |
878 | | EXAMPLES:: |
879 | | |
880 | | sage: from sage.combinat.e_one_star import Face, Patch |
881 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
882 | | sage: P.dimension() |
883 | | 3 |
884 | | |
885 | | TESTS:: |
886 | | |
887 | | sage: from sage.combinat.e_one_star import Patch |
888 | | sage: p = Patch([]) |
889 | | sage: p.dimension() is None |
890 | | True |
891 | | |
892 | | It works when the patch is created from an iterator:: |
893 | | |
894 | | sage: p = Patch(Face((0,0,0),t) for t in [1,2,3]) |
895 | | sage: p.dimension() |
896 | | 3 |
897 | | """ |
898 | | return self._dimension |
899 | | |
900 | | def faces_of_vector(self, v): |
901 | | r""" |
902 | | Returns a list of the faces whose vector is ``v``. |
903 | | |
904 | | INPUT: |
905 | | |
906 | | - ``v`` - a vector |
907 | | |
908 | | EXAMPLES:: |
909 | | |
910 | | sage: from sage.combinat.e_one_star import Face, Patch |
911 | | sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)]) |
912 | | sage: P.faces_of_vector([1,2,0]) |
913 | | [[(1, 2, 0), 3]*, [(1, 2, 0), 1]*] |
914 | | """ |
915 | | v = vector(v) |
916 | | return [f for f in self if f.vector() == v] |
917 | | |
918 | | def faces_of_type(self, t): |
919 | | r""" |
920 | | Returns a list of the faces that have type ``t``. |
921 | | |
922 | | INPUT: |
923 | | |
924 | | - ``t`` - integer or any other type |
925 | | |
926 | | EXAMPLES:: |
927 | | |
928 | | sage: from sage.combinat.e_one_star import Face, Patch |
929 | | sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)]) |
930 | | sage: P.faces_of_type(1) |
931 | | [[(0, 0, 0), 1]*, [(1, 2, 0), 1]*] |
932 | | """ |
933 | | return [f for f in self if f.type() == t] |
934 | | |
935 | | def faces_of_color(self, color): |
936 | | r""" |
937 | | Returns a list of the faces that have the given color. |
938 | | |
939 | | INPUT: |
940 | | |
941 | | - ``color`` - color |
942 | | |
943 | | EXAMPLES:: |
944 | | |
945 | | sage: from sage.combinat.e_one_star import Face, Patch |
946 | | sage: P = Patch([Face((0,0,0),1, 'red'), Face((1,2,0),3, 'blue'), Face((1,2,0),1, 'red')]) |
947 | | sage: P.faces_of_color('red') |
948 | | [[(0, 0, 0), 1]*, [(1, 2, 0), 1]*] |
949 | | """ |
950 | | color = tuple(Color(color)) |
951 | | return [f for f in self if tuple(f.color()) == color] |
952 | | |
953 | | def translate(self, v): |
954 | | r""" |
955 | | Returns a translated copy of self by vector ``v``. |
956 | | |
957 | | INPUT: |
958 | | |
959 | | - ``v`` - vector or tuple |
960 | | |
961 | | EXAMPLES:: |
962 | | |
963 | | sage: from sage.combinat.e_one_star import Face, Patch |
964 | | sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)]) |
965 | | sage: P.translate([-1,-2,0]) |
966 | | Patch: [[(-1, -2, 0), 1]*, [(0, 0, 0), 1]*, [(0, 0, 0), 3]*] |
967 | | """ |
968 | | v = vector(v) |
969 | | return Patch(Face(f.vector()+v, f.type(), f.color()) for f in self) |
970 | | |
971 | | def occurrences_of(self, other): |
972 | | r""" |
973 | | Returns all positions at which other appears in self, that is, |
974 | | all vectors v such that ``set(other.translate(v)) <= set(self)``. |
975 | | |
976 | | INPUT: |
977 | | |
978 | | - ``other`` - a Patch |
979 | | |
980 | | OUTPUT: |
981 | | |
982 | | a list of vectors |
983 | | |
984 | | EXAMPLES:: |
985 | | |
986 | | sage: from sage.combinat.e_one_star import Face, Patch, E1Star |
987 | | sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2), Face([0,0,0], 3)]) |
988 | | sage: Q = Patch([Face([0,0,0], 1), Face([0,0,0], 2)]) |
989 | | sage: P.occurrences_of(Q) |
990 | | [(0, 0, 0)] |
991 | | sage: Q = Q.translate([1,2,3]) |
992 | | sage: P.occurrences_of(Q) |
993 | | [(-1, -2, -3)] |
994 | | |
995 | | :: |
996 | | |
997 | | sage: E = E1Star(WordMorphism({1:[1,2], 2:[1,3], 3:[1]})) |
998 | | sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2), Face([0,0,0], 3)]) |
999 | | sage: P = E(P,4) |
1000 | | sage: Q = Patch([Face([0,0,0], 1), Face([0,0,0], 2)]) |
1001 | | sage: L = P.occurrences_of(Q) |
1002 | | sage: sorted(L) |
1003 | | [(0, 0, 0), (0, 0, 1), (0, 1, -1), (1, 0, -1), (1, 1, -3), (1, 1, -2)] |
1004 | | """ |
1005 | | f0 = next(iter(other)) |
1006 | | x = f0.vector() |
1007 | | t = f0.type() |
1008 | | L = self.faces_of_type(t) |
1009 | | positions = [] |
1010 | | for f in L: |
1011 | | y = f.vector() |
1012 | | if other.translate(y-x)._faces.issubset(self._faces): |
1013 | | positions.append(y-x) |
1014 | | return positions |
1015 | | |
1016 | | def apply_substitution(self, E, iterations=1): |
1017 | | r""" |
1018 | | Returns the image of self by the E1Star substitution ``E``. |
1019 | | |
1020 | | The color of every new face in the image is given the same color as its preimage. |
1021 | | |
1022 | | INPUT: |
1023 | | |
1024 | | - ``E`` - an instance of the ``E1Star`` class. Its domain alphabet must |
1025 | | be of the same size as the dimension of the ambient space of the |
1026 | | faces. |
1027 | | - ``iterations`` - integer (optional, default: 1) |
1028 | | number of times the substitution E is applied |
1029 | | |
1030 | | EXAMPLES:: |
1031 | | |
1032 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1033 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1034 | | sage: E = E1Star(sigma) |
1035 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1036 | | sage: P.apply_substitution(E,6) #not tested |
1037 | | Patch of 105 faces |
1038 | | """ |
1039 | | if not isinstance(E, E1Star): |
1040 | | raise TypeError, "E must be an instance of E1Star" |
1041 | | |
1042 | | from sage.misc.misc import deprecation |
1043 | | deprecation("Objects sage.combinat.e_one_star.Patch " + \ |
1044 | | "are immutable since Sage-4.7.1. " + \ |
1045 | | "Use the usual calling method of E1Star (E(P)).") |
1046 | | return E(self, iterations=iterations) |
1047 | | |
1048 | | def repaint(self, cmap='Set1'): |
1049 | | r""" |
1050 | | Repaints all the faces of self from the given color map. |
1051 | | |
1052 | | This only changes the colors of the faces of self. |
1053 | | |
1054 | | INPUT: |
1055 | | |
1056 | | - ``cmap`` - color map (default: ``'Set1'``). It can be one of the |
1057 | | following : |
1058 | | |
1059 | | - string - A coloring map. For available coloring map names type: |
1060 | | ``sorted(colormaps)`` |
1061 | | - list - a list of colors to assign cyclically to the faces. |
1062 | | A list of a single color colors all the faces with the same color. |
1063 | | - dict - a dict of face types mapped to colors, to color the |
1064 | | faces according to their type. |
1065 | | - ``{}``, the empty dict - shorcut for |
1066 | | ``{1:'red', 2:'green', 3:'blue'}``. |
1067 | | |
1068 | | EXAMPLES: |
1069 | | |
1070 | | Using a color map:: |
1071 | | |
1072 | | sage: from sage.combinat.e_one_star import Face, Patch |
1073 | | sage: color = (0, 0, 0) |
1074 | | sage: P = Patch([Face((0,0,0),t,color) for t in [1,2,3]]) |
1075 | | sage: for f in P: f.color() |
1076 | | RGB color (0.0, 0.0, 0.0) |
1077 | | RGB color (0.0, 0.0, 0.0) |
1078 | | RGB color (0.0, 0.0, 0.0) |
1079 | | sage: P.repaint() |
1080 | | sage: next(iter(P)).color() #random |
1081 | | RGB color (0.498..., 0.432..., 0.522...) |
1082 | | |
1083 | | Using a list of colors:: |
1084 | | |
1085 | | sage: P = Patch([Face((0,0,0),t,color) for t in [1,2,3]]) |
1086 | | sage: P.repaint([(0.9, 0.9, 0.9), (0.65,0.65,0.65), (0.4,0.4,0.4)]) |
1087 | | sage: for f in P: f.color() |
1088 | | RGB color (0.9, 0.9, 0.9) |
1089 | | RGB color (0.65, 0.65, 0.65) |
1090 | | RGB color (0.4, 0.4, 0.4) |
1091 | | |
1092 | | Using a dictionary to color faces according to their type:: |
1093 | | |
1094 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1095 | | sage: P.repaint({1:'black', 2:'yellow', 3:'green'}) |
1096 | | sage: P.plot() #not tested |
1097 | | sage: P.repaint({}) |
1098 | | sage: P.plot() #not tested |
1099 | | """ |
1100 | | if cmap == {}: |
1101 | | cmap = {1: 'red', 2:'green', 3:'blue'} |
1102 | | |
1103 | | if isinstance(cmap, dict): |
1104 | | for f in self: |
1105 | | f.color(cmap[f.type()]) |
1106 | | |
1107 | | elif isinstance(cmap, list): |
1108 | | L = len(cmap) |
1109 | | for i, f in enumerate(self): |
1110 | | f.color(cmap[i % L]) |
1111 | | |
1112 | | elif isinstance(cmap, str): |
1113 | | # matplotlib color maps |
1114 | | global cm |
1115 | | if not cm: |
1116 | | from matplotlib import cm |
1117 | | |
1118 | | if not cmap in cm.datad.keys(): |
1119 | | raise RuntimeError("Color map %s not known (type sorted(colors) for valid names)" % cmap) |
1120 | | cmap = cm.__dict__[cmap] |
1121 | | dim = float(len(self)) |
1122 | | for i,f in enumerate(self): |
1123 | | f.color(cmap(i/dim)[:3]) |
1124 | | |
1125 | | else: |
1126 | | raise TypeError, "Type of cmap (=%s) must be dict, list or str" %cmap |
1127 | | |
1128 | | def plot(self, projmat=None, opacity=0.75): |
1129 | | r""" |
1130 | | Returns a 2D graphic object depicting the patch. |
1131 | | |
1132 | | INPUT: |
1133 | | |
1134 | | - ``projmat`` - matrix (optional, default: ``None``) the projection |
1135 | | matrix. Its number of lines must be two. Its number of columns |
1136 | | must equal the dimension of the ambient space of the faces. If |
1137 | | ``None``, the isometric projection is used by default. |
1138 | | |
1139 | | - ``opacity`` - float between ``0`` and ``1`` (optional, default: ``0.75``) |
1140 | | opacity of the the face |
1141 | | |
1142 | | .. WARNING:: |
1143 | | |
1144 | | Plotting is implemented only for patches in two or three dimensions. |
1145 | | |
1146 | | EXAMPLES:: |
1147 | | |
1148 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1149 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1150 | | sage: P.plot() |
1151 | | |
1152 | | :: |
1153 | | |
1154 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1155 | | sage: E = E1Star(sigma) |
1156 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1157 | | sage: P = E(P, 5) |
1158 | | sage: P.plot() |
1159 | | |
1160 | | Plot with a different projection matrix:: |
1161 | | |
1162 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1163 | | sage: E = E1Star(sigma) |
1164 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1165 | | sage: M = matrix(2, 3, [1,0,-1,0.3,1,-3]) |
1166 | | sage: P = E(P, 3) |
1167 | | sage: P.plot(projmat=M) |
1168 | | |
1169 | | Plot patches made of unit segments:: |
1170 | | |
1171 | | sage: P = Patch([Face([0,0], 1), Face([0,0], 2)]) |
1172 | | sage: E = E1Star(WordMorphism({1:[1,2],2:[1]})) |
1173 | | sage: F = E1Star(WordMorphism({1:[1,1,2],2:[2,1]})) |
1174 | | sage: E(P,5).plot() |
1175 | | sage: F(P,3).plot() |
1176 | | """ |
1177 | | if self.dimension() == 2: |
1178 | | G = Graphics() |
1179 | | for face in self: |
1180 | | G += face._plot(None, None, 1) |
1181 | | G.set_aspect_ratio(1) |
1182 | | return G |
1183 | | |
1184 | | if self.dimension() == 3: |
1185 | | if projmat == None: |
1186 | | projmat = matrix(2, [-1.7320508075688772*0.5, 1.7320508075688772*0.5, 0, -0.5, -0.5, 1]) |
1187 | | |
1188 | | G = Graphics() |
1189 | | for face in self: |
1190 | | G += face._plot(projmat, self._face_contour, opacity) |
1191 | | G.set_aspect_ratio(1) |
1192 | | return G |
1193 | | |
1194 | | else: |
1195 | | raise NotImplementedError, "Plotting is implemented only for patches in two or three dimensions." |
1196 | | |
1197 | | def plot3d(self): |
1198 | | r""" |
1199 | | Returns a 3D graphics object depicting the patch. |
1200 | | |
1201 | | .. WARNING:: |
1202 | | |
1203 | | 3D plotting is implemented only for patches in three dimensions. |
1204 | | |
1205 | | EXAMPLES:: |
1206 | | |
1207 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1208 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1209 | | sage: P.plot3d() #not tested |
1210 | | |
1211 | | :: |
1212 | | |
1213 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1214 | | sage: E = E1Star(sigma) |
1215 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1216 | | sage: P = E(P, 5) |
1217 | | sage: P.repaint() |
1218 | | sage: P.plot3d() #not tested |
1219 | | """ |
1220 | | if self.dimension() != 3: |
1221 | | raise NotImplementedError, "3D plotting is implemented only for patches in three dimensions." |
1222 | | |
1223 | | face_list = [face._plot3d(self._face_contour) for face in self] |
1224 | | G = sum(face_list) |
1225 | | return G |
1226 | | |
1227 | | def plot_tikz(self, projmat=None, print_tikz_env=True, edgecolor='black', |
1228 | | scale=0.25, drawzero=False, extra_code_before='', extra_code_after=''): |
1229 | | r""" |
1230 | | Returns a string containing some TikZ code to be included into |
1231 | | a LaTeX document, depicting the patch. |
1232 | | |
1233 | | .. WARNING:: |
1234 | | |
1235 | | Tikz Plotting is implemented only for patches in three dimensions. |
1236 | | |
1237 | | INPUT: |
1238 | | |
1239 | | - ``projmat`` - matrix (optional, default: ``None``) the projection |
1240 | | matrix. Its number of lines must be two. Its number of columns |
1241 | | must equal the dimension of the ambient space of the faces. If |
1242 | | ``None``, the isometric projection is used by default. |
1243 | | - ``print_tikz_env`` - bool (optional, default: ``True``) if ``True``, |
1244 | | the tikzpicture environment are printed |
1245 | | - ``edgecolor`` - string (optional, default: ``'black'``) either |
1246 | | ``'black'`` or ``'facecolor'`` (color of unit face edges) |
1247 | | - ``scale`` - real number (optional, default: ``0.25``) scaling |
1248 | | constant for the whole figure |
1249 | | - ``drawzero`` - bool (optional, default: ``False``) if ``True``, |
1250 | | mark the origin by a black dot |
1251 | | - ``extra_code_before`` - string (optional, default: ``''``) extra code to |
1252 | | include in the tikz picture |
1253 | | - ``extra_code_after`` - string (optional, default: ``''``) extra code to |
1254 | | include in the tikz picture |
1255 | | |
1256 | | EXAMPLES:: |
1257 | | |
1258 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1259 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1260 | | sage: s = P.plot_tikz() |
1261 | | sage: len(s) |
1262 | | 602 |
1263 | | sage: print s #not tested |
1264 | | \begin{tikzpicture} |
1265 | | [x={(-0.216506cm,-0.125000cm)}, y={(0.216506cm,-0.125000cm)}, z={(0.000000cm,0.250000cm)}] |
1266 | | \definecolor{facecolor}{rgb}{0.000,1.000,0.000} |
1267 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
1268 | | (0, 0, 0) -- (0, 0, 1) -- (1, 0, 1) -- (1, 0, 0) -- cycle; |
1269 | | \definecolor{facecolor}{rgb}{1.000,0.000,0.000} |
1270 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
1271 | | (0, 0, 0) -- (0, 1, 0) -- (0, 1, 1) -- (0, 0, 1) -- cycle; |
1272 | | \definecolor{facecolor}{rgb}{0.000,0.000,1.000} |
1273 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
1274 | | (0, 0, 0) -- (1, 0, 0) -- (1, 1, 0) -- (0, 1, 0) -- cycle; |
1275 | | \end{tikzpicture} |
1276 | | |
1277 | | :: |
1278 | | |
1279 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1280 | | sage: E = E1Star(sigma) |
1281 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1282 | | sage: P = E(P, 4) |
1283 | | sage: from sage.misc.latex import latex #not tested |
1284 | | sage: latex.add_to_preamble('\\usepackage{tikz}') #not tested |
1285 | | sage: view(P, tightpage=true) #not tested |
1286 | | |
1287 | | Plot using shades of gray (useful for article figures):: |
1288 | | |
1289 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1290 | | sage: E = E1Star(sigma) |
1291 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1292 | | sage: P.repaint([(0.9, 0.9, 0.9), (0.65,0.65,0.65), (0.4,0.4,0.4)]) |
1293 | | sage: P = E(P, 4) |
1294 | | sage: s = P.plot_tikz() |
1295 | | |
1296 | | Plotting with various options:: |
1297 | | |
1298 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1299 | | sage: E = E1Star(sigma) |
1300 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1301 | | sage: M = matrix(2, 3, map(float, [1,0,-0.7071,0,1,-0.7071])) |
1302 | | sage: P = E(P, 3) |
1303 | | sage: s = P.plot_tikz(projmat=M, edgecolor='facecolor', scale=0.6, drawzero=True) |
1304 | | |
1305 | | Adding X, Y, Z axes using the extra code feature:: |
1306 | | |
1307 | | sage: length = 1.5 |
1308 | | sage: space = 0.3 |
1309 | | sage: axes = '' |
1310 | | sage: axes += "\\draw[->, thick, black] (0,0,0) -- (%s, 0, 0);\n" % length |
1311 | | sage: axes += "\\draw[->, thick, black] (0,0,0) -- (0, %s, 0);\n" % length |
1312 | | sage: axes += "\\node at (%s,0,0) {$x$};\n" % (length + space) |
1313 | | sage: axes += "\\node at (0,%s,0) {$y$};\n" % (length + space) |
1314 | | sage: axes += "\\node at (0,0,%s) {$z$};\n" % (length + space) |
1315 | | sage: axes += "\\draw[->, thick, black] (0,0,0) -- (0, 0, %s);\n" % length |
1316 | | sage: cube = Patch([Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)]) |
1317 | | sage: options = dict(scale=0.5,drawzero=True,extra_code_before=axes) |
1318 | | sage: s = cube.plot_tikz(**options) |
1319 | | sage: len(s) |
1320 | | 986 |
1321 | | sage: print s #not tested |
1322 | | \begin{tikzpicture} |
1323 | | [x={(-0.433013cm,-0.250000cm)}, y={(0.433013cm,-0.250000cm)}, z={(0.000000cm,0.500000cm)}] |
1324 | | \draw[->, thick, black] (0,0,0) -- (1.50000000000000, 0, 0); |
1325 | | \draw[->, thick, black] (0,0,0) -- (0, 1.50000000000000, 0); |
1326 | | \node at (1.80000000000000,0,0) {$x$}; |
1327 | | \node at (0,1.80000000000000,0) {$y$}; |
1328 | | \node at (0,0,1.80000000000000) {$z$}; |
1329 | | \draw[->, thick, black] (0,0,0) -- (0, 0, 1.50000000000000); |
1330 | | \definecolor{facecolor}{rgb}{0.000,1.000,0.000} |
1331 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
1332 | | (0, 0, 0) -- (0, 0, 1) -- (1, 0, 1) -- (1, 0, 0) -- cycle; |
1333 | | \definecolor{facecolor}{rgb}{1.000,0.000,0.000} |
1334 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
1335 | | (0, 0, 0) -- (0, 1, 0) -- (0, 1, 1) -- (0, 0, 1) -- cycle; |
1336 | | \definecolor{facecolor}{rgb}{0.000,0.000,1.000} |
1337 | | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
1338 | | (0, 0, 0) -- (1, 0, 0) -- (1, 1, 0) -- (0, 1, 0) -- cycle; |
1339 | | \node[circle,fill=black,draw=black,minimum size=1.5mm,inner sep=0pt] at (0,0,0) {}; |
1340 | | \end{tikzpicture} |
1341 | | """ |
1342 | | if self.dimension() != 3: |
1343 | | raise NotImplementedError, "Tikz Plotting is implemented only for patches in three dimensions." |
1344 | | |
1345 | | if projmat == None: |
1346 | | projmat = matrix(2, [-1.7320508075688772*0.5, 1.7320508075688772*0.5, 0, -0.5, -0.5, 1])*scale |
1347 | | |
1348 | | e1 = projmat*vector([1,0,0]) |
1349 | | e2 = projmat*vector([0,1,0]) |
1350 | | e3 = projmat*vector([0,0,1]) |
1351 | | face_contour = self._face_contour |
1352 | | color = () |
1353 | | |
1354 | | # string s contains the TiKZ code of the patch |
1355 | | s = '' |
1356 | | |
1357 | | if print_tikz_env: |
1358 | | s += '\\begin{tikzpicture}\n' |
1359 | | s += '[x={(%fcm,%fcm)}, y={(%fcm,%fcm)}, z={(%fcm,%fcm)}]\n'%(e1[0], e1[1], e2[0], e2[1], e3[0], e3[1]) |
1360 | | |
1361 | | s += extra_code_before |
1362 | | |
1363 | | for f in self: |
1364 | | t = f.type() |
1365 | | x, y, z = f.vector() |
1366 | | |
1367 | | if tuple(color) != tuple(f.color()): #tuple is needed, comparison for RGB fails |
1368 | | color = f.color() |
1369 | | s += '\\definecolor{facecolor}{rgb}{%.3f,%.3f,%.3f}\n'%(color[0], color[1], color[2]) |
1370 | | |
1371 | | s += '\\fill[fill=facecolor, draw=%s, shift={(%d,%d,%d)}]\n'%(edgecolor, x, y, z) |
1372 | | s += ' -- '.join(map(str, face_contour[t])) + ' -- cycle;\n' |
1373 | | |
1374 | | s += extra_code_after |
1375 | | |
1376 | | if drawzero: |
1377 | | s += '\\node[circle,fill=black,draw=black,minimum size=1.5mm,inner sep=0pt] at (0,0,0) {};\n' |
1378 | | |
1379 | | if print_tikz_env: |
1380 | | s += '\\end{tikzpicture}' |
1381 | | |
1382 | | return LatexExpr(s) |
1383 | | |
1384 | | _latex_ = plot_tikz |
1385 | | |
1386 | | class E1Star(SageObject): |
1387 | | r""" |
1388 | | A class to model the `E_1^*(\sigma)` map associated with |
1389 | | a unimodular substitution `\sigma`. |
1390 | | |
1391 | | INPUT: |
1392 | | |
1393 | | - ``sigma`` - unimodular ``WordMorphism``, i.e. such that its incidence |
1394 | | matrix has determinant `\pm 1`. |
1395 | | |
1396 | | - ``method`` - 'prefix' or 'suffix' (optional, default: 'suffix') |
1397 | | Enables to use an alternative definition `E_1^*(\sigma)` substitutions, |
1398 | | where the abelianized of the prefix` is used instead of the suffix. |
1399 | | |
1400 | | .. NOTE:: |
1401 | | |
1402 | | The alphabet of the domain and the codomain of `\sigma` must be |
1403 | | equal, and they must be of the form ``[1, ..., d]``, where ``d`` |
1404 | | is a positive integer corresponding to the length of the vectors |
1405 | | of the faces on which `E_1^*(\sigma)` will act. |
1406 | | |
1407 | | EXAMPLES:: |
1408 | | |
1409 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1410 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1411 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1412 | | sage: E = E1Star(sigma) |
1413 | | sage: E(P) |
1414 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*] |
1415 | | |
1416 | | :: |
1417 | | |
1418 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1419 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1420 | | sage: E = E1Star(sigma, method='prefix') |
1421 | | sage: E(P) |
1422 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 0, 1), 1]*, [(0, 0, 1), 2]*] |
1423 | | |
1424 | | :: |
1425 | | |
1426 | | sage: x = [Face((0,0,0,0),1), Face((0,0,0,0),4)] |
1427 | | sage: P = Patch(x) |
1428 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1,4], 4:[1]}) |
1429 | | sage: E = E1Star(sigma) |
1430 | | sage: E(P) |
1431 | | Patch: [[(0, 0, 0, 0), 3]*, [(0, 0, 0, 0), 4]*, [(0, 0, 1, -1), 3]*, [(0, 1, 0, -1), 2]*, [(1, 0, 0, -1), 1]*] |
1432 | | """ |
1433 | | def __init__(self, sigma, method='suffix'): |
1434 | | r""" |
1435 | | E1Star constructor. See class doc for more information. |
1436 | | |
1437 | | EXAMPLES:: |
1438 | | |
1439 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1440 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1441 | | sage: E = E1Star(sigma) |
1442 | | sage: E |
1443 | | E_1^*(WordMorphism: 1->12, 2->13, 3->1) |
1444 | | """ |
1445 | | if not isinstance(sigma, WordMorphism): |
1446 | | raise TypeError, "sigma (=%s) must be an instance of WordMorphism"%sigma |
1447 | | |
1448 | | if sigma.domain().alphabet() != sigma.codomain().alphabet(): |
1449 | | raise ValueError, "The domain and codomain of (%s) must be the same."%sigma |
1450 | | |
1451 | | if abs(det(matrix(sigma))) != 1: |
1452 | | raise ValueError, "The substitution (%s) must be unimodular."%sigma |
1453 | | |
1454 | | first_letter = sigma.codomain().alphabet()[0] |
1455 | | if not (first_letter in ZZ) or (first_letter < 1): |
1456 | | raise ValueError, "The substitution (%s) must be defined on positive integers."%sigma |
1457 | | |
1458 | | self._sigma = WordMorphism(sigma) |
1459 | | self._d = self._sigma.domain().size_of_alphabet() |
1460 | | |
1461 | | # self._base_iter is a base for the iteration of the application of self on set |
1462 | | # of faces. (Exploits the linearity of `E_1^*(\sigma)` to optimize computation.) |
1463 | | alphabet = self._sigma.domain().alphabet() |
1464 | | X = {} |
1465 | | for k in alphabet: |
1466 | | subst_im = self._sigma.image(k) |
1467 | | for n, letter in enumerate(subst_im): |
1468 | | if method == 'suffix': |
1469 | | image_word = subst_im[n+1:] |
1470 | | elif method == 'prefix': |
1471 | | image_word = subst_im[:n] |
1472 | | else: |
1473 | | raise ValueError, "Option 'method' can only be 'prefix' or 'suffix'." |
1474 | | if not letter in X: |
1475 | | X[letter] = [] |
1476 | | v = self.inverse_matrix()*vector(image_word.parikh_vector()) |
1477 | | X[letter].append((v, k)) |
1478 | | self._base_iter = X |
1479 | | |
1480 | | def __eq__(self, other): |
1481 | | r""" |
1482 | | Equality test for E1Star morphisms. |
1483 | | |
1484 | | INPUT: |
1485 | | |
1486 | | - ``other`` - an object |
1487 | | |
1488 | | EXAMPLES:: |
1489 | | |
1490 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1491 | | sage: s = WordMorphism({1:[1,3], 2:[1,2,3], 3:[3]}) |
1492 | | sage: t = WordMorphism({1:[1,2,3], 2:[2,3], 3:[3]}) |
1493 | | sage: S = E1Star(s) |
1494 | | sage: T = E1Star(t) |
1495 | | sage: S == T |
1496 | | False |
1497 | | sage: S2 = E1Star(s, method='prefix') |
1498 | | sage: S == S2 |
1499 | | False |
1500 | | """ |
1501 | | return (isinstance(other, E1Star) and self._base_iter == other._base_iter) |
1502 | | |
1503 | | def __call__(self, patch, iterations=1): |
1504 | | r""" |
1505 | | Applies a generalized substitution to a Patch; this returns a new object. |
1506 | | |
1507 | | The color of every new face in the image is given the same color as its preimage. |
1508 | | |
1509 | | INPUT: |
1510 | | |
1511 | | - ``patch`` - a patch |
1512 | | - ``iterations`` - integer (optional, default: 1) number of iterations |
1513 | | |
1514 | | OUTPUT: |
1515 | | |
1516 | | a patch |
1517 | | |
1518 | | EXAMPLES:: |
1519 | | |
1520 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1521 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1522 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1523 | | sage: E = E1Star(sigma) |
1524 | | sage: E(P) |
1525 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*] |
1526 | | sage: E(P, iterations=4) |
1527 | | Patch of 31 faces |
1528 | | |
1529 | | TEST: |
1530 | | |
1531 | | We test that iterations=0 works (see #10699):: |
1532 | | |
1533 | | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
1534 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1535 | | sage: E = E1Star(sigma) |
1536 | | sage: E(P, iterations=0) |
1537 | | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
1538 | | """ |
1539 | | if iterations == 0: |
1540 | | return Patch(patch) |
1541 | | elif iterations < 0: |
1542 | | raise ValueError, "iterations (=%s) must be >= 0." % iterations |
1543 | | else: |
1544 | | old_faces = patch |
1545 | | for i in xrange(iterations): |
1546 | | new_faces = [] |
1547 | | for f in old_faces: |
1548 | | new_faces.extend(self._call_on_face(f, color=f.color())) |
1549 | | old_faces = new_faces |
1550 | | return Patch(new_faces) |
1551 | | |
1552 | | def __mul__(self, other): |
1553 | | r""" |
1554 | | Return the product of self and other. |
1555 | | |
1556 | | The product satisfies the following rule : |
1557 | | `E_1^*(\sigma\circ\sigma') = E_1^*(\sigma')` \circ E_1^*(\sigma)` |
1558 | | |
1559 | | INPUT: |
1560 | | |
1561 | | - ``other`` - an instance of E1Star |
1562 | | |
1563 | | OUTPUT: |
1564 | | |
1565 | | an instance of E1Star |
1566 | | |
1567 | | EXAMPLES:: |
1568 | | |
1569 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1570 | | sage: s = WordMorphism({1:[2],2:[3],3:[1,2]}) |
1571 | | sage: t = WordMorphism({1:[1,3,1],2:[1],3:[1,1,3,2]}) |
1572 | | sage: E1Star(s) * E1Star(t) |
1573 | | E_1^*(WordMorphism: 1->1, 2->1132, 3->1311) |
1574 | | sage: E1Star(t * s) |
1575 | | E_1^*(WordMorphism: 1->1, 2->1132, 3->1311) |
1576 | | """ |
1577 | | if not isinstance(other, E1Star): |
1578 | | raise TypeError, "other (=%s) must be an instance of E1Star" % other |
1579 | | return E1Star(other.sigma() * self.sigma()) |
1580 | | |
1581 | | def __repr__(self): |
1582 | | r""" |
1583 | | String representation of a patch. |
1584 | | |
1585 | | EXAMPLES:: |
1586 | | |
1587 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1588 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1589 | | sage: E = E1Star(sigma) |
1590 | | sage: E |
1591 | | E_1^*(WordMorphism: 1->12, 2->13, 3->1) |
1592 | | """ |
1593 | | return "E_1^*(%s)" % str(self._sigma) |
1594 | | |
1595 | | def _call_on_face(self, face, color=None): |
1596 | | r""" |
1597 | | Returns an iterator of faces obtained by applying self on the face. |
1598 | | |
1599 | | INPUT: |
1600 | | |
1601 | | - ``face`` - a face |
1602 | | - ``color`` - string, RGB tuple or color, (optional, default: None) |
1603 | | RGB color |
1604 | | |
1605 | | OUTPUT: |
1606 | | |
1607 | | iterator of faces |
1608 | | |
1609 | | EXAMPLES:: |
1610 | | |
1611 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1612 | | sage: f = Face((0,2,0), 1) |
1613 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1614 | | sage: E = E1Star(sigma) |
1615 | | sage: list(E._call_on_face(f)) |
1616 | | [[(3, 0, -3), 1]*, [(2, 1, -3), 2]*, [(2, 0, -2), 3]*] |
1617 | | """ |
1618 | | if len(face.vector()) != self._d: |
1619 | | raise ValueError, "The dimension of the faces must be equal to the size of the alphabet of the substitution." |
1620 | | x_new = self.inverse_matrix() * face.vector() |
1621 | | t = face.type() |
1622 | | return (Face(x_new + v, k, color=color) for v, k in self._base_iter[t]) |
1623 | | |
1624 | | @cached_method |
1625 | | def matrix(self): |
1626 | | r""" |
1627 | | Returns the matrix associated with self. |
1628 | | |
1629 | | EXAMPLES:: |
1630 | | |
1631 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1632 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1633 | | sage: E = E1Star(sigma) |
1634 | | sage: E.matrix() |
1635 | | [1 1 1] |
1636 | | [1 0 0] |
1637 | | [0 1 0] |
1638 | | """ |
1639 | | return self._sigma.incidence_matrix() |
1640 | | |
1641 | | @cached_method |
1642 | | def inverse_matrix(self): |
1643 | | r""" |
1644 | | Returns the inverse of the matrix associated with self. |
1645 | | |
1646 | | EXAMPLES:: |
1647 | | |
1648 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1649 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1650 | | sage: E = E1Star(sigma) |
1651 | | sage: E.inverse_matrix() |
1652 | | [ 0 1 0] |
1653 | | [ 0 0 1] |
1654 | | [ 1 -1 -1] |
1655 | | |
1656 | | """ |
1657 | | return self.matrix().inverse() |
1658 | | |
1659 | | def sigma(self): |
1660 | | r""" |
1661 | | Returns the ``WordMorphism`` associated with self. |
1662 | | |
1663 | | EXAMPLES:: |
1664 | | |
1665 | | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
1666 | | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
1667 | | sage: E = E1Star(sigma) |
1668 | | sage: print E.sigma() |
1669 | | WordMorphism: 1->12, 2->13, 3->1 |
1670 | | """ |
1671 | | return self._sigma |
1672 | | |
| 1 | r""" |
| 2 | Substitutions over unit cube faces (Rauzy fractals) |
| 3 | |
| 4 | This module implements the `E_1^*(\sigma)` substitution |
| 5 | associated with a one-dimensional substitution `\sigma`, |
| 6 | that acts on unit faces of dimension `(d-1)` in `\RR^d`. |
| 7 | |
| 8 | This module defines the following classes and functions: |
| 9 | |
| 10 | - ``Face`` - a class to model a face |
| 11 | |
| 12 | - ``Patch`` - a class to model a finite set of faces |
| 13 | |
| 14 | - ``E1Star`` - a class to model the `E_1^*(\sigma)` application |
| 15 | defined by the substitution sigma |
| 16 | |
| 17 | See the documentation of these objects for more information. |
| 18 | |
| 19 | The convention for the choice of the unit faces and the |
| 20 | definition of `E_1^*(\sigma)` varies from article to article. |
| 21 | Here, unit faces are defined by |
| 22 | |
| 23 | .. MATH:: |
| 24 | |
| 25 | \begin{array}{ccc} |
| 26 | \,[x, 1]^* & = & \{x + \lambda e_2 + \mu e_3 : \lambda, \mu \in [0,1]\} \\ |
| 27 | \,[x, 2]^* & = & \{x + \lambda e_1 + \mu e_3 : \lambda, \mu \in [0,1]\} \\ |
| 28 | \,[x, 3]^* & = & \{x + \lambda e_1 + \mu e_2 : \lambda, \mu \in [0,1]\} |
| 29 | \end{array} |
| 30 | |
| 31 | and the dual substitution `E_1^*(\sigma)` is defined by |
| 32 | |
| 33 | .. MATH:: |
| 34 | |
| 35 | E_1^*(\sigma)([x,i]^*) = |
| 36 | \bigcup_{k = 1,2,3} \; \bigcup_{s | \sigma(k) = pis} |
| 37 | [M^{-1}(x + \ell(s)), k]^*, |
| 38 | |
| 39 | where `\ell(s)` is the abelianized of `s`, and `M` is the matrix of `\sigma`. |
| 40 | |
| 41 | AUTHORS: |
| 42 | |
| 43 | - Franco Saliola (2009): initial version |
| 44 | - Vincent Delecroix, Timo Jolivet, Stepan Starosta, Sebastien Labbe (2010-05): redesign |
| 45 | - Timo Jolivet (2010-08, 2010-09, 2011): redesign |
| 46 | |
| 47 | REFERENCES: |
| 48 | |
| 49 | .. [AI] P. Arnoux, S. Ito, |
| 50 | Pisot substitutions and Rauzy fractals, |
| 51 | Bull. Belg. Math. Soc. 8 (2), 2001, pp. 181--207 |
| 52 | |
| 53 | .. [SAI] Y. Sano, P. Arnoux, S. Ito, |
| 54 | Higher dimensional extensions of substitutions and their dual maps, |
| 55 | J. Anal. Math. 83, 2001, pp. 183--206 |
| 56 | |
| 57 | EXAMPLES: |
| 58 | |
| 59 | We start by drawing a simple three-face patch:: |
| 60 | |
| 61 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 62 | sage: x = [Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)] |
| 63 | sage: P = Patch(x) |
| 64 | sage: P |
| 65 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
| 66 | sage: P.plot() #not tested |
| 67 | |
| 68 | We apply a substitution to this patch, and draw the result:: |
| 69 | |
| 70 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 71 | sage: E = E1Star(sigma) |
| 72 | sage: E(P) |
| 73 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*] |
| 74 | sage: E(P).plot() #not tested |
| 75 | |
| 76 | .. NOTE:: |
| 77 | |
| 78 | - The type of a face is given by an integer in ``[1, ..., d]`` |
| 79 | where ``d`` is the length of the vector of the face. |
| 80 | |
| 81 | - The alphabet of the domain and the codomain of `\sigma` must be |
| 82 | equal, and they must be of the form ``[1, ..., d]``, where ``d`` |
| 83 | is a positive integer corresponding to the length of the vectors |
| 84 | of the faces on which `E_1^*(\sigma)` will act. |
| 85 | |
| 86 | :: |
| 87 | |
| 88 | sage: P = Patch([Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)]) |
| 89 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 90 | sage: E = E1Star(sigma) |
| 91 | sage: E(P) |
| 92 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*] |
| 93 | |
| 94 | The application of an ``E1Star`` substitution assigns to each new face the color of its preimage. |
| 95 | The ``repaint`` method allows us to repaint the faces of a patch. |
| 96 | A single color can also be assigned to every face, by specifying a list of a single color:: |
| 97 | |
| 98 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 99 | sage: P = E(P, 5) |
| 100 | sage: P.repaint(['green']) |
| 101 | sage: P.plot() #not tested |
| 102 | |
| 103 | A list of colors allows us to color the faces sequentially:: |
| 104 | |
| 105 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 106 | sage: P = E(P) |
| 107 | sage: P.repaint(['red', 'yellow', 'green', 'blue', 'black']) |
| 108 | sage: P = E(P, 3) |
| 109 | sage: P.plot() #not tested |
| 110 | |
| 111 | All the color schemes from ``matplotlib.cm.datad.keys()`` can be used:: |
| 112 | |
| 113 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 114 | sage: P.repaint(cmap='summer') |
| 115 | sage: P = E(P, 3) |
| 116 | sage: P.plot() #not tested |
| 117 | sage: P.repaint(cmap='hsv') |
| 118 | sage: P = E(P, 2) |
| 119 | sage: P.plot() #not tested |
| 120 | |
| 121 | It is also possible to specify a dictionary to color the faces according to their type:: |
| 122 | |
| 123 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 124 | sage: P = E(P, 5) |
| 125 | sage: P.repaint({1:(0.7, 0.7, 0.7), 2:(0.5,0.5,0.5), 3:(0.3,0.3,0.3)}) |
| 126 | sage: P.plot() #not tested |
| 127 | sage: P.repaint({1:'red', 2:'yellow', 3:'green'}) |
| 128 | sage: P.plot() #not tested |
| 129 | |
| 130 | Let us look at a nice big patch in 3D:: |
| 131 | |
| 132 | sage: sigma = WordMorphism({1:[1,2], 2:[3], 3:[1]}) |
| 133 | sage: E = E1Star(sigma) |
| 134 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 135 | sage: P = P + P.translate([-1,1,0]) |
| 136 | sage: P = E(P, 11) |
| 137 | sage: P.plot3d() #not tested |
| 138 | |
| 139 | Plotting with TikZ pictures is possible:: |
| 140 | |
| 141 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 142 | sage: s = P.plot_tikz() |
| 143 | sage: print s #not tested |
| 144 | \begin{tikzpicture} |
| 145 | [x={(-0.216506cm,-0.125000cm)}, y={(0.216506cm,-0.125000cm)}, z={(0.000000cm,0.250000cm)}] |
| 146 | \definecolor{facecolor}{rgb}{0.000,1.000,0.000} |
| 147 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 148 | (0, 0, 0) -- (0, 0, 1) -- (1, 0, 1) -- (1, 0, 0) -- cycle; |
| 149 | \definecolor{facecolor}{rgb}{1.000,0.000,0.000} |
| 150 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 151 | (0, 0, 0) -- (0, 1, 0) -- (0, 1, 1) -- (0, 0, 1) -- cycle; |
| 152 | \definecolor{facecolor}{rgb}{0.000,0.000,1.000} |
| 153 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 154 | (0, 0, 0) -- (1, 0, 0) -- (1, 1, 0) -- (0, 1, 0) -- cycle; |
| 155 | \end{tikzpicture} |
| 156 | |
| 157 | Plotting patches made of unit segments instead of unit faces:: |
| 158 | |
| 159 | sage: P = Patch([Face([0,0], 1), Face([0,0], 2)]) |
| 160 | sage: E = E1Star(WordMorphism({1:[1,2],2:[1]})) |
| 161 | sage: F = E1Star(WordMorphism({1:[1,1,2],2:[2,1]})) |
| 162 | sage: E(P,5).plot() |
| 163 | sage: F(P,3).plot() |
| 164 | |
| 165 | Everything works in any dimension (except for the plotting features |
| 166 | which only work in dimension two or three):: |
| 167 | |
| 168 | sage: P = Patch([Face((0,0,0,0),1), Face((0,0,0,0),4)]) |
| 169 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1,4], 4:[1]}) |
| 170 | sage: E = E1Star(sigma) |
| 171 | sage: E(P) |
| 172 | Patch: [[(0, 0, 0, 0), 3]*, [(0, 0, 0, 0), 4]*, [(0, 0, 1, -1), 3]*, [(0, 1, 0, -1), 2]*, [(1, 0, 0, -1), 1]*] |
| 173 | |
| 174 | :: |
| 175 | |
| 176 | sage: sigma = WordMorphism({1:[1,2],2:[1,3],3:[1,4],4:[1,5],5:[1,6],6:[1,7],7:[1,8],8:[1,9],9:[1,10],10:[1,11],11:[1,12],12:[1]}) |
| 177 | sage: E = E1Star(sigma) |
| 178 | sage: E |
| 179 | E_1^*(WordMorphism: 1->12, 10->1,11, 11->1,12, 12->1, 2->13, 3->14, 4->15, 5->16, 6->17, 7->18, 8->19, 9->1,10) |
| 180 | sage: P = Patch([Face((0,0,0,0,0,0,0,0,0,0,0,0),t) for t in [1,2,3]]) |
| 181 | sage: for x in sorted(list(E(P)), key=lambda x : (x.vector(),x.type())): print x |
| 182 | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 1]* |
| 183 | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 2]* |
| 184 | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0), 12]* |
| 185 | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -1), 11]* |
| 186 | [(0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, -1), 10]* |
| 187 | [(0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, -1), 9]* |
| 188 | [(0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, -1), 8]* |
| 189 | [(0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1), 7]* |
| 190 | [(0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, -1), 6]* |
| 191 | [(0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, -1), 5]* |
| 192 | [(0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, -1), 4]* |
| 193 | [(0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, -1), 3]* |
| 194 | [(0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1), 2]* |
| 195 | [(1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1), 1]* |
| 196 | """ |
| 197 | #***************************************************************************** |
| 198 | # Copyright (C) 2010 Franco Saliola <saliola@gmail.com> |
| 199 | # Vincent Delecroix <20100.delecroix@gmail.com> |
| 200 | # Timo Jolivet <timo.jolivet@gmail.com> |
| 201 | # Stepan Starosta <stepan.starosta@gmail.com> |
| 202 | # Sebastien Labbe <slabqc at gmail.com> |
| 203 | # |
| 204 | # Distributed under the terms of the GNU General Public License (GPL) |
| 205 | # as published by the Free Software Foundation; either version 2 of |
| 206 | # the License, or (at your option) any later version. |
| 207 | # http://www.gnu.org/licenses/ |
| 208 | #***************************************************************************** |
| 209 | from sage.misc.functional import det |
| 210 | from sage.structure.sage_object import SageObject |
| 211 | from sage.combinat.words.morphism import WordMorphism |
| 212 | from sage.matrix.constructor import matrix |
| 213 | from sage.modules.free_module_element import vector |
| 214 | from sage.plot.plot import Graphics |
| 215 | from sage.plot.plot import rainbow |
| 216 | from sage.plot.colors import Color |
| 217 | from sage.plot.polygon import polygon |
| 218 | from sage.plot.line import line |
| 219 | from sage.rings.integer_ring import ZZ |
| 220 | from sage.misc.latex import LatexExpr |
| 221 | from sage.misc.lazy_attribute import lazy_attribute |
| 222 | from sage.misc.cachefunc import cached_method |
| 223 | |
| 224 | # matplotlib color maps, loaded on-demand |
| 225 | cm = None |
| 226 | |
| 227 | class Face(SageObject): |
| 228 | r""" |
| 229 | A class to model a unit face of arbitrary dimension. |
| 230 | |
| 231 | A unit face in dimension `d` is represented by |
| 232 | a `d`-dimensional vector ``v`` and a type ``t`` in `\{1, \ldots, d\}`. |
| 233 | The type of the face corresponds to the canonical unit vector |
| 234 | to which the face is orthogonal. |
| 235 | The optional ``color`` argument is used in plotting functions. |
| 236 | |
| 237 | INPUT: |
| 238 | |
| 239 | - ``v`` - tuple of integers |
| 240 | - ``t`` - integer in ``[1, ..., len(v)]``, type of the face. The face of type `i` |
| 241 | is orthogonal to the canonical vector `e_i`. |
| 242 | - ``color`` - color (optional, default: ``None``) color of the face, |
| 243 | used for plotting only. If ``None``, its value is guessed from the |
| 244 | face type. |
| 245 | |
| 246 | EXAMPLES:: |
| 247 | |
| 248 | sage: from sage.combinat.e_one_star import Face |
| 249 | sage: f = Face((0,2,0), 3) |
| 250 | sage: f.vector() |
| 251 | (0, 2, 0) |
| 252 | sage: f.type() |
| 253 | 3 |
| 254 | |
| 255 | :: |
| 256 | |
| 257 | sage: f = Face((0,2,0), 3, color=(0.5, 0.5, 0.5)) |
| 258 | sage: f.color() |
| 259 | RGB color (0.5, 0.5, 0.5) |
| 260 | """ |
| 261 | def __init__(self, v, t, color=None): |
| 262 | r""" |
| 263 | Face constructor. See class doc for more information. |
| 264 | |
| 265 | EXAMPLES:: |
| 266 | |
| 267 | sage: from sage.combinat.e_one_star import Face |
| 268 | sage: f = Face((0,2,0), 3) |
| 269 | sage: f.vector() |
| 270 | (0, 2, 0) |
| 271 | sage: f.type() |
| 272 | 3 |
| 273 | |
| 274 | TEST: |
| 275 | |
| 276 | We test that types can be given by an int (see #10699):: |
| 277 | |
| 278 | sage: f = Face((0,2,0), int(1)) |
| 279 | """ |
| 280 | self._vector = (ZZ**len(v))(v) |
| 281 | self._vector.set_immutable() |
| 282 | |
| 283 | if not((t in ZZ) and 1 <= t <= len(v)): |
| 284 | raise ValueError, 'The type must be an integer between 1 and len(v)' |
| 285 | self._type = t |
| 286 | |
| 287 | if color is None: |
| 288 | if self._type == 1: |
| 289 | color = Color((1,0,0)) |
| 290 | elif self._type == 2: |
| 291 | color = Color((0,1,0)) |
| 292 | elif self._type == 3: |
| 293 | color = Color((0,0,1)) |
| 294 | else: |
| 295 | color = Color() |
| 296 | self._color = Color(color) |
| 297 | |
| 298 | def __repr__(self): |
| 299 | r""" |
| 300 | String representation of a face. |
| 301 | |
| 302 | EXAMPLES:: |
| 303 | |
| 304 | sage: from sage.combinat.e_one_star import Face |
| 305 | sage: f = Face((0,0,0,3), 3) |
| 306 | sage: f |
| 307 | [(0, 0, 0, 3), 3]* |
| 308 | |
| 309 | :: |
| 310 | |
| 311 | sage: f = Face((0,0,0,3), 3) |
| 312 | sage: f |
| 313 | [(0, 0, 0, 3), 3]* |
| 314 | """ |
| 315 | return "[%s, %s]*"%(self.vector(), self.type()) |
| 316 | |
| 317 | def __eq__(self, other): |
| 318 | r""" |
| 319 | Equality of faces. |
| 320 | |
| 321 | EXAMPLES:: |
| 322 | |
| 323 | sage: from sage.combinat.e_one_star import Face |
| 324 | sage: f = Face((0,0,0,3), 3) |
| 325 | sage: g = Face((0,0,0,3), 3) |
| 326 | sage: f == g |
| 327 | True |
| 328 | """ |
| 329 | return (isinstance(other, Face) and |
| 330 | self.vector() == other.vector() and |
| 331 | self.type() == other.type() ) |
| 332 | |
| 333 | def __cmp__(self, other): |
| 334 | r""" |
| 335 | Compare self and other, returning -1, 0, or 1, depending on if |
| 336 | self < other, self == other, or self > other, respectively. |
| 337 | |
| 338 | The vectors of the faces are first compared, |
| 339 | and the types of the faces are compared if the vectors are equal. |
| 340 | |
| 341 | EXAMPLES:: |
| 342 | |
| 343 | sage: from sage.combinat.e_one_star import Face |
| 344 | sage: Face([-2,1,0], 2) < Face([-1,2,2],3) |
| 345 | True |
| 346 | sage: Face([-2,1,0], 2) < Face([-2,1,0],3) |
| 347 | True |
| 348 | sage: Face([-2,1,0], 2) < Face([-2,1,0],2) |
| 349 | False |
| 350 | """ |
| 351 | v1 = self.vector() |
| 352 | v2 = other.vector() |
| 353 | t1 = self.type() |
| 354 | t2 = other.type() |
| 355 | |
| 356 | if v1 < v2: |
| 357 | return -1 |
| 358 | elif v1 > v2: |
| 359 | return 1 |
| 360 | else: |
| 361 | return t1.__cmp__(t2) |
| 362 | |
| 363 | def __hash__(self): |
| 364 | r""" |
| 365 | EXAMPLES:: |
| 366 | |
| 367 | sage: from sage.combinat.e_one_star import Face |
| 368 | sage: f = Face((0,0,0,3), 3) |
| 369 | sage: g = Face((0,0,0,3), 3) |
| 370 | sage: hash(f) == hash(g) |
| 371 | True |
| 372 | """ |
| 373 | return hash((self.vector(), self.type())) |
| 374 | |
| 375 | def __add__(self, other): |
| 376 | r""" |
| 377 | Addition of self with a Face, a Patch or a finite iterable of faces. |
| 378 | |
| 379 | INPUT: |
| 380 | |
| 381 | - ``other`` - a Patch or a Face or a finite iterable of faces |
| 382 | |
| 383 | EXAMPLES:: |
| 384 | |
| 385 | sage: from sage.combinat.e_one_star import Face, Patch |
| 386 | sage: f = Face([0,0,0], 3) |
| 387 | sage: g = Face([0,1,-1], 2) |
| 388 | sage: f + g |
| 389 | Patch: [[(0, 0, 0), 3]*, [(0, 1, -1), 2]*] |
| 390 | sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2)]) |
| 391 | sage: f + P |
| 392 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
| 393 | |
| 394 | Adding a finite iterable of faces:: |
| 395 | |
| 396 | sage: from sage.combinat.e_one_star import Face |
| 397 | sage: f = Face([0,0,0], 3) |
| 398 | sage: f + [f,f] |
| 399 | Patch: [[(0, 0, 0), 3]*] |
| 400 | """ |
| 401 | if isinstance(other, Face): |
| 402 | return Patch([self, other]) |
| 403 | else: |
| 404 | return Patch(other).union(self) |
| 405 | |
| 406 | def vector(self): |
| 407 | r""" |
| 408 | Return the vector of the face. |
| 409 | |
| 410 | EXAMPLES:: |
| 411 | |
| 412 | sage: from sage.combinat.e_one_star import Face |
| 413 | sage: f = Face((0,2,0), 3) |
| 414 | sage: f.vector() |
| 415 | (0, 2, 0) |
| 416 | """ |
| 417 | return self._vector |
| 418 | |
| 419 | def type(self): |
| 420 | r""" |
| 421 | Returns the type of the face. |
| 422 | |
| 423 | EXAMPLES:: |
| 424 | |
| 425 | sage: from sage.combinat.e_one_star import Face |
| 426 | sage: f = Face((0,2,0), 3) |
| 427 | sage: f.type() |
| 428 | 3 |
| 429 | |
| 430 | :: |
| 431 | |
| 432 | sage: f = Face((0,2,0), 3) |
| 433 | sage: f.type() |
| 434 | 3 |
| 435 | """ |
| 436 | return self._type |
| 437 | |
| 438 | def color(self, color=None): |
| 439 | r""" |
| 440 | Returns or change the color of the face. |
| 441 | |
| 442 | INPUT: |
| 443 | |
| 444 | - ``color`` - string, rgb tuple, color (optional, default: ``None``) |
| 445 | the new color to assign to the face. If ``None``, it returns the |
| 446 | color of the face. |
| 447 | |
| 448 | OUTPUT: |
| 449 | |
| 450 | color |
| 451 | |
| 452 | EXAMPLES:: |
| 453 | |
| 454 | sage: from sage.combinat.e_one_star import Face |
| 455 | sage: f = Face((0,2,0), 3) |
| 456 | sage: f.color() |
| 457 | RGB color (0.0, 0.0, 1.0) |
| 458 | sage: f.color('red') |
| 459 | sage: f.color() |
| 460 | RGB color (1.0, 0.0, 0.0) |
| 461 | |
| 462 | """ |
| 463 | if color is None: |
| 464 | return self._color |
| 465 | else: |
| 466 | self._color = Color(color) |
| 467 | |
| 468 | def _plot(self, projmat, face_contour, opacity): |
| 469 | r""" |
| 470 | Returns a 2D graphic object representing the face. |
| 471 | |
| 472 | INPUT: |
| 473 | |
| 474 | - ``projmat`` - 2*3 projection matrix (used only for faces in three dimensions) |
| 475 | - ``face_contour`` - dict, maps the face type to vectors describing |
| 476 | the contour of unit faces (used only for faces in three dimensions) |
| 477 | - ``opacity`` - the alpha value for the color of the face |
| 478 | |
| 479 | OUTPUT: |
| 480 | |
| 481 | 2D graphic object |
| 482 | |
| 483 | EXAMPLES:: |
| 484 | |
| 485 | sage: from sage.combinat.e_one_star import Face |
| 486 | sage: f = Face((0,0,3), 3) |
| 487 | sage: projmat = matrix(2, [-1.7320508075688772*0.5, 1.7320508075688772*0.5, 0, -0.5, -0.5, 1]) |
| 488 | sage: face_contour = {} |
| 489 | sage: face_contour[1] = map(vector, [(0,0,0),(0,1,0),(0,1,1),(0,0,1)]) |
| 490 | sage: face_contour[2] = map(vector, [(0,0,0),(0,0,1),(1,0,1),(1,0,0)]) |
| 491 | sage: face_contour[3] = map(vector, [(0,0,0),(1,0,0),(1,1,0),(0,1,0)]) |
| 492 | sage: G = f._plot(projmat, face_contour, 0.75) |
| 493 | |
| 494 | :: |
| 495 | |
| 496 | sage: f = Face((0,0), 2) |
| 497 | sage: f._plot(None, None, 1) |
| 498 | """ |
| 499 | v = self.vector() |
| 500 | t = self.type() |
| 501 | G = Graphics() |
| 502 | |
| 503 | if len(v) == 2: |
| 504 | if t == 1: |
| 505 | G += line([v, v + vector([0,1])], rgbcolor=self.color(), thickness=1.5, alpha=opacity) |
| 506 | elif t == 2: |
| 507 | G += line([v, v + vector([1,0])], rgbcolor=self.color(), thickness=1.5, alpha=opacity) |
| 508 | |
| 509 | elif len(v) == 3: |
| 510 | G += polygon([projmat*(u+v) for u in face_contour[t]], alpha=opacity, |
| 511 | thickness=1, rgbcolor=self.color()) |
| 512 | |
| 513 | else: |
| 514 | raise NotImplementedError, "Plotting is implemented only for patches in two or three dimensions." |
| 515 | |
| 516 | return G |
| 517 | |
| 518 | def _plot3d(self, face_contour): |
| 519 | r""" |
| 520 | 3D reprensentation of a unit face (Jmol). |
| 521 | |
| 522 | INPUT: |
| 523 | |
| 524 | - ``face_contour`` - dict, maps the face type to vectors describing |
| 525 | the contour of unit faces |
| 526 | |
| 527 | EXAMPLES:: |
| 528 | |
| 529 | sage: from sage.combinat.e_one_star import Face |
| 530 | sage: f = Face((0,0,3), 3) |
| 531 | sage: face_contour = {1: map(vector, [(0,0,0),(0,1,0),(0,1,1),(0,0,1)]), 2: map(vector, [(0,0,0),(0,0,1),(1,0,1),(1,0,0)]), 3: map(vector, [(0,0,0),(1,0,0),(1,1,0),(0,1,0)])} |
| 532 | sage: G = f._plot3d(face_contour) #not tested |
| 533 | """ |
| 534 | v = self.vector() |
| 535 | t = self.type() |
| 536 | c = self.color() |
| 537 | G = polygon([u+v for u in face_contour[t]], rgbcolor=c) |
| 538 | return G |
| 539 | |
| 540 | class Patch(SageObject): |
| 541 | r""" |
| 542 | A class to model a collection of faces. A patch is represented by an immutable set of Faces. |
| 543 | |
| 544 | .. NOTE:: |
| 545 | |
| 546 | The dimension of a patch is the length of the vectors of the faces in the patch, |
| 547 | which is assumed to be the same for every face in the patch. |
| 548 | |
| 549 | .. NOTE:: |
| 550 | |
| 551 | Since version 4.7.1, Patches are immutable, except for the colors of the faces, |
| 552 | which are not taken into account for equality tests and hash functions. |
| 553 | |
| 554 | INPUT: |
| 555 | |
| 556 | - ``faces`` - finite iterable of faces |
| 557 | - ``face_contour`` - dict (optional, default:``None``) maps the face |
| 558 | type to vectors describing the contour of unit faces. If None, |
| 559 | defaults contour are assumed for faces of type 1, 2, 3 or 1, 2, 3. |
| 560 | Used in plotting methods only. |
| 561 | |
| 562 | EXAMPLES:: |
| 563 | |
| 564 | sage: from sage.combinat.e_one_star import Face, Patch |
| 565 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 566 | sage: P |
| 567 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
| 568 | |
| 569 | :: |
| 570 | |
| 571 | sage: face_contour = {} |
| 572 | sage: face_contour[1] = map(vector, [(0,0,0),(0,1,0),(0,1,1),(0,0,1)]) |
| 573 | sage: face_contour[2] = map(vector, [(0,0,0),(0,0,1),(1,0,1),(1,0,0)]) |
| 574 | sage: face_contour[3] = map(vector, [(0,0,0),(1,0,0),(1,1,0),(0,1,0)]) |
| 575 | sage: Patch([Face((0,0,0),t) for t in [1,2,3]], face_contour=face_contour) |
| 576 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
| 577 | """ |
| 578 | def __init__(self, faces, face_contour=None): |
| 579 | r""" |
| 580 | Constructor of a patch (set of faces). See class doc for more information. |
| 581 | |
| 582 | EXAMPLES:: |
| 583 | |
| 584 | sage: from sage.combinat.e_one_star import Face, Patch |
| 585 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 586 | sage: P |
| 587 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
| 588 | |
| 589 | TEST: |
| 590 | |
| 591 | We test that colors are not anymore mixed up between Patches (see #11255):: |
| 592 | |
| 593 | sage: P = Patch([Face([0,0,0],2)]) |
| 594 | sage: Q = Patch(P) |
| 595 | sage: next(iter(P)).color() |
| 596 | RGB color (0.0, 1.0, 0.0) |
| 597 | sage: next(iter(Q)).color('yellow') |
| 598 | sage: next(iter(P)).color() |
| 599 | RGB color (0.0, 1.0, 0.0) |
| 600 | |
| 601 | """ |
| 602 | self._faces = frozenset(Face(f.vector(), f.type(), f.color()) for f in faces) |
| 603 | |
| 604 | try: |
| 605 | f0 = next(iter(self._faces)) |
| 606 | except StopIteration: |
| 607 | self._dimension = None |
| 608 | else: |
| 609 | self._dimension = len(f0.vector()) |
| 610 | |
| 611 | if not face_contour is None: |
| 612 | self._face_contour = face_contour |
| 613 | |
| 614 | else: |
| 615 | self._face_contour = { |
| 616 | 1: map(vector, [(0,0,0),(0,1,0),(0,1,1),(0,0,1)]), |
| 617 | 2: map(vector, [(0,0,0),(0,0,1),(1,0,1),(1,0,0)]), |
| 618 | 3: map(vector, [(0,0,0),(1,0,0),(1,1,0),(0,1,0)]) |
| 619 | } |
| 620 | |
| 621 | def __eq__(self, other): |
| 622 | r""" |
| 623 | Equality test for Patch. |
| 624 | |
| 625 | INPUT: |
| 626 | |
| 627 | - ``other`` - an object |
| 628 | |
| 629 | EXAMPLES:: |
| 630 | |
| 631 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 632 | sage: P = Patch([Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)]) |
| 633 | sage: Q = Patch([Face((0,1,0),1), Face((0,0,0),3)]) |
| 634 | sage: P == P |
| 635 | True |
| 636 | sage: P == Q |
| 637 | False |
| 638 | sage: P == 4 |
| 639 | False |
| 640 | |
| 641 | :: |
| 642 | |
| 643 | sage: s = WordMorphism({1:[1,3], 2:[1,2,3], 3:[3]}) |
| 644 | sage: t = WordMorphism({1:[1,2,3], 2:[2,3], 3:[3]}) |
| 645 | sage: P = Patch([Face((0,0,0), 1), Face((0,0,0), 2), Face((0,0,0), 3)]) |
| 646 | sage: E1Star(s)(P) == E1Star(t)(P) |
| 647 | False |
| 648 | sage: E1Star(s*t)(P) == E1Star(t)(E1Star(s)(P)) |
| 649 | True |
| 650 | """ |
| 651 | return (isinstance(other, Patch) and self._faces == other._faces) |
| 652 | |
| 653 | def __hash__(self): |
| 654 | r""" |
| 655 | Hash function of Patch. |
| 656 | |
| 657 | EXAMPLES:: |
| 658 | |
| 659 | sage: from sage.combinat.e_one_star import Face, Patch |
| 660 | sage: x = [Face((0,0,0),t) for t in [1,2,3]] |
| 661 | sage: P = Patch(x) |
| 662 | sage: hash(P) #random |
| 663 | -4839605361791007520 |
| 664 | |
| 665 | TEST: |
| 666 | |
| 667 | We test that two equal patches have the same hash (see #11255):: |
| 668 | |
| 669 | sage: P = Patch([Face([0,0,0],1), Face([0,0,0],2)]) |
| 670 | sage: Q = Patch([Face([0,0,0],2), Face([0,0,0],1)]) |
| 671 | sage: P == Q |
| 672 | True |
| 673 | sage: hash(P) == hash(Q) |
| 674 | True |
| 675 | |
| 676 | Changing the color does not affect the hash value:: |
| 677 | |
| 678 | sage: p = Patch([Face((0,0,0), t) for t in [1,2,3]]) |
| 679 | sage: H1 = hash(p) |
| 680 | sage: p.repaint(['blue']) |
| 681 | sage: H2 = hash(p) |
| 682 | sage: H1 == H2 |
| 683 | True |
| 684 | """ |
| 685 | return hash(self._faces) |
| 686 | |
| 687 | def __len__(self): |
| 688 | r""" |
| 689 | Returns the number of faces contained in the patch. |
| 690 | |
| 691 | OUPUT: |
| 692 | |
| 693 | integer |
| 694 | |
| 695 | EXAMPLES:: |
| 696 | |
| 697 | sage: from sage.combinat.e_one_star import Face, Patch |
| 698 | sage: x = [Face((0,0,0),t) for t in [1,2,3]] |
| 699 | sage: P = Patch(x) |
| 700 | sage: len(P) #indirect doctest |
| 701 | 3 |
| 702 | """ |
| 703 | return len(self._faces) |
| 704 | |
| 705 | def __iter__(self): |
| 706 | r""" |
| 707 | Return an iterator over the faces of the patch. |
| 708 | |
| 709 | OUTPUT: |
| 710 | |
| 711 | iterator |
| 712 | |
| 713 | EXAMPLES:: |
| 714 | |
| 715 | sage: from sage.combinat.e_one_star import Face, Patch |
| 716 | sage: x = [Face((0,0,0),t) for t in [1,2,3]] |
| 717 | sage: P = Patch(x) |
| 718 | sage: it = iter(P) |
| 719 | sage: type(it.next()) |
| 720 | <class 'sage.combinat.e_one_star.Face'> |
| 721 | sage: type(it.next()) |
| 722 | <class 'sage.combinat.e_one_star.Face'> |
| 723 | sage: type(it.next()) |
| 724 | <class 'sage.combinat.e_one_star.Face'> |
| 725 | sage: type(it.next()) |
| 726 | Traceback (most recent call last): |
| 727 | ... |
| 728 | StopIteration |
| 729 | """ |
| 730 | return iter(self._faces) |
| 731 | |
| 732 | def __add__(self, other): |
| 733 | r""" |
| 734 | Addition of patches (union). |
| 735 | |
| 736 | INPUT: |
| 737 | |
| 738 | - ``other`` - a Patch or a Face or a finite iterable of faces |
| 739 | |
| 740 | EXAMPLES:: |
| 741 | |
| 742 | sage: from sage.combinat.e_one_star import Face, Patch |
| 743 | sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2)]) |
| 744 | sage: Q = P.translate([1,-1,0]) |
| 745 | sage: P + Q |
| 746 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, -1, 0), 1]*, [(1, -1, 0), 2]*] |
| 747 | sage: P + Face([0,0,0],3) |
| 748 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
| 749 | sage: P + [Face([0,0,0],3), Face([1,1,1],2)] |
| 750 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(1, 1, 1), 2]*] |
| 751 | """ |
| 752 | return self.union(other) |
| 753 | |
| 754 | def __sub__(self, other): |
| 755 | r""" |
| 756 | Subtraction of patches (difference). |
| 757 | |
| 758 | INPUT: |
| 759 | |
| 760 | - ``other`` - a Patch or a Face or a finite iterable of faces |
| 761 | |
| 762 | EXAMPLES:: |
| 763 | |
| 764 | sage: from sage.combinat.e_one_star import Face, Patch |
| 765 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 766 | sage: P - Face([0,0,0],2) |
| 767 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 3]*] |
| 768 | sage: P - P |
| 769 | Patch: [] |
| 770 | """ |
| 771 | return self.difference(other) |
| 772 | |
| 773 | def __repr__(self): |
| 774 | r""" |
| 775 | String representation of a patch. |
| 776 | |
| 777 | Displays all the faces if there less than 20, |
| 778 | otherwise displays only the number of faces. |
| 779 | |
| 780 | EXAMPLES:: |
| 781 | |
| 782 | sage: from sage.combinat.e_one_star import Face, Patch |
| 783 | sage: x = [Face((0,0,0),t) for t in [1,2,3]] |
| 784 | sage: P = Patch(x) |
| 785 | sage: P |
| 786 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
| 787 | |
| 788 | :: |
| 789 | |
| 790 | sage: x = [Face((0,0,a),1) for a in range(25)] |
| 791 | sage: P = Patch(x) |
| 792 | sage: P |
| 793 | Patch of 25 faces |
| 794 | """ |
| 795 | if len(self) <= 20: |
| 796 | L = list(self) |
| 797 | L.sort(key=lambda x : (x.vector(),x.type())) |
| 798 | return "Patch: %s"%L |
| 799 | else: |
| 800 | return "Patch of %s faces"%len(self) |
| 801 | |
| 802 | def add(self, other): |
| 803 | r""" |
| 804 | Returns a Patch consisting of the union of self and other. |
| 805 | |
| 806 | INPUT: |
| 807 | |
| 808 | - ``other`` - a Patch or a Face or a finite iterable of faces |
| 809 | |
| 810 | EXAMPLES:: |
| 811 | |
| 812 | sage: from sage.combinat.e_one_star import Face, Patch |
| 813 | sage: P = Patch([Face((0,0,0),1), Face((0,0,0),2)]) |
| 814 | sage: P.add(Face((1,2,3), 3)) #not tested |
| 815 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*] |
| 816 | sage: P.add([Face((1,2,3), 3), Face((2,3,3), 2)]) #not tested |
| 817 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*, [(2, 3, 3), 2]*] |
| 818 | """ |
| 819 | from sage.misc.misc import deprecation |
| 820 | deprecation("Objects sage.combinat.e_one_star.Patch " + \ |
| 821 | "are immutable since Sage-4.7.1. " + \ |
| 822 | "Use the usual addition (P + Q) or use the Patch.union method.") |
| 823 | return self.union(other) |
| 824 | |
| 825 | def union(self, other): |
| 826 | r""" |
| 827 | Returns a Patch consisting of the union of self and other. |
| 828 | |
| 829 | INPUT: |
| 830 | |
| 831 | - ``other`` - a Patch or a Face or a finite iterable of faces |
| 832 | |
| 833 | EXAMPLES:: |
| 834 | |
| 835 | sage: from sage.combinat.e_one_star import Face, Patch |
| 836 | sage: P = Patch([Face((0,0,0),1), Face((0,0,0),2)]) |
| 837 | sage: P.union(Face((1,2,3), 3)) |
| 838 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*] |
| 839 | sage: P.union([Face((1,2,3), 3), Face((2,3,3), 2)]) |
| 840 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*, [(2, 3, 3), 2]*] |
| 841 | """ |
| 842 | if isinstance(other, Face): |
| 843 | return Patch(self._faces.union([other])) |
| 844 | else: |
| 845 | return Patch(self._faces.union(other)) |
| 846 | |
| 847 | def difference(self, other): |
| 848 | r""" |
| 849 | Returns the difference of self and other. |
| 850 | |
| 851 | INPUT: |
| 852 | |
| 853 | - ``other`` - a finite iterable of faces or a single face |
| 854 | |
| 855 | EXAMPLES:: |
| 856 | |
| 857 | sage: from sage.combinat.e_one_star import Face, Patch |
| 858 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 859 | sage: P.difference(Face([0,0,0],2)) |
| 860 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 3]*] |
| 861 | sage: P.difference(P) |
| 862 | Patch: [] |
| 863 | """ |
| 864 | if isinstance(other, Face): |
| 865 | return Patch(self._faces.difference([other])) |
| 866 | else: |
| 867 | return Patch(self._faces.difference(other)) |
| 868 | |
| 869 | def dimension(self): |
| 870 | r""" |
| 871 | Returns the dimension of the vectors of the faces of self |
| 872 | |
| 873 | It returns ``None`` if self is the empty patch. |
| 874 | |
| 875 | The dimension of a patch is the length of the vectors of the faces in the patch, |
| 876 | which is assumed to be the same for every face in the patch. |
| 877 | |
| 878 | EXAMPLES:: |
| 879 | |
| 880 | sage: from sage.combinat.e_one_star import Face, Patch |
| 881 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 882 | sage: P.dimension() |
| 883 | 3 |
| 884 | |
| 885 | TESTS:: |
| 886 | |
| 887 | sage: from sage.combinat.e_one_star import Patch |
| 888 | sage: p = Patch([]) |
| 889 | sage: p.dimension() is None |
| 890 | True |
| 891 | |
| 892 | It works when the patch is created from an iterator:: |
| 893 | |
| 894 | sage: p = Patch(Face((0,0,0),t) for t in [1,2,3]) |
| 895 | sage: p.dimension() |
| 896 | 3 |
| 897 | """ |
| 898 | return self._dimension |
| 899 | |
| 900 | def faces_of_vector(self, v): |
| 901 | r""" |
| 902 | Returns a list of the faces whose vector is ``v``. |
| 903 | |
| 904 | INPUT: |
| 905 | |
| 906 | - ``v`` - a vector |
| 907 | |
| 908 | EXAMPLES:: |
| 909 | |
| 910 | sage: from sage.combinat.e_one_star import Face, Patch |
| 911 | sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)]) |
| 912 | sage: P.faces_of_vector([1,2,0]) |
| 913 | [[(1, 2, 0), 3]*, [(1, 2, 0), 1]*] |
| 914 | """ |
| 915 | v = vector(v) |
| 916 | return [f for f in self if f.vector() == v] |
| 917 | |
| 918 | def faces_of_type(self, t): |
| 919 | r""" |
| 920 | Returns a list of the faces that have type ``t``. |
| 921 | |
| 922 | INPUT: |
| 923 | |
| 924 | - ``t`` - integer or any other type |
| 925 | |
| 926 | EXAMPLES:: |
| 927 | |
| 928 | sage: from sage.combinat.e_one_star import Face, Patch |
| 929 | sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)]) |
| 930 | sage: P.faces_of_type(1) |
| 931 | [[(0, 0, 0), 1]*, [(1, 2, 0), 1]*] |
| 932 | """ |
| 933 | return [f for f in self if f.type() == t] |
| 934 | |
| 935 | def faces_of_color(self, color): |
| 936 | r""" |
| 937 | Returns a list of the faces that have the given color. |
| 938 | |
| 939 | INPUT: |
| 940 | |
| 941 | - ``color`` - color |
| 942 | |
| 943 | EXAMPLES:: |
| 944 | |
| 945 | sage: from sage.combinat.e_one_star import Face, Patch |
| 946 | sage: P = Patch([Face((0,0,0),1, 'red'), Face((1,2,0),3, 'blue'), Face((1,2,0),1, 'red')]) |
| 947 | sage: P.faces_of_color('red') |
| 948 | [[(0, 0, 0), 1]*, [(1, 2, 0), 1]*] |
| 949 | """ |
| 950 | color = tuple(Color(color)) |
| 951 | return [f for f in self if tuple(f.color()) == color] |
| 952 | |
| 953 | def translate(self, v): |
| 954 | r""" |
| 955 | Returns a translated copy of self by vector ``v``. |
| 956 | |
| 957 | INPUT: |
| 958 | |
| 959 | - ``v`` - vector or tuple |
| 960 | |
| 961 | EXAMPLES:: |
| 962 | |
| 963 | sage: from sage.combinat.e_one_star import Face, Patch |
| 964 | sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)]) |
| 965 | sage: P.translate([-1,-2,0]) |
| 966 | Patch: [[(-1, -2, 0), 1]*, [(0, 0, 0), 1]*, [(0, 0, 0), 3]*] |
| 967 | """ |
| 968 | v = vector(v) |
| 969 | return Patch(Face(f.vector()+v, f.type(), f.color()) for f in self) |
| 970 | |
| 971 | def occurrences_of(self, other): |
| 972 | r""" |
| 973 | Returns all positions at which other appears in self, that is, |
| 974 | all vectors v such that ``set(other.translate(v)) <= set(self)``. |
| 975 | |
| 976 | INPUT: |
| 977 | |
| 978 | - ``other`` - a Patch |
| 979 | |
| 980 | OUTPUT: |
| 981 | |
| 982 | a list of vectors |
| 983 | |
| 984 | EXAMPLES:: |
| 985 | |
| 986 | sage: from sage.combinat.e_one_star import Face, Patch, E1Star |
| 987 | sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2), Face([0,0,0], 3)]) |
| 988 | sage: Q = Patch([Face([0,0,0], 1), Face([0,0,0], 2)]) |
| 989 | sage: P.occurrences_of(Q) |
| 990 | [(0, 0, 0)] |
| 991 | sage: Q = Q.translate([1,2,3]) |
| 992 | sage: P.occurrences_of(Q) |
| 993 | [(-1, -2, -3)] |
| 994 | |
| 995 | :: |
| 996 | |
| 997 | sage: E = E1Star(WordMorphism({1:[1,2], 2:[1,3], 3:[1]})) |
| 998 | sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2), Face([0,0,0], 3)]) |
| 999 | sage: P = E(P,4) |
| 1000 | sage: Q = Patch([Face([0,0,0], 1), Face([0,0,0], 2)]) |
| 1001 | sage: L = P.occurrences_of(Q) |
| 1002 | sage: sorted(L) |
| 1003 | [(0, 0, 0), (0, 0, 1), (0, 1, -1), (1, 0, -1), (1, 1, -3), (1, 1, -2)] |
| 1004 | """ |
| 1005 | f0 = next(iter(other)) |
| 1006 | x = f0.vector() |
| 1007 | t = f0.type() |
| 1008 | L = self.faces_of_type(t) |
| 1009 | positions = [] |
| 1010 | for f in L: |
| 1011 | y = f.vector() |
| 1012 | if other.translate(y-x)._faces.issubset(self._faces): |
| 1013 | positions.append(y-x) |
| 1014 | return positions |
| 1015 | |
| 1016 | def apply_substitution(self, E, iterations=1): |
| 1017 | r""" |
| 1018 | Returns the image of self by the E1Star substitution ``E``. |
| 1019 | |
| 1020 | The color of every new face in the image is given the same color as its preimage. |
| 1021 | |
| 1022 | INPUT: |
| 1023 | |
| 1024 | - ``E`` - an instance of the ``E1Star`` class. Its domain alphabet must |
| 1025 | be of the same size as the dimension of the ambient space of the |
| 1026 | faces. |
| 1027 | - ``iterations`` - integer (optional, default: 1) |
| 1028 | number of times the substitution E is applied |
| 1029 | |
| 1030 | EXAMPLES:: |
| 1031 | |
| 1032 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1033 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1034 | sage: E = E1Star(sigma) |
| 1035 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1036 | sage: P.apply_substitution(E,6) #not tested |
| 1037 | Patch of 105 faces |
| 1038 | """ |
| 1039 | if not isinstance(E, E1Star): |
| 1040 | raise TypeError, "E must be an instance of E1Star" |
| 1041 | |
| 1042 | from sage.misc.misc import deprecation |
| 1043 | deprecation("Objects sage.combinat.e_one_star.Patch " + \ |
| 1044 | "are immutable since Sage-4.7.1. " + \ |
| 1045 | "Use the usual calling method of E1Star (E(P)).") |
| 1046 | return E(self, iterations=iterations) |
| 1047 | |
| 1048 | def repaint(self, cmap='Set1'): |
| 1049 | r""" |
| 1050 | Repaints all the faces of self from the given color map. |
| 1051 | |
| 1052 | This only changes the colors of the faces of self. |
| 1053 | |
| 1054 | INPUT: |
| 1055 | |
| 1056 | - ``cmap`` - color map (default: ``'Set1'``). It can be one of the |
| 1057 | following : |
| 1058 | |
| 1059 | - string - A coloring map. For available coloring map names type: |
| 1060 | ``sorted(colormaps)`` |
| 1061 | - list - a list of colors to assign cyclically to the faces. |
| 1062 | A list of a single color colors all the faces with the same color. |
| 1063 | - dict - a dict of face types mapped to colors, to color the |
| 1064 | faces according to their type. |
| 1065 | - ``{}``, the empty dict - shorcut for |
| 1066 | ``{1:'red', 2:'green', 3:'blue'}``. |
| 1067 | |
| 1068 | EXAMPLES: |
| 1069 | |
| 1070 | Using a color map:: |
| 1071 | |
| 1072 | sage: from sage.combinat.e_one_star import Face, Patch |
| 1073 | sage: color = (0, 0, 0) |
| 1074 | sage: P = Patch([Face((0,0,0),t,color) for t in [1,2,3]]) |
| 1075 | sage: for f in P: f.color() |
| 1076 | RGB color (0.0, 0.0, 0.0) |
| 1077 | RGB color (0.0, 0.0, 0.0) |
| 1078 | RGB color (0.0, 0.0, 0.0) |
| 1079 | sage: P.repaint() |
| 1080 | sage: next(iter(P)).color() #random |
| 1081 | RGB color (0.498..., 0.432..., 0.522...) |
| 1082 | |
| 1083 | Using a list of colors:: |
| 1084 | |
| 1085 | sage: P = Patch([Face((0,0,0),t,color) for t in [1,2,3]]) |
| 1086 | sage: P.repaint([(0.9, 0.9, 0.9), (0.65,0.65,0.65), (0.4,0.4,0.4)]) |
| 1087 | sage: for f in P: f.color() |
| 1088 | RGB color (0.9, 0.9, 0.9) |
| 1089 | RGB color (0.65, 0.65, 0.65) |
| 1090 | RGB color (0.4, 0.4, 0.4) |
| 1091 | |
| 1092 | Using a dictionary to color faces according to their type:: |
| 1093 | |
| 1094 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1095 | sage: P.repaint({1:'black', 2:'yellow', 3:'green'}) |
| 1096 | sage: P.plot() #not tested |
| 1097 | sage: P.repaint({}) |
| 1098 | sage: P.plot() #not tested |
| 1099 | """ |
| 1100 | if cmap == {}: |
| 1101 | cmap = {1: 'red', 2:'green', 3:'blue'} |
| 1102 | |
| 1103 | if isinstance(cmap, dict): |
| 1104 | for f in self: |
| 1105 | f.color(cmap[f.type()]) |
| 1106 | |
| 1107 | elif isinstance(cmap, list): |
| 1108 | L = len(cmap) |
| 1109 | for i, f in enumerate(self): |
| 1110 | f.color(cmap[i % L]) |
| 1111 | |
| 1112 | elif isinstance(cmap, str): |
| 1113 | # matplotlib color maps |
| 1114 | global cm |
| 1115 | if not cm: |
| 1116 | from matplotlib import cm |
| 1117 | |
| 1118 | if not cmap in cm.datad.keys(): |
| 1119 | raise RuntimeError("Color map %s not known (type sorted(colors) for valid names)" % cmap) |
| 1120 | cmap = cm.__dict__[cmap] |
| 1121 | dim = float(len(self)) |
| 1122 | for i,f in enumerate(self): |
| 1123 | f.color(cmap(i/dim)[:3]) |
| 1124 | |
| 1125 | else: |
| 1126 | raise TypeError, "Type of cmap (=%s) must be dict, list or str" %cmap |
| 1127 | |
| 1128 | def plot(self, projmat=None, opacity=0.75): |
| 1129 | r""" |
| 1130 | Returns a 2D graphic object depicting the patch. |
| 1131 | |
| 1132 | INPUT: |
| 1133 | |
| 1134 | - ``projmat`` - matrix (optional, default: ``None``) the projection |
| 1135 | matrix. Its number of lines must be two. Its number of columns |
| 1136 | must equal the dimension of the ambient space of the faces. If |
| 1137 | ``None``, the isometric projection is used by default. |
| 1138 | |
| 1139 | - ``opacity`` - float between ``0`` and ``1`` (optional, default: ``0.75``) |
| 1140 | opacity of the the face |
| 1141 | |
| 1142 | .. WARNING:: |
| 1143 | |
| 1144 | Plotting is implemented only for patches in two or three dimensions. |
| 1145 | |
| 1146 | EXAMPLES:: |
| 1147 | |
| 1148 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1149 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1150 | sage: P.plot() |
| 1151 | |
| 1152 | :: |
| 1153 | |
| 1154 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1155 | sage: E = E1Star(sigma) |
| 1156 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1157 | sage: P = E(P, 5) |
| 1158 | sage: P.plot() |
| 1159 | |
| 1160 | Plot with a different projection matrix:: |
| 1161 | |
| 1162 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1163 | sage: E = E1Star(sigma) |
| 1164 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1165 | sage: M = matrix(2, 3, [1,0,-1,0.3,1,-3]) |
| 1166 | sage: P = E(P, 3) |
| 1167 | sage: P.plot(projmat=M) |
| 1168 | |
| 1169 | Plot patches made of unit segments:: |
| 1170 | |
| 1171 | sage: P = Patch([Face([0,0], 1), Face([0,0], 2)]) |
| 1172 | sage: E = E1Star(WordMorphism({1:[1,2],2:[1]})) |
| 1173 | sage: F = E1Star(WordMorphism({1:[1,1,2],2:[2,1]})) |
| 1174 | sage: E(P,5).plot() |
| 1175 | sage: F(P,3).plot() |
| 1176 | """ |
| 1177 | if self.dimension() == 2: |
| 1178 | G = Graphics() |
| 1179 | for face in self: |
| 1180 | G += face._plot(None, None, 1) |
| 1181 | G.set_aspect_ratio(1) |
| 1182 | return G |
| 1183 | |
| 1184 | if self.dimension() == 3: |
| 1185 | if projmat == None: |
| 1186 | projmat = matrix(2, [-1.7320508075688772*0.5, 1.7320508075688772*0.5, 0, -0.5, -0.5, 1]) |
| 1187 | |
| 1188 | G = Graphics() |
| 1189 | for face in self: |
| 1190 | G += face._plot(projmat, self._face_contour, opacity) |
| 1191 | G.set_aspect_ratio(1) |
| 1192 | return G |
| 1193 | |
| 1194 | else: |
| 1195 | raise NotImplementedError, "Plotting is implemented only for patches in two or three dimensions." |
| 1196 | |
| 1197 | def plot3d(self): |
| 1198 | r""" |
| 1199 | Returns a 3D graphics object depicting the patch. |
| 1200 | |
| 1201 | .. WARNING:: |
| 1202 | |
| 1203 | 3D plotting is implemented only for patches in three dimensions. |
| 1204 | |
| 1205 | EXAMPLES:: |
| 1206 | |
| 1207 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1208 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1209 | sage: P.plot3d() #not tested |
| 1210 | |
| 1211 | :: |
| 1212 | |
| 1213 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1214 | sage: E = E1Star(sigma) |
| 1215 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1216 | sage: P = E(P, 5) |
| 1217 | sage: P.repaint() |
| 1218 | sage: P.plot3d() #not tested |
| 1219 | """ |
| 1220 | if self.dimension() != 3: |
| 1221 | raise NotImplementedError, "3D plotting is implemented only for patches in three dimensions." |
| 1222 | |
| 1223 | face_list = [face._plot3d(self._face_contour) for face in self] |
| 1224 | G = sum(face_list) |
| 1225 | return G |
| 1226 | |
| 1227 | def plot_tikz(self, projmat=None, print_tikz_env=True, edgecolor='black', |
| 1228 | scale=0.25, drawzero=False, extra_code_before='', extra_code_after=''): |
| 1229 | r""" |
| 1230 | Returns a string containing some TikZ code to be included into |
| 1231 | a LaTeX document, depicting the patch. |
| 1232 | |
| 1233 | .. WARNING:: |
| 1234 | |
| 1235 | Tikz Plotting is implemented only for patches in three dimensions. |
| 1236 | |
| 1237 | INPUT: |
| 1238 | |
| 1239 | - ``projmat`` - matrix (optional, default: ``None``) the projection |
| 1240 | matrix. Its number of lines must be two. Its number of columns |
| 1241 | must equal the dimension of the ambient space of the faces. If |
| 1242 | ``None``, the isometric projection is used by default. |
| 1243 | - ``print_tikz_env`` - bool (optional, default: ``True``) if ``True``, |
| 1244 | the tikzpicture environment are printed |
| 1245 | - ``edgecolor`` - string (optional, default: ``'black'``) either |
| 1246 | ``'black'`` or ``'facecolor'`` (color of unit face edges) |
| 1247 | - ``scale`` - real number (optional, default: ``0.25``) scaling |
| 1248 | constant for the whole figure |
| 1249 | - ``drawzero`` - bool (optional, default: ``False``) if ``True``, |
| 1250 | mark the origin by a black dot |
| 1251 | - ``extra_code_before`` - string (optional, default: ``''``) extra code to |
| 1252 | include in the tikz picture |
| 1253 | - ``extra_code_after`` - string (optional, default: ``''``) extra code to |
| 1254 | include in the tikz picture |
| 1255 | |
| 1256 | EXAMPLES:: |
| 1257 | |
| 1258 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1259 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1260 | sage: s = P.plot_tikz() |
| 1261 | sage: len(s) |
| 1262 | 602 |
| 1263 | sage: print s #not tested |
| 1264 | \begin{tikzpicture} |
| 1265 | [x={(-0.216506cm,-0.125000cm)}, y={(0.216506cm,-0.125000cm)}, z={(0.000000cm,0.250000cm)}] |
| 1266 | \definecolor{facecolor}{rgb}{0.000,1.000,0.000} |
| 1267 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 1268 | (0, 0, 0) -- (0, 0, 1) -- (1, 0, 1) -- (1, 0, 0) -- cycle; |
| 1269 | \definecolor{facecolor}{rgb}{1.000,0.000,0.000} |
| 1270 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 1271 | (0, 0, 0) -- (0, 1, 0) -- (0, 1, 1) -- (0, 0, 1) -- cycle; |
| 1272 | \definecolor{facecolor}{rgb}{0.000,0.000,1.000} |
| 1273 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 1274 | (0, 0, 0) -- (1, 0, 0) -- (1, 1, 0) -- (0, 1, 0) -- cycle; |
| 1275 | \end{tikzpicture} |
| 1276 | |
| 1277 | :: |
| 1278 | |
| 1279 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1280 | sage: E = E1Star(sigma) |
| 1281 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1282 | sage: P = E(P, 4) |
| 1283 | sage: from sage.misc.latex import latex #not tested |
| 1284 | sage: latex.add_to_preamble('\\usepackage{tikz}') #not tested |
| 1285 | sage: view(P, tightpage=true) #not tested |
| 1286 | |
| 1287 | Plot using shades of gray (useful for article figures):: |
| 1288 | |
| 1289 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1290 | sage: E = E1Star(sigma) |
| 1291 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1292 | sage: P.repaint([(0.9, 0.9, 0.9), (0.65,0.65,0.65), (0.4,0.4,0.4)]) |
| 1293 | sage: P = E(P, 4) |
| 1294 | sage: s = P.plot_tikz() |
| 1295 | |
| 1296 | Plotting with various options:: |
| 1297 | |
| 1298 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1299 | sage: E = E1Star(sigma) |
| 1300 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1301 | sage: M = matrix(2, 3, map(float, [1,0,-0.7071,0,1,-0.7071])) |
| 1302 | sage: P = E(P, 3) |
| 1303 | sage: s = P.plot_tikz(projmat=M, edgecolor='facecolor', scale=0.6, drawzero=True) |
| 1304 | |
| 1305 | Adding X, Y, Z axes using the extra code feature:: |
| 1306 | |
| 1307 | sage: length = 1.5 |
| 1308 | sage: space = 0.3 |
| 1309 | sage: axes = '' |
| 1310 | sage: axes += "\\draw[->, thick, black] (0,0,0) -- (%s, 0, 0);\n" % length |
| 1311 | sage: axes += "\\draw[->, thick, black] (0,0,0) -- (0, %s, 0);\n" % length |
| 1312 | sage: axes += "\\node at (%s,0,0) {$x$};\n" % (length + space) |
| 1313 | sage: axes += "\\node at (0,%s,0) {$y$};\n" % (length + space) |
| 1314 | sage: axes += "\\node at (0,0,%s) {$z$};\n" % (length + space) |
| 1315 | sage: axes += "\\draw[->, thick, black] (0,0,0) -- (0, 0, %s);\n" % length |
| 1316 | sage: cube = Patch([Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)]) |
| 1317 | sage: options = dict(scale=0.5,drawzero=True,extra_code_before=axes) |
| 1318 | sage: s = cube.plot_tikz(**options) |
| 1319 | sage: len(s) |
| 1320 | 986 |
| 1321 | sage: print s #not tested |
| 1322 | \begin{tikzpicture} |
| 1323 | [x={(-0.433013cm,-0.250000cm)}, y={(0.433013cm,-0.250000cm)}, z={(0.000000cm,0.500000cm)}] |
| 1324 | \draw[->, thick, black] (0,0,0) -- (1.50000000000000, 0, 0); |
| 1325 | \draw[->, thick, black] (0,0,0) -- (0, 1.50000000000000, 0); |
| 1326 | \node at (1.80000000000000,0,0) {$x$}; |
| 1327 | \node at (0,1.80000000000000,0) {$y$}; |
| 1328 | \node at (0,0,1.80000000000000) {$z$}; |
| 1329 | \draw[->, thick, black] (0,0,0) -- (0, 0, 1.50000000000000); |
| 1330 | \definecolor{facecolor}{rgb}{0.000,1.000,0.000} |
| 1331 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 1332 | (0, 0, 0) -- (0, 0, 1) -- (1, 0, 1) -- (1, 0, 0) -- cycle; |
| 1333 | \definecolor{facecolor}{rgb}{1.000,0.000,0.000} |
| 1334 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 1335 | (0, 0, 0) -- (0, 1, 0) -- (0, 1, 1) -- (0, 0, 1) -- cycle; |
| 1336 | \definecolor{facecolor}{rgb}{0.000,0.000,1.000} |
| 1337 | \fill[fill=facecolor, draw=black, shift={(0,0,0)}] |
| 1338 | (0, 0, 0) -- (1, 0, 0) -- (1, 1, 0) -- (0, 1, 0) -- cycle; |
| 1339 | \node[circle,fill=black,draw=black,minimum size=1.5mm,inner sep=0pt] at (0,0,0) {}; |
| 1340 | \end{tikzpicture} |
| 1341 | """ |
| 1342 | if self.dimension() != 3: |
| 1343 | raise NotImplementedError, "Tikz Plotting is implemented only for patches in three dimensions." |
| 1344 | |
| 1345 | if projmat == None: |
| 1346 | projmat = matrix(2, [-1.7320508075688772*0.5, 1.7320508075688772*0.5, 0, -0.5, -0.5, 1])*scale |
| 1347 | |
| 1348 | e1 = projmat*vector([1,0,0]) |
| 1349 | e2 = projmat*vector([0,1,0]) |
| 1350 | e3 = projmat*vector([0,0,1]) |
| 1351 | face_contour = self._face_contour |
| 1352 | color = () |
| 1353 | |
| 1354 | # string s contains the TiKZ code of the patch |
| 1355 | s = '' |
| 1356 | |
| 1357 | if print_tikz_env: |
| 1358 | s += '\\begin{tikzpicture}\n' |
| 1359 | s += '[x={(%fcm,%fcm)}, y={(%fcm,%fcm)}, z={(%fcm,%fcm)}]\n'%(e1[0], e1[1], e2[0], e2[1], e3[0], e3[1]) |
| 1360 | |
| 1361 | s += extra_code_before |
| 1362 | |
| 1363 | for f in self: |
| 1364 | t = f.type() |
| 1365 | x, y, z = f.vector() |
| 1366 | |
| 1367 | if tuple(color) != tuple(f.color()): #tuple is needed, comparison for RGB fails |
| 1368 | color = f.color() |
| 1369 | s += '\\definecolor{facecolor}{rgb}{%.3f,%.3f,%.3f}\n'%(color[0], color[1], color[2]) |
| 1370 | |
| 1371 | s += '\\fill[fill=facecolor, draw=%s, shift={(%d,%d,%d)}]\n'%(edgecolor, x, y, z) |
| 1372 | s += ' -- '.join(map(str, face_contour[t])) + ' -- cycle;\n' |
| 1373 | |
| 1374 | s += extra_code_after |
| 1375 | |
| 1376 | if drawzero: |
| 1377 | s += '\\node[circle,fill=black,draw=black,minimum size=1.5mm,inner sep=0pt] at (0,0,0) {};\n' |
| 1378 | |
| 1379 | if print_tikz_env: |
| 1380 | s += '\\end{tikzpicture}' |
| 1381 | |
| 1382 | return LatexExpr(s) |
| 1383 | |
| 1384 | _latex_ = plot_tikz |
| 1385 | |
| 1386 | class E1Star(SageObject): |
| 1387 | r""" |
| 1388 | A class to model the `E_1^*(\sigma)` map associated with |
| 1389 | a unimodular substitution `\sigma`. |
| 1390 | |
| 1391 | INPUT: |
| 1392 | |
| 1393 | - ``sigma`` - unimodular ``WordMorphism``, i.e. such that its incidence |
| 1394 | matrix has determinant `\pm 1`. |
| 1395 | |
| 1396 | - ``method`` - 'prefix' or 'suffix' (optional, default: 'suffix') |
| 1397 | Enables to use an alternative definition `E_1^*(\sigma)` substitutions, |
| 1398 | where the abelianized of the prefix` is used instead of the suffix. |
| 1399 | |
| 1400 | .. NOTE:: |
| 1401 | |
| 1402 | The alphabet of the domain and the codomain of `\sigma` must be |
| 1403 | equal, and they must be of the form ``[1, ..., d]``, where ``d`` |
| 1404 | is a positive integer corresponding to the length of the vectors |
| 1405 | of the faces on which `E_1^*(\sigma)` will act. |
| 1406 | |
| 1407 | EXAMPLES:: |
| 1408 | |
| 1409 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1410 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1411 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1412 | sage: E = E1Star(sigma) |
| 1413 | sage: E(P) |
| 1414 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*] |
| 1415 | |
| 1416 | :: |
| 1417 | |
| 1418 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1419 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1420 | sage: E = E1Star(sigma, method='prefix') |
| 1421 | sage: E(P) |
| 1422 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 0, 1), 1]*, [(0, 0, 1), 2]*] |
| 1423 | |
| 1424 | :: |
| 1425 | |
| 1426 | sage: x = [Face((0,0,0,0),1), Face((0,0,0,0),4)] |
| 1427 | sage: P = Patch(x) |
| 1428 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1,4], 4:[1]}) |
| 1429 | sage: E = E1Star(sigma) |
| 1430 | sage: E(P) |
| 1431 | Patch: [[(0, 0, 0, 0), 3]*, [(0, 0, 0, 0), 4]*, [(0, 0, 1, -1), 3]*, [(0, 1, 0, -1), 2]*, [(1, 0, 0, -1), 1]*] |
| 1432 | """ |
| 1433 | def __init__(self, sigma, method='suffix'): |
| 1434 | r""" |
| 1435 | E1Star constructor. See class doc for more information. |
| 1436 | |
| 1437 | EXAMPLES:: |
| 1438 | |
| 1439 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1440 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1441 | sage: E = E1Star(sigma) |
| 1442 | sage: E |
| 1443 | E_1^*(WordMorphism: 1->12, 2->13, 3->1) |
| 1444 | """ |
| 1445 | if not isinstance(sigma, WordMorphism): |
| 1446 | raise TypeError, "sigma (=%s) must be an instance of WordMorphism"%sigma |
| 1447 | |
| 1448 | if sigma.domain().alphabet() != sigma.codomain().alphabet(): |
| 1449 | raise ValueError, "The domain and codomain of (%s) must be the same."%sigma |
| 1450 | |
| 1451 | if abs(det(matrix(sigma))) != 1: |
| 1452 | raise ValueError, "The substitution (%s) must be unimodular."%sigma |
| 1453 | |
| 1454 | first_letter = sigma.codomain().alphabet()[0] |
| 1455 | if not (first_letter in ZZ) or (first_letter < 1): |
| 1456 | raise ValueError, "The substitution (%s) must be defined on positive integers."%sigma |
| 1457 | |
| 1458 | self._sigma = WordMorphism(sigma) |
| 1459 | self._d = self._sigma.domain().size_of_alphabet() |
| 1460 | |
| 1461 | # self._base_iter is a base for the iteration of the application of self on set |
| 1462 | # of faces. (Exploits the linearity of `E_1^*(\sigma)` to optimize computation.) |
| 1463 | alphabet = self._sigma.domain().alphabet() |
| 1464 | X = {} |
| 1465 | for k in alphabet: |
| 1466 | subst_im = self._sigma.image(k) |
| 1467 | for n, letter in enumerate(subst_im): |
| 1468 | if method == 'suffix': |
| 1469 | image_word = subst_im[n+1:] |
| 1470 | elif method == 'prefix': |
| 1471 | image_word = subst_im[:n] |
| 1472 | else: |
| 1473 | raise ValueError, "Option 'method' can only be 'prefix' or 'suffix'." |
| 1474 | if not letter in X: |
| 1475 | X[letter] = [] |
| 1476 | v = self.inverse_matrix()*vector(image_word.parikh_vector()) |
| 1477 | X[letter].append((v, k)) |
| 1478 | self._base_iter = X |
| 1479 | |
| 1480 | def __eq__(self, other): |
| 1481 | r""" |
| 1482 | Equality test for E1Star morphisms. |
| 1483 | |
| 1484 | INPUT: |
| 1485 | |
| 1486 | - ``other`` - an object |
| 1487 | |
| 1488 | EXAMPLES:: |
| 1489 | |
| 1490 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1491 | sage: s = WordMorphism({1:[1,3], 2:[1,2,3], 3:[3]}) |
| 1492 | sage: t = WordMorphism({1:[1,2,3], 2:[2,3], 3:[3]}) |
| 1493 | sage: S = E1Star(s) |
| 1494 | sage: T = E1Star(t) |
| 1495 | sage: S == T |
| 1496 | False |
| 1497 | sage: S2 = E1Star(s, method='prefix') |
| 1498 | sage: S == S2 |
| 1499 | False |
| 1500 | """ |
| 1501 | return (isinstance(other, E1Star) and self._base_iter == other._base_iter) |
| 1502 | |
| 1503 | def __call__(self, patch, iterations=1): |
| 1504 | r""" |
| 1505 | Applies a generalized substitution to a Patch; this returns a new object. |
| 1506 | |
| 1507 | The color of every new face in the image is given the same color as its preimage. |
| 1508 | |
| 1509 | INPUT: |
| 1510 | |
| 1511 | - ``patch`` - a patch |
| 1512 | - ``iterations`` - integer (optional, default: 1) number of iterations |
| 1513 | |
| 1514 | OUTPUT: |
| 1515 | |
| 1516 | a patch |
| 1517 | |
| 1518 | EXAMPLES:: |
| 1519 | |
| 1520 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1521 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1522 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1523 | sage: E = E1Star(sigma) |
| 1524 | sage: E(P) |
| 1525 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*] |
| 1526 | sage: E(P, iterations=4) |
| 1527 | Patch of 31 faces |
| 1528 | |
| 1529 | TEST: |
| 1530 | |
| 1531 | We test that iterations=0 works (see #10699):: |
| 1532 | |
| 1533 | sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]]) |
| 1534 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1535 | sage: E = E1Star(sigma) |
| 1536 | sage: E(P, iterations=0) |
| 1537 | Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*] |
| 1538 | """ |
| 1539 | if iterations == 0: |
| 1540 | return Patch(patch) |
| 1541 | elif iterations < 0: |
| 1542 | raise ValueError, "iterations (=%s) must be >= 0." % iterations |
| 1543 | else: |
| 1544 | old_faces = patch |
| 1545 | for i in xrange(iterations): |
| 1546 | new_faces = [] |
| 1547 | for f in old_faces: |
| 1548 | new_faces.extend(self._call_on_face(f, color=f.color())) |
| 1549 | old_faces = new_faces |
| 1550 | return Patch(new_faces) |
| 1551 | |
| 1552 | def __mul__(self, other): |
| 1553 | r""" |
| 1554 | Return the product of self and other. |
| 1555 | |
| 1556 | The product satisfies the following rule : |
| 1557 | `E_1^*(\sigma\circ\sigma') = E_1^*(\sigma')` \circ E_1^*(\sigma)` |
| 1558 | |
| 1559 | INPUT: |
| 1560 | |
| 1561 | - ``other`` - an instance of E1Star |
| 1562 | |
| 1563 | OUTPUT: |
| 1564 | |
| 1565 | an instance of E1Star |
| 1566 | |
| 1567 | EXAMPLES:: |
| 1568 | |
| 1569 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1570 | sage: s = WordMorphism({1:[2],2:[3],3:[1,2]}) |
| 1571 | sage: t = WordMorphism({1:[1,3,1],2:[1],3:[1,1,3,2]}) |
| 1572 | sage: E1Star(s) * E1Star(t) |
| 1573 | E_1^*(WordMorphism: 1->1, 2->1132, 3->1311) |
| 1574 | sage: E1Star(t * s) |
| 1575 | E_1^*(WordMorphism: 1->1, 2->1132, 3->1311) |
| 1576 | """ |
| 1577 | if not isinstance(other, E1Star): |
| 1578 | raise TypeError, "other (=%s) must be an instance of E1Star" % other |
| 1579 | return E1Star(other.sigma() * self.sigma()) |
| 1580 | |
| 1581 | def __repr__(self): |
| 1582 | r""" |
| 1583 | String representation of a patch. |
| 1584 | |
| 1585 | EXAMPLES:: |
| 1586 | |
| 1587 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1588 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1589 | sage: E = E1Star(sigma) |
| 1590 | sage: E |
| 1591 | E_1^*(WordMorphism: 1->12, 2->13, 3->1) |
| 1592 | """ |
| 1593 | return "E_1^*(%s)" % str(self._sigma) |
| 1594 | |
| 1595 | def _call_on_face(self, face, color=None): |
| 1596 | r""" |
| 1597 | Returns an iterator of faces obtained by applying self on the face. |
| 1598 | |
| 1599 | INPUT: |
| 1600 | |
| 1601 | - ``face`` - a face |
| 1602 | - ``color`` - string, RGB tuple or color, (optional, default: None) |
| 1603 | RGB color |
| 1604 | |
| 1605 | OUTPUT: |
| 1606 | |
| 1607 | iterator of faces |
| 1608 | |
| 1609 | EXAMPLES:: |
| 1610 | |
| 1611 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1612 | sage: f = Face((0,2,0), 1) |
| 1613 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1614 | sage: E = E1Star(sigma) |
| 1615 | sage: list(E._call_on_face(f)) |
| 1616 | [[(3, 0, -3), 1]*, [(2, 1, -3), 2]*, [(2, 0, -2), 3]*] |
| 1617 | """ |
| 1618 | if len(face.vector()) != self._d: |
| 1619 | raise ValueError, "The dimension of the faces must be equal to the size of the alphabet of the substitution." |
| 1620 | x_new = self.inverse_matrix() * face.vector() |
| 1621 | t = face.type() |
| 1622 | return (Face(x_new + v, k, color=color) for v, k in self._base_iter[t]) |
| 1623 | |
| 1624 | @cached_method |
| 1625 | def matrix(self): |
| 1626 | r""" |
| 1627 | Returns the matrix associated with self. |
| 1628 | |
| 1629 | EXAMPLES:: |
| 1630 | |
| 1631 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1632 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1633 | sage: E = E1Star(sigma) |
| 1634 | sage: E.matrix() |
| 1635 | [1 1 1] |
| 1636 | [1 0 0] |
| 1637 | [0 1 0] |
| 1638 | """ |
| 1639 | return self._sigma.incidence_matrix() |
| 1640 | |
| 1641 | @cached_method |
| 1642 | def inverse_matrix(self): |
| 1643 | r""" |
| 1644 | Returns the inverse of the matrix associated with self. |
| 1645 | |
| 1646 | EXAMPLES:: |
| 1647 | |
| 1648 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1649 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1650 | sage: E = E1Star(sigma) |
| 1651 | sage: E.inverse_matrix() |
| 1652 | [ 0 1 0] |
| 1653 | [ 0 0 1] |
| 1654 | [ 1 -1 -1] |
| 1655 | |
| 1656 | """ |
| 1657 | return self.matrix().inverse() |
| 1658 | |
| 1659 | def sigma(self): |
| 1660 | r""" |
| 1661 | Returns the ``WordMorphism`` associated with self. |
| 1662 | |
| 1663 | EXAMPLES:: |
| 1664 | |
| 1665 | sage: from sage.combinat.e_one_star import E1Star, Face, Patch |
| 1666 | sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]}) |
| 1667 | sage: E = E1Star(sigma) |
| 1668 | sage: print E.sigma() |
| 1669 | WordMorphism: 1->12, 2->13, 3->1 |
| 1670 | """ |
| 1671 | return self._sigma |
| 1672 | |