Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/combinat/e_one_star.py
8817 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^*(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 union(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.union(Face((1,2,3), 3))
815
Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*]
816
sage: P.union([Face((1,2,3), 3), Face((2,3,3), 2)])
817
Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(1, 2, 3), 3]*, [(2, 3, 3), 2]*]
818
"""
819
if isinstance(other, Face):
820
return Patch(self._faces.union([other]))
821
else:
822
return Patch(self._faces.union(other))
823
824
def difference(self, other):
825
r"""
826
Returns the difference of self and other.
827
828
INPUT:
829
830
- ``other`` - a finite iterable of faces or a single face
831
832
EXAMPLES::
833
834
sage: from sage.combinat.e_one_star import Face, Patch
835
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
836
sage: P.difference(Face([0,0,0],2))
837
Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 3]*]
838
sage: P.difference(P)
839
Patch: []
840
"""
841
if isinstance(other, Face):
842
return Patch(self._faces.difference([other]))
843
else:
844
return Patch(self._faces.difference(other))
845
846
def dimension(self):
847
r"""
848
Returns the dimension of the vectors of the faces of self
849
850
It returns ``None`` if self is the empty patch.
851
852
The dimension of a patch is the length of the vectors of the faces in the patch,
853
which is assumed to be the same for every face in the patch.
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.dimension()
860
3
861
862
TESTS::
863
864
sage: from sage.combinat.e_one_star import Patch
865
sage: p = Patch([])
866
sage: p.dimension() is None
867
True
868
869
It works when the patch is created from an iterator::
870
871
sage: p = Patch(Face((0,0,0),t) for t in [1,2,3])
872
sage: p.dimension()
873
3
874
"""
875
return self._dimension
876
877
def faces_of_vector(self, v):
878
r"""
879
Returns a list of the faces whose vector is ``v``.
880
881
INPUT:
882
883
- ``v`` - a vector
884
885
EXAMPLES::
886
887
sage: from sage.combinat.e_one_star import Face, Patch
888
sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)])
889
sage: P.faces_of_vector([1,2,0])
890
[[(1, 2, 0), 3]*, [(1, 2, 0), 1]*]
891
"""
892
v = vector(v)
893
return [f for f in self if f.vector() == v]
894
895
def faces_of_type(self, t):
896
r"""
897
Returns a list of the faces that have type ``t``.
898
899
INPUT:
900
901
- ``t`` - integer or any other type
902
903
EXAMPLES::
904
905
sage: from sage.combinat.e_one_star import Face, Patch
906
sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)])
907
sage: P.faces_of_type(1)
908
[[(0, 0, 0), 1]*, [(1, 2, 0), 1]*]
909
"""
910
return [f for f in self if f.type() == t]
911
912
def faces_of_color(self, color):
913
r"""
914
Returns a list of the faces that have the given color.
915
916
INPUT:
917
918
- ``color`` - color
919
920
EXAMPLES::
921
922
sage: from sage.combinat.e_one_star import Face, Patch
923
sage: P = Patch([Face((0,0,0),1, 'red'), Face((1,2,0),3, 'blue'), Face((1,2,0),1, 'red')])
924
sage: P.faces_of_color('red')
925
[[(0, 0, 0), 1]*, [(1, 2, 0), 1]*]
926
"""
927
color = tuple(Color(color))
928
return [f for f in self if tuple(f.color()) == color]
929
930
def translate(self, v):
931
r"""
932
Returns a translated copy of self by vector ``v``.
933
934
INPUT:
935
936
- ``v`` - vector or tuple
937
938
EXAMPLES::
939
940
sage: from sage.combinat.e_one_star import Face, Patch
941
sage: P = Patch([Face((0,0,0),1), Face((1,2,0),3), Face((1,2,0),1)])
942
sage: P.translate([-1,-2,0])
943
Patch: [[(-1, -2, 0), 1]*, [(0, 0, 0), 1]*, [(0, 0, 0), 3]*]
944
"""
945
v = vector(v)
946
return Patch(Face(f.vector()+v, f.type(), f.color()) for f in self)
947
948
def occurrences_of(self, other):
949
r"""
950
Returns all positions at which other appears in self, that is,
951
all vectors v such that ``set(other.translate(v)) <= set(self)``.
952
953
INPUT:
954
955
- ``other`` - a Patch
956
957
OUTPUT:
958
959
a list of vectors
960
961
EXAMPLES::
962
963
sage: from sage.combinat.e_one_star import Face, Patch, E1Star
964
sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2), Face([0,0,0], 3)])
965
sage: Q = Patch([Face([0,0,0], 1), Face([0,0,0], 2)])
966
sage: P.occurrences_of(Q)
967
[(0, 0, 0)]
968
sage: Q = Q.translate([1,2,3])
969
sage: P.occurrences_of(Q)
970
[(-1, -2, -3)]
971
972
::
973
974
sage: E = E1Star(WordMorphism({1:[1,2], 2:[1,3], 3:[1]}))
975
sage: P = Patch([Face([0,0,0], 1), Face([0,0,0], 2), Face([0,0,0], 3)])
976
sage: P = E(P,4)
977
sage: Q = Patch([Face([0,0,0], 1), Face([0,0,0], 2)])
978
sage: L = P.occurrences_of(Q)
979
sage: sorted(L)
980
[(0, 0, 0), (0, 0, 1), (0, 1, -1), (1, 0, -1), (1, 1, -3), (1, 1, -2)]
981
"""
982
f0 = next(iter(other))
983
x = f0.vector()
984
t = f0.type()
985
L = self.faces_of_type(t)
986
positions = []
987
for f in L:
988
y = f.vector()
989
if other.translate(y-x)._faces.issubset(self._faces):
990
positions.append(y-x)
991
return positions
992
993
def repaint(self, cmap='Set1'):
994
r"""
995
Repaints all the faces of self from the given color map.
996
997
This only changes the colors of the faces of self.
998
999
INPUT:
1000
1001
- ``cmap`` - color map (default: ``'Set1'``). It can be one of the
1002
following :
1003
1004
- string - A coloring map. For available coloring map names type:
1005
``sorted(colormaps)``
1006
- list - a list of colors to assign cyclically to the faces.
1007
A list of a single color colors all the faces with the same color.
1008
- dict - a dict of face types mapped to colors, to color the
1009
faces according to their type.
1010
- ``{}``, the empty dict - shorcut for
1011
``{1:'red', 2:'green', 3:'blue'}``.
1012
1013
EXAMPLES:
1014
1015
Using a color map::
1016
1017
sage: from sage.combinat.e_one_star import Face, Patch
1018
sage: color = (0, 0, 0)
1019
sage: P = Patch([Face((0,0,0),t,color) for t in [1,2,3]])
1020
sage: for f in P: f.color()
1021
RGB color (0.0, 0.0, 0.0)
1022
RGB color (0.0, 0.0, 0.0)
1023
RGB color (0.0, 0.0, 0.0)
1024
sage: P.repaint()
1025
sage: next(iter(P)).color() #random
1026
RGB color (0.498..., 0.432..., 0.522...)
1027
1028
Using a list of colors::
1029
1030
sage: P = Patch([Face((0,0,0),t,color) for t in [1,2,3]])
1031
sage: P.repaint([(0.9, 0.9, 0.9), (0.65,0.65,0.65), (0.4,0.4,0.4)])
1032
sage: for f in P: f.color()
1033
RGB color (0.9, 0.9, 0.9)
1034
RGB color (0.65, 0.65, 0.65)
1035
RGB color (0.4, 0.4, 0.4)
1036
1037
Using a dictionary to color faces according to their type::
1038
1039
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1040
sage: P.repaint({1:'black', 2:'yellow', 3:'green'})
1041
sage: P.plot() #not tested
1042
sage: P.repaint({})
1043
sage: P.plot() #not tested
1044
"""
1045
if cmap == {}:
1046
cmap = {1: 'red', 2:'green', 3:'blue'}
1047
1048
if isinstance(cmap, dict):
1049
for f in self:
1050
f.color(cmap[f.type()])
1051
1052
elif isinstance(cmap, list):
1053
L = len(cmap)
1054
for i, f in enumerate(self):
1055
f.color(cmap[i % L])
1056
1057
elif isinstance(cmap, str):
1058
# matplotlib color maps
1059
global cm
1060
if not cm:
1061
from matplotlib import cm
1062
1063
if not cmap in cm.datad.keys():
1064
raise RuntimeError("Color map %s not known (type sorted(colors) for valid names)" % cmap)
1065
cmap = cm.__dict__[cmap]
1066
dim = float(len(self))
1067
for i,f in enumerate(self):
1068
f.color(cmap(i/dim)[:3])
1069
1070
else:
1071
raise TypeError, "Type of cmap (=%s) must be dict, list or str" %cmap
1072
1073
def plot(self, projmat=None, opacity=0.75):
1074
r"""
1075
Returns a 2D graphic object depicting the patch.
1076
1077
INPUT:
1078
1079
- ``projmat`` - matrix (optional, default: ``None``) the projection
1080
matrix. Its number of lines must be two. Its number of columns
1081
must equal the dimension of the ambient space of the faces. If
1082
``None``, the isometric projection is used by default.
1083
1084
- ``opacity`` - float between ``0`` and ``1`` (optional, default: ``0.75``)
1085
opacity of the the face
1086
1087
.. WARNING::
1088
1089
Plotting is implemented only for patches in two or three dimensions.
1090
1091
EXAMPLES::
1092
1093
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1094
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1095
sage: P.plot()
1096
1097
::
1098
1099
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1100
sage: E = E1Star(sigma)
1101
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1102
sage: P = E(P, 5)
1103
sage: P.plot()
1104
1105
Plot with a different projection matrix::
1106
1107
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1108
sage: E = E1Star(sigma)
1109
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1110
sage: M = matrix(2, 3, [1,0,-1,0.3,1,-3])
1111
sage: P = E(P, 3)
1112
sage: P.plot(projmat=M)
1113
1114
Plot patches made of unit segments::
1115
1116
sage: P = Patch([Face([0,0], 1), Face([0,0], 2)])
1117
sage: E = E1Star(WordMorphism({1:[1,2],2:[1]}))
1118
sage: F = E1Star(WordMorphism({1:[1,1,2],2:[2,1]}))
1119
sage: E(P,5).plot()
1120
sage: F(P,3).plot()
1121
"""
1122
if self.dimension() == 2:
1123
G = Graphics()
1124
for face in self:
1125
G += face._plot(None, None, 1)
1126
G.set_aspect_ratio(1)
1127
return G
1128
1129
if self.dimension() == 3:
1130
if projmat == None:
1131
projmat = matrix(2, [-1.7320508075688772*0.5, 1.7320508075688772*0.5, 0, -0.5, -0.5, 1])
1132
1133
G = Graphics()
1134
for face in self:
1135
G += face._plot(projmat, self._face_contour, opacity)
1136
G.set_aspect_ratio(1)
1137
return G
1138
1139
else:
1140
raise NotImplementedError, "Plotting is implemented only for patches in two or three dimensions."
1141
1142
def plot3d(self):
1143
r"""
1144
Returns a 3D graphics object depicting the patch.
1145
1146
.. WARNING::
1147
1148
3D plotting is implemented only for patches in three dimensions.
1149
1150
EXAMPLES::
1151
1152
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1153
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1154
sage: P.plot3d() #not tested
1155
1156
::
1157
1158
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1159
sage: E = E1Star(sigma)
1160
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1161
sage: P = E(P, 5)
1162
sage: P.repaint()
1163
sage: P.plot3d() #not tested
1164
"""
1165
if self.dimension() != 3:
1166
raise NotImplementedError, "3D plotting is implemented only for patches in three dimensions."
1167
1168
face_list = [face._plot3d(self._face_contour) for face in self]
1169
G = sum(face_list)
1170
return G
1171
1172
def plot_tikz(self, projmat=None, print_tikz_env=True, edgecolor='black',
1173
scale=0.25, drawzero=False, extra_code_before='', extra_code_after=''):
1174
r"""
1175
Returns a string containing some TikZ code to be included into
1176
a LaTeX document, depicting the patch.
1177
1178
.. WARNING::
1179
1180
Tikz Plotting is implemented only for patches in three dimensions.
1181
1182
INPUT:
1183
1184
- ``projmat`` - matrix (optional, default: ``None``) the projection
1185
matrix. Its number of lines must be two. Its number of columns
1186
must equal the dimension of the ambient space of the faces. If
1187
``None``, the isometric projection is used by default.
1188
- ``print_tikz_env`` - bool (optional, default: ``True``) if ``True``,
1189
the tikzpicture environment are printed
1190
- ``edgecolor`` - string (optional, default: ``'black'``) either
1191
``'black'`` or ``'facecolor'`` (color of unit face edges)
1192
- ``scale`` - real number (optional, default: ``0.25``) scaling
1193
constant for the whole figure
1194
- ``drawzero`` - bool (optional, default: ``False``) if ``True``,
1195
mark the origin by a black dot
1196
- ``extra_code_before`` - string (optional, default: ``''``) extra code to
1197
include in the tikz picture
1198
- ``extra_code_after`` - string (optional, default: ``''``) extra code to
1199
include in the tikz picture
1200
1201
EXAMPLES::
1202
1203
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1204
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1205
sage: s = P.plot_tikz()
1206
sage: len(s)
1207
602
1208
sage: print s #not tested
1209
\begin{tikzpicture}
1210
[x={(-0.216506cm,-0.125000cm)}, y={(0.216506cm,-0.125000cm)}, z={(0.000000cm,0.250000cm)}]
1211
\definecolor{facecolor}{rgb}{0.000,1.000,0.000}
1212
\fill[fill=facecolor, draw=black, shift={(0,0,0)}]
1213
(0, 0, 0) -- (0, 0, 1) -- (1, 0, 1) -- (1, 0, 0) -- cycle;
1214
\definecolor{facecolor}{rgb}{1.000,0.000,0.000}
1215
\fill[fill=facecolor, draw=black, shift={(0,0,0)}]
1216
(0, 0, 0) -- (0, 1, 0) -- (0, 1, 1) -- (0, 0, 1) -- cycle;
1217
\definecolor{facecolor}{rgb}{0.000,0.000,1.000}
1218
\fill[fill=facecolor, draw=black, shift={(0,0,0)}]
1219
(0, 0, 0) -- (1, 0, 0) -- (1, 1, 0) -- (0, 1, 0) -- cycle;
1220
\end{tikzpicture}
1221
1222
::
1223
1224
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1225
sage: E = E1Star(sigma)
1226
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1227
sage: P = E(P, 4)
1228
sage: from sage.misc.latex import latex #not tested
1229
sage: latex.add_to_preamble('\\usepackage{tikz}') #not tested
1230
sage: view(P, tightpage=true) #not tested
1231
1232
Plot using shades of gray (useful for article figures)::
1233
1234
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1235
sage: E = E1Star(sigma)
1236
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1237
sage: P.repaint([(0.9, 0.9, 0.9), (0.65,0.65,0.65), (0.4,0.4,0.4)])
1238
sage: P = E(P, 4)
1239
sage: s = P.plot_tikz()
1240
1241
Plotting with various options::
1242
1243
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1244
sage: E = E1Star(sigma)
1245
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1246
sage: M = matrix(2, 3, map(float, [1,0,-0.7071,0,1,-0.7071]))
1247
sage: P = E(P, 3)
1248
sage: s = P.plot_tikz(projmat=M, edgecolor='facecolor', scale=0.6, drawzero=True)
1249
1250
Adding X, Y, Z axes using the extra code feature::
1251
1252
sage: length = 1.5
1253
sage: space = 0.3
1254
sage: axes = ''
1255
sage: axes += "\\draw[->, thick, black] (0,0,0) -- (%s, 0, 0);\n" % length
1256
sage: axes += "\\draw[->, thick, black] (0,0,0) -- (0, %s, 0);\n" % length
1257
sage: axes += "\\node at (%s,0,0) {$x$};\n" % (length + space)
1258
sage: axes += "\\node at (0,%s,0) {$y$};\n" % (length + space)
1259
sage: axes += "\\node at (0,0,%s) {$z$};\n" % (length + space)
1260
sage: axes += "\\draw[->, thick, black] (0,0,0) -- (0, 0, %s);\n" % length
1261
sage: cube = Patch([Face((0,0,0),1), Face((0,0,0),2), Face((0,0,0),3)])
1262
sage: options = dict(scale=0.5,drawzero=True,extra_code_before=axes)
1263
sage: s = cube.plot_tikz(**options)
1264
sage: len(s)
1265
986
1266
sage: print s #not tested
1267
\begin{tikzpicture}
1268
[x={(-0.433013cm,-0.250000cm)}, y={(0.433013cm,-0.250000cm)}, z={(0.000000cm,0.500000cm)}]
1269
\draw[->, thick, black] (0,0,0) -- (1.50000000000000, 0, 0);
1270
\draw[->, thick, black] (0,0,0) -- (0, 1.50000000000000, 0);
1271
\node at (1.80000000000000,0,0) {$x$};
1272
\node at (0,1.80000000000000,0) {$y$};
1273
\node at (0,0,1.80000000000000) {$z$};
1274
\draw[->, thick, black] (0,0,0) -- (0, 0, 1.50000000000000);
1275
\definecolor{facecolor}{rgb}{0.000,1.000,0.000}
1276
\fill[fill=facecolor, draw=black, shift={(0,0,0)}]
1277
(0, 0, 0) -- (0, 0, 1) -- (1, 0, 1) -- (1, 0, 0) -- cycle;
1278
\definecolor{facecolor}{rgb}{1.000,0.000,0.000}
1279
\fill[fill=facecolor, draw=black, shift={(0,0,0)}]
1280
(0, 0, 0) -- (0, 1, 0) -- (0, 1, 1) -- (0, 0, 1) -- cycle;
1281
\definecolor{facecolor}{rgb}{0.000,0.000,1.000}
1282
\fill[fill=facecolor, draw=black, shift={(0,0,0)}]
1283
(0, 0, 0) -- (1, 0, 0) -- (1, 1, 0) -- (0, 1, 0) -- cycle;
1284
\node[circle,fill=black,draw=black,minimum size=1.5mm,inner sep=0pt] at (0,0,0) {};
1285
\end{tikzpicture}
1286
"""
1287
if self.dimension() != 3:
1288
raise NotImplementedError, "Tikz Plotting is implemented only for patches in three dimensions."
1289
1290
if projmat == None:
1291
projmat = matrix(2, [-1.7320508075688772*0.5, 1.7320508075688772*0.5, 0, -0.5, -0.5, 1])*scale
1292
1293
e1 = projmat*vector([1,0,0])
1294
e2 = projmat*vector([0,1,0])
1295
e3 = projmat*vector([0,0,1])
1296
face_contour = self._face_contour
1297
color = ()
1298
1299
# string s contains the TiKZ code of the patch
1300
s = ''
1301
1302
if print_tikz_env:
1303
s += '\\begin{tikzpicture}\n'
1304
s += '[x={(%fcm,%fcm)}, y={(%fcm,%fcm)}, z={(%fcm,%fcm)}]\n'%(e1[0], e1[1], e2[0], e2[1], e3[0], e3[1])
1305
1306
s += extra_code_before
1307
1308
for f in self:
1309
t = f.type()
1310
x, y, z = f.vector()
1311
1312
if tuple(color) != tuple(f.color()): #tuple is needed, comparison for RGB fails
1313
color = f.color()
1314
s += '\\definecolor{facecolor}{rgb}{%.3f,%.3f,%.3f}\n'%(color[0], color[1], color[2])
1315
1316
s += '\\fill[fill=facecolor, draw=%s, shift={(%d,%d,%d)}]\n'%(edgecolor, x, y, z)
1317
s += ' -- '.join(map(str, face_contour[t])) + ' -- cycle;\n'
1318
1319
s += extra_code_after
1320
1321
if drawzero:
1322
s += '\\node[circle,fill=black,draw=black,minimum size=1.5mm,inner sep=0pt] at (0,0,0) {};\n'
1323
1324
if print_tikz_env:
1325
s += '\\end{tikzpicture}'
1326
1327
return LatexExpr(s)
1328
1329
_latex_ = plot_tikz
1330
1331
class E1Star(SageObject):
1332
r"""
1333
A class to model the `E_1^*(\sigma)` map associated with
1334
a unimodular substitution `\sigma`.
1335
1336
INPUT:
1337
1338
- ``sigma`` - unimodular ``WordMorphism``, i.e. such that its incidence
1339
matrix has determinant `\pm 1`.
1340
1341
- ``method`` - 'prefix' or 'suffix' (optional, default: 'suffix')
1342
Enables to use an alternative definition `E_1^*(\sigma)` substitutions,
1343
where the abelianized of the prefix` is used instead of the suffix.
1344
1345
.. NOTE::
1346
1347
The alphabet of the domain and the codomain of `\sigma` must be
1348
equal, and they must be of the form ``[1, ..., d]``, where ``d``
1349
is a positive integer corresponding to the length of the vectors
1350
of the faces on which `E_1^*(\sigma)` will act.
1351
1352
EXAMPLES::
1353
1354
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1355
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1356
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1357
sage: E = E1Star(sigma)
1358
sage: E(P)
1359
Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*]
1360
1361
::
1362
1363
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1364
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1365
sage: E = E1Star(sigma, method='prefix')
1366
sage: E(P)
1367
Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 0, 1), 1]*, [(0, 0, 1), 2]*]
1368
1369
::
1370
1371
sage: x = [Face((0,0,0,0),1), Face((0,0,0,0),4)]
1372
sage: P = Patch(x)
1373
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1,4], 4:[1]})
1374
sage: E = E1Star(sigma)
1375
sage: E(P)
1376
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]*]
1377
"""
1378
def __init__(self, sigma, method='suffix'):
1379
r"""
1380
E1Star constructor. See class doc for more information.
1381
1382
EXAMPLES::
1383
1384
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1385
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1386
sage: E = E1Star(sigma)
1387
sage: E
1388
E_1^*(1->12, 2->13, 3->1)
1389
"""
1390
if not isinstance(sigma, WordMorphism):
1391
raise TypeError, "sigma (=%s) must be an instance of WordMorphism"%sigma
1392
1393
if sigma.domain().alphabet() != sigma.codomain().alphabet():
1394
raise ValueError, "The domain and codomain of (%s) must be the same."%sigma
1395
1396
if abs(det(matrix(sigma))) != 1:
1397
raise ValueError, "The substitution (%s) must be unimodular."%sigma
1398
1399
first_letter = sigma.codomain().alphabet()[0]
1400
if not (first_letter in ZZ) or (first_letter < 1):
1401
raise ValueError, "The substitution (%s) must be defined on positive integers."%sigma
1402
1403
self._sigma = WordMorphism(sigma)
1404
self._d = self._sigma.domain().size_of_alphabet()
1405
1406
# self._base_iter is a base for the iteration of the application of self on set
1407
# of faces. (Exploits the linearity of `E_1^*(\sigma)` to optimize computation.)
1408
alphabet = self._sigma.domain().alphabet()
1409
X = {}
1410
for k in alphabet:
1411
subst_im = self._sigma.image(k)
1412
for n, letter in enumerate(subst_im):
1413
if method == 'suffix':
1414
image_word = subst_im[n+1:]
1415
elif method == 'prefix':
1416
image_word = subst_im[:n]
1417
else:
1418
raise ValueError, "Option 'method' can only be 'prefix' or 'suffix'."
1419
if not letter in X:
1420
X[letter] = []
1421
v = self.inverse_matrix()*vector(image_word.parikh_vector())
1422
X[letter].append((v, k))
1423
self._base_iter = X
1424
1425
def __eq__(self, other):
1426
r"""
1427
Equality test for E1Star morphisms.
1428
1429
INPUT:
1430
1431
- ``other`` - an object
1432
1433
EXAMPLES::
1434
1435
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1436
sage: s = WordMorphism({1:[1,3], 2:[1,2,3], 3:[3]})
1437
sage: t = WordMorphism({1:[1,2,3], 2:[2,3], 3:[3]})
1438
sage: S = E1Star(s)
1439
sage: T = E1Star(t)
1440
sage: S == T
1441
False
1442
sage: S2 = E1Star(s, method='prefix')
1443
sage: S == S2
1444
False
1445
"""
1446
return (isinstance(other, E1Star) and self._base_iter == other._base_iter)
1447
1448
def __call__(self, patch, iterations=1):
1449
r"""
1450
Applies a generalized substitution to a Patch; this returns a new object.
1451
1452
The color of every new face in the image is given the same color as its preimage.
1453
1454
INPUT:
1455
1456
- ``patch`` - a patch
1457
- ``iterations`` - integer (optional, default: 1) number of iterations
1458
1459
OUTPUT:
1460
1461
a patch
1462
1463
EXAMPLES::
1464
1465
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1466
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1467
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1468
sage: E = E1Star(sigma)
1469
sage: E(P)
1470
Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*, [(0, 1, -1), 2]*, [(1, 0, -1), 1]*]
1471
sage: E(P, iterations=4)
1472
Patch of 31 faces
1473
1474
TEST:
1475
1476
We test that iterations=0 works (see #10699)::
1477
1478
sage: P = Patch([Face((0,0,0),t) for t in [1,2,3]])
1479
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1480
sage: E = E1Star(sigma)
1481
sage: E(P, iterations=0)
1482
Patch: [[(0, 0, 0), 1]*, [(0, 0, 0), 2]*, [(0, 0, 0), 3]*]
1483
"""
1484
if iterations == 0:
1485
return Patch(patch)
1486
elif iterations < 0:
1487
raise ValueError, "iterations (=%s) must be >= 0." % iterations
1488
else:
1489
old_faces = patch
1490
for i in xrange(iterations):
1491
new_faces = []
1492
for f in old_faces:
1493
new_faces.extend(self._call_on_face(f, color=f.color()))
1494
old_faces = new_faces
1495
return Patch(new_faces)
1496
1497
def __mul__(self, other):
1498
r"""
1499
Return the product of self and other.
1500
1501
The product satisfies the following rule :
1502
`E_1^*(\sigma\circ\sigma') = E_1^*(\sigma')` \circ E_1^*(\sigma)`
1503
1504
INPUT:
1505
1506
- ``other`` - an instance of E1Star
1507
1508
OUTPUT:
1509
1510
an instance of E1Star
1511
1512
EXAMPLES::
1513
1514
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1515
sage: s = WordMorphism({1:[2],2:[3],3:[1,2]})
1516
sage: t = WordMorphism({1:[1,3,1],2:[1],3:[1,1,3,2]})
1517
sage: E1Star(s) * E1Star(t)
1518
E_1^*(1->1, 2->1132, 3->1311)
1519
sage: E1Star(t * s)
1520
E_1^*(1->1, 2->1132, 3->1311)
1521
"""
1522
if not isinstance(other, E1Star):
1523
raise TypeError, "other (=%s) must be an instance of E1Star" % other
1524
return E1Star(other.sigma() * self.sigma())
1525
1526
def __repr__(self):
1527
r"""
1528
String representation of a patch.
1529
1530
EXAMPLES::
1531
1532
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1533
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1534
sage: E = E1Star(sigma)
1535
sage: E
1536
E_1^*(1->12, 2->13, 3->1)
1537
"""
1538
return "E_1^*(%s)" % str(self._sigma)
1539
1540
def _call_on_face(self, face, color=None):
1541
r"""
1542
Returns an iterator of faces obtained by applying self on the face.
1543
1544
INPUT:
1545
1546
- ``face`` - a face
1547
- ``color`` - string, RGB tuple or color, (optional, default: None)
1548
RGB color
1549
1550
OUTPUT:
1551
1552
iterator of faces
1553
1554
EXAMPLES::
1555
1556
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1557
sage: f = Face((0,2,0), 1)
1558
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1559
sage: E = E1Star(sigma)
1560
sage: list(E._call_on_face(f))
1561
[[(3, 0, -3), 1]*, [(2, 1, -3), 2]*, [(2, 0, -2), 3]*]
1562
"""
1563
if len(face.vector()) != self._d:
1564
raise ValueError, "The dimension of the faces must be equal to the size of the alphabet of the substitution."
1565
x_new = self.inverse_matrix() * face.vector()
1566
t = face.type()
1567
return (Face(x_new + v, k, color=color) for v, k in self._base_iter[t])
1568
1569
@cached_method
1570
def matrix(self):
1571
r"""
1572
Returns the matrix associated with self.
1573
1574
EXAMPLES::
1575
1576
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1577
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1578
sage: E = E1Star(sigma)
1579
sage: E.matrix()
1580
[1 1 1]
1581
[1 0 0]
1582
[0 1 0]
1583
"""
1584
return self._sigma.incidence_matrix()
1585
1586
@cached_method
1587
def inverse_matrix(self):
1588
r"""
1589
Returns the inverse of the matrix associated with self.
1590
1591
EXAMPLES::
1592
1593
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1594
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1595
sage: E = E1Star(sigma)
1596
sage: E.inverse_matrix()
1597
[ 0 1 0]
1598
[ 0 0 1]
1599
[ 1 -1 -1]
1600
1601
"""
1602
return self.matrix().inverse()
1603
1604
def sigma(self):
1605
r"""
1606
Returns the ``WordMorphism`` associated with self.
1607
1608
EXAMPLES::
1609
1610
sage: from sage.combinat.e_one_star import E1Star, Face, Patch
1611
sage: sigma = WordMorphism({1:[1,2], 2:[1,3], 3:[1]})
1612
sage: E = E1Star(sigma)
1613
sage: E.sigma()
1614
WordMorphism: 1->12, 2->13, 3->1
1615
"""
1616
return self._sigma
1617
1618
1619