Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/combinat/e_one_star.py
4058 views
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 <[email protected]>
199
# Vincent Delecroix <[email protected]>
200
# Timo Jolivet <[email protected]>
201
# Stepan Starosta <[email protected]>
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
210
from sage.misc.functional import det
211
from sage.structure.sage_object import SageObject
212
from sage.combinat.words.morphism import WordMorphism
213
from sage.matrix.constructor import matrix
214
from sage.modules.free_module_element import vector
215
from sage.plot.all import Graphics
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
1673
1674