Coverage for /usr/lib/python3/dist-packages/sympy/combinatorics/polyhedron.py: 42%
112 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from sympy.combinatorics import Permutation as Perm
2from sympy.combinatorics.perm_groups import PermutationGroup
3from sympy.core import Basic, Tuple, default_sort_key
4from sympy.sets import FiniteSet
5from sympy.utilities.iterables import (minlex, unflatten, flatten)
6from sympy.utilities.misc import as_int
8rmul = Perm.rmul
11class Polyhedron(Basic):
12 """
13 Represents the polyhedral symmetry group (PSG).
15 Explanation
16 ===========
18 The PSG is one of the symmetry groups of the Platonic solids.
19 There are three polyhedral groups: the tetrahedral group
20 of order 12, the octahedral group of order 24, and the
21 icosahedral group of order 60.
23 All doctests have been given in the docstring of the
24 constructor of the object.
26 References
27 ==========
29 .. [1] https://mathworld.wolfram.com/PolyhedralGroup.html
31 """
32 _edges = None
34 def __new__(cls, corners, faces=(), pgroup=()):
35 """
36 The constructor of the Polyhedron group object.
38 Explanation
39 ===========
41 It takes up to three parameters: the corners, faces, and
42 allowed transformations.
44 The corners/vertices are entered as a list of arbitrary
45 expressions that are used to identify each vertex.
47 The faces are entered as a list of tuples of indices; a tuple
48 of indices identifies the vertices which define the face. They
49 should be entered in a cw or ccw order; they will be standardized
50 by reversal and rotation to be give the lowest lexical ordering.
51 If no faces are given then no edges will be computed.
53 >>> from sympy.combinatorics.polyhedron import Polyhedron
54 >>> Polyhedron(list('abc'), [(1, 2, 0)]).faces
55 {(0, 1, 2)}
56 >>> Polyhedron(list('abc'), [(1, 0, 2)]).faces
57 {(0, 1, 2)}
59 The allowed transformations are entered as allowable permutations
60 of the vertices for the polyhedron. Instance of Permutations
61 (as with faces) should refer to the supplied vertices by index.
62 These permutation are stored as a PermutationGroup.
64 Examples
65 ========
67 >>> from sympy.combinatorics.permutations import Permutation
68 >>> from sympy import init_printing
69 >>> from sympy.abc import w, x, y, z
70 >>> init_printing(pretty_print=False, perm_cyclic=False)
72 Here we construct the Polyhedron object for a tetrahedron.
74 >>> corners = [w, x, y, z]
75 >>> faces = [(0, 1, 2), (0, 2, 3), (0, 3, 1), (1, 2, 3)]
77 Next, allowed transformations of the polyhedron must be given. This
78 is given as permutations of vertices.
80 Although the vertices of a tetrahedron can be numbered in 24 (4!)
81 different ways, there are only 12 different orientations for a
82 physical tetrahedron. The following permutations, applied once or
83 twice, will generate all 12 of the orientations. (The identity
84 permutation, Permutation(range(4)), is not included since it does
85 not change the orientation of the vertices.)
87 >>> pgroup = [Permutation([[0, 1, 2], [3]]), \
88 Permutation([[0, 1, 3], [2]]), \
89 Permutation([[0, 2, 3], [1]]), \
90 Permutation([[1, 2, 3], [0]]), \
91 Permutation([[0, 1], [2, 3]]), \
92 Permutation([[0, 2], [1, 3]]), \
93 Permutation([[0, 3], [1, 2]])]
95 The Polyhedron is now constructed and demonstrated:
97 >>> tetra = Polyhedron(corners, faces, pgroup)
98 >>> tetra.size
99 4
100 >>> tetra.edges
101 {(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)}
102 >>> tetra.corners
103 (w, x, y, z)
105 It can be rotated with an arbitrary permutation of vertices, e.g.
106 the following permutation is not in the pgroup:
108 >>> tetra.rotate(Permutation([0, 1, 3, 2]))
109 >>> tetra.corners
110 (w, x, z, y)
112 An allowed permutation of the vertices can be constructed by
113 repeatedly applying permutations from the pgroup to the vertices.
114 Here is a demonstration that applying p and p**2 for every p in
115 pgroup generates all the orientations of a tetrahedron and no others:
117 >>> all = ( (w, x, y, z), \
118 (x, y, w, z), \
119 (y, w, x, z), \
120 (w, z, x, y), \
121 (z, w, y, x), \
122 (w, y, z, x), \
123 (y, z, w, x), \
124 (x, z, y, w), \
125 (z, y, x, w), \
126 (y, x, z, w), \
127 (x, w, z, y), \
128 (z, x, w, y) )
130 >>> got = []
131 >>> for p in (pgroup + [p**2 for p in pgroup]):
132 ... h = Polyhedron(corners)
133 ... h.rotate(p)
134 ... got.append(h.corners)
135 ...
136 >>> set(got) == set(all)
137 True
139 The make_perm method of a PermutationGroup will randomly pick
140 permutations, multiply them together, and return the permutation that
141 can be applied to the polyhedron to give the orientation produced
142 by those individual permutations.
144 Here, 3 permutations are used:
146 >>> tetra.pgroup.make_perm(3) # doctest: +SKIP
147 Permutation([0, 3, 1, 2])
149 To select the permutations that should be used, supply a list
150 of indices to the permutations in pgroup in the order they should
151 be applied:
153 >>> use = [0, 0, 2]
154 >>> p002 = tetra.pgroup.make_perm(3, use)
155 >>> p002
156 Permutation([1, 0, 3, 2])
159 Apply them one at a time:
161 >>> tetra.reset()
162 >>> for i in use:
163 ... tetra.rotate(pgroup[i])
164 ...
165 >>> tetra.vertices
166 (x, w, z, y)
167 >>> sequentially = tetra.vertices
169 Apply the composite permutation:
171 >>> tetra.reset()
172 >>> tetra.rotate(p002)
173 >>> tetra.corners
174 (x, w, z, y)
175 >>> tetra.corners in all and tetra.corners == sequentially
176 True
178 Notes
179 =====
181 Defining permutation groups
182 ---------------------------
184 It is not necessary to enter any permutations, nor is necessary to
185 enter a complete set of transformations. In fact, for a polyhedron,
186 all configurations can be constructed from just two permutations.
187 For example, the orientations of a tetrahedron can be generated from
188 an axis passing through a vertex and face and another axis passing
189 through a different vertex or from an axis passing through the
190 midpoints of two edges opposite of each other.
192 For simplicity of presentation, consider a square --
193 not a cube -- with vertices 1, 2, 3, and 4:
195 1-----2 We could think of axes of rotation being:
196 | | 1) through the face
197 | | 2) from midpoint 1-2 to 3-4 or 1-3 to 2-4
198 3-----4 3) lines 1-4 or 2-3
201 To determine how to write the permutations, imagine 4 cameras,
202 one at each corner, labeled A-D:
204 A B A B
205 1-----2 1-----3 vertex index:
206 | | | | 1 0
207 | | | | 2 1
208 3-----4 2-----4 3 2
209 C D C D 4 3
211 original after rotation
212 along 1-4
214 A diagonal and a face axis will be chosen for the "permutation group"
215 from which any orientation can be constructed.
217 >>> pgroup = []
219 Imagine a clockwise rotation when viewing 1-4 from camera A. The new
220 orientation is (in camera-order): 1, 3, 2, 4 so the permutation is
221 given using the *indices* of the vertices as:
223 >>> pgroup.append(Permutation((0, 2, 1, 3)))
225 Now imagine rotating clockwise when looking down an axis entering the
226 center of the square as viewed. The new camera-order would be
227 3, 1, 4, 2 so the permutation is (using indices):
229 >>> pgroup.append(Permutation((2, 0, 3, 1)))
231 The square can now be constructed:
232 ** use real-world labels for the vertices, entering them in
233 camera order
234 ** for the faces we use zero-based indices of the vertices
235 in *edge-order* as the face is traversed; neither the
236 direction nor the starting point matter -- the faces are
237 only used to define edges (if so desired).
239 >>> square = Polyhedron((1, 2, 3, 4), [(0, 1, 3, 2)], pgroup)
241 To rotate the square with a single permutation we can do:
243 >>> square.rotate(square.pgroup[0])
244 >>> square.corners
245 (1, 3, 2, 4)
247 To use more than one permutation (or to use one permutation more
248 than once) it is more convenient to use the make_perm method:
250 >>> p011 = square.pgroup.make_perm([0, 1, 1]) # diag flip + 2 rotations
251 >>> square.reset() # return to initial orientation
252 >>> square.rotate(p011)
253 >>> square.corners
254 (4, 2, 3, 1)
256 Thinking outside the box
257 ------------------------
259 Although the Polyhedron object has a direct physical meaning, it
260 actually has broader application. In the most general sense it is
261 just a decorated PermutationGroup, allowing one to connect the
262 permutations to something physical. For example, a Rubik's cube is
263 not a proper polyhedron, but the Polyhedron class can be used to
264 represent it in a way that helps to visualize the Rubik's cube.
266 >>> from sympy import flatten, unflatten, symbols
267 >>> from sympy.combinatorics import RubikGroup
268 >>> facelets = flatten([symbols(s+'1:5') for s in 'UFRBLD'])
269 >>> def show():
270 ... pairs = unflatten(r2.corners, 2)
271 ... print(pairs[::2])
272 ... print(pairs[1::2])
273 ...
274 >>> r2 = Polyhedron(facelets, pgroup=RubikGroup(2))
275 >>> show()
276 [(U1, U2), (F1, F2), (R1, R2), (B1, B2), (L1, L2), (D1, D2)]
277 [(U3, U4), (F3, F4), (R3, R4), (B3, B4), (L3, L4), (D3, D4)]
278 >>> r2.rotate(0) # cw rotation of F
279 >>> show()
280 [(U1, U2), (F3, F1), (U3, R2), (B1, B2), (L1, D1), (R3, R1)]
281 [(L4, L2), (F4, F2), (U4, R4), (B3, B4), (L3, D2), (D3, D4)]
283 Predefined Polyhedra
284 ====================
286 For convenience, the vertices and faces are defined for the following
287 standard solids along with a permutation group for transformations.
288 When the polyhedron is oriented as indicated below, the vertices in
289 a given horizontal plane are numbered in ccw direction, starting from
290 the vertex that will give the lowest indices in a given face. (In the
291 net of the vertices, indices preceded by "-" indicate replication of
292 the lhs index in the net.)
294 tetrahedron, tetrahedron_faces
295 ------------------------------
297 4 vertices (vertex up) net:
299 0 0-0
300 1 2 3-1
302 4 faces:
304 (0, 1, 2) (0, 2, 3) (0, 3, 1) (1, 2, 3)
306 cube, cube_faces
307 ----------------
309 8 vertices (face up) net:
311 0 1 2 3-0
312 4 5 6 7-4
314 6 faces:
316 (0, 1, 2, 3)
317 (0, 1, 5, 4) (1, 2, 6, 5) (2, 3, 7, 6) (0, 3, 7, 4)
318 (4, 5, 6, 7)
320 octahedron, octahedron_faces
321 ----------------------------
323 6 vertices (vertex up) net:
325 0 0 0-0
326 1 2 3 4-1
327 5 5 5-5
329 8 faces:
331 (0, 1, 2) (0, 2, 3) (0, 3, 4) (0, 1, 4)
332 (1, 2, 5) (2, 3, 5) (3, 4, 5) (1, 4, 5)
334 dodecahedron, dodecahedron_faces
335 --------------------------------
337 20 vertices (vertex up) net:
339 0 1 2 3 4 -0
340 5 6 7 8 9 -5
341 14 10 11 12 13-14
342 15 16 17 18 19-15
344 12 faces:
346 (0, 1, 2, 3, 4) (0, 1, 6, 10, 5) (1, 2, 7, 11, 6)
347 (2, 3, 8, 12, 7) (3, 4, 9, 13, 8) (0, 4, 9, 14, 5)
348 (5, 10, 16, 15, 14) (6, 10, 16, 17, 11) (7, 11, 17, 18, 12)
349 (8, 12, 18, 19, 13) (9, 13, 19, 15, 14)(15, 16, 17, 18, 19)
351 icosahedron, icosahedron_faces
352 ------------------------------
354 12 vertices (face up) net:
356 0 0 0 0 -0
357 1 2 3 4 5 -1
358 6 7 8 9 10 -6
359 11 11 11 11 -11
361 20 faces:
363 (0, 1, 2) (0, 2, 3) (0, 3, 4)
364 (0, 4, 5) (0, 1, 5) (1, 2, 6)
365 (2, 3, 7) (3, 4, 8) (4, 5, 9)
366 (1, 5, 10) (2, 6, 7) (3, 7, 8)
367 (4, 8, 9) (5, 9, 10) (1, 6, 10)
368 (6, 7, 11) (7, 8, 11) (8, 9, 11)
369 (9, 10, 11) (6, 10, 11)
371 >>> from sympy.combinatorics.polyhedron import cube
372 >>> cube.edges
373 {(0, 1), (0, 3), (0, 4), (1, 2), (1, 5), (2, 3), (2, 6), (3, 7), (4, 5), (4, 7), (5, 6), (6, 7)}
375 If you want to use letters or other names for the corners you
376 can still use the pre-calculated faces:
378 >>> corners = list('abcdefgh')
379 >>> Polyhedron(corners, cube.faces).corners
380 (a, b, c, d, e, f, g, h)
382 References
383 ==========
385 .. [1] www.ocf.berkeley.edu/~wwu/articles/platonicsolids.pdf
387 """
388 faces = [minlex(f, directed=False, key=default_sort_key) for f in faces]
389 corners, faces, pgroup = args = \
390 [Tuple(*a) for a in (corners, faces, pgroup)]
391 obj = Basic.__new__(cls, *args)
392 obj._corners = tuple(corners) # in order given
393 obj._faces = FiniteSet(*faces)
394 if pgroup and pgroup[0].size != len(corners):
395 raise ValueError("Permutation size unequal to number of corners.")
396 # use the identity permutation if none are given
397 obj._pgroup = PermutationGroup(
398 pgroup or [Perm(range(len(corners)))] )
399 return obj
401 @property
402 def corners(self):
403 """
404 Get the corners of the Polyhedron.
406 The method ``vertices`` is an alias for ``corners``.
408 Examples
409 ========
411 >>> from sympy.combinatorics import Polyhedron
412 >>> from sympy.abc import a, b, c, d
413 >>> p = Polyhedron(list('abcd'))
414 >>> p.corners == p.vertices == (a, b, c, d)
415 True
417 See Also
418 ========
420 array_form, cyclic_form
421 """
422 return self._corners
423 vertices = corners
425 @property
426 def array_form(self):
427 """Return the indices of the corners.
429 The indices are given relative to the original position of corners.
431 Examples
432 ========
434 >>> from sympy.combinatorics.polyhedron import tetrahedron
435 >>> tetrahedron = tetrahedron.copy()
436 >>> tetrahedron.array_form
437 [0, 1, 2, 3]
439 >>> tetrahedron.rotate(0)
440 >>> tetrahedron.array_form
441 [0, 2, 3, 1]
442 >>> tetrahedron.pgroup[0].array_form
443 [0, 2, 3, 1]
445 See Also
446 ========
448 corners, cyclic_form
449 """
450 corners = list(self.args[0])
451 return [corners.index(c) for c in self.corners]
453 @property
454 def cyclic_form(self):
455 """Return the indices of the corners in cyclic notation.
457 The indices are given relative to the original position of corners.
459 See Also
460 ========
462 corners, array_form
463 """
464 return Perm._af_new(self.array_form).cyclic_form
466 @property
467 def size(self):
468 """
469 Get the number of corners of the Polyhedron.
470 """
471 return len(self._corners)
473 @property
474 def faces(self):
475 """
476 Get the faces of the Polyhedron.
477 """
478 return self._faces
480 @property
481 def pgroup(self):
482 """
483 Get the permutations of the Polyhedron.
484 """
485 return self._pgroup
487 @property
488 def edges(self):
489 """
490 Given the faces of the polyhedra we can get the edges.
492 Examples
493 ========
495 >>> from sympy.combinatorics import Polyhedron
496 >>> from sympy.abc import a, b, c
497 >>> corners = (a, b, c)
498 >>> faces = [(0, 1, 2)]
499 >>> Polyhedron(corners, faces).edges
500 {(0, 1), (0, 2), (1, 2)}
502 """
503 if self._edges is None:
504 output = set()
505 for face in self.faces:
506 for i in range(len(face)):
507 edge = tuple(sorted([face[i], face[i - 1]]))
508 output.add(edge)
509 self._edges = FiniteSet(*output)
510 return self._edges
512 def rotate(self, perm):
513 """
514 Apply a permutation to the polyhedron *in place*. The permutation
515 may be given as a Permutation instance or an integer indicating
516 which permutation from pgroup of the Polyhedron should be
517 applied.
519 This is an operation that is analogous to rotation about
520 an axis by a fixed increment.
522 Notes
523 =====
525 When a Permutation is applied, no check is done to see if that
526 is a valid permutation for the Polyhedron. For example, a cube
527 could be given a permutation which effectively swaps only 2
528 vertices. A valid permutation (that rotates the object in a
529 physical way) will be obtained if one only uses
530 permutations from the ``pgroup`` of the Polyhedron. On the other
531 hand, allowing arbitrary rotations (applications of permutations)
532 gives a way to follow named elements rather than indices since
533 Polyhedron allows vertices to be named while Permutation works
534 only with indices.
536 Examples
537 ========
539 >>> from sympy.combinatorics import Polyhedron, Permutation
540 >>> from sympy.combinatorics.polyhedron import cube
541 >>> cube = cube.copy()
542 >>> cube.corners
543 (0, 1, 2, 3, 4, 5, 6, 7)
544 >>> cube.rotate(0)
545 >>> cube.corners
546 (1, 2, 3, 0, 5, 6, 7, 4)
548 A non-physical "rotation" that is not prohibited by this method:
550 >>> cube.reset()
551 >>> cube.rotate(Permutation([[1, 2]], size=8))
552 >>> cube.corners
553 (0, 2, 1, 3, 4, 5, 6, 7)
555 Polyhedron can be used to follow elements of set that are
556 identified by letters instead of integers:
558 >>> shadow = h5 = Polyhedron(list('abcde'))
559 >>> p = Permutation([3, 0, 1, 2, 4])
560 >>> h5.rotate(p)
561 >>> h5.corners
562 (d, a, b, c, e)
563 >>> _ == shadow.corners
564 True
565 >>> copy = h5.copy()
566 >>> h5.rotate(p)
567 >>> h5.corners == copy.corners
568 False
569 """
570 if not isinstance(perm, Perm):
571 perm = self.pgroup[perm]
572 # and we know it's valid
573 else:
574 if perm.size != self.size:
575 raise ValueError('Polyhedron and Permutation sizes differ.')
576 a = perm.array_form
577 corners = [self.corners[a[i]] for i in range(len(self.corners))]
578 self._corners = tuple(corners)
580 def reset(self):
581 """Return corners to their original positions.
583 Examples
584 ========
586 >>> from sympy.combinatorics.polyhedron import tetrahedron as T
587 >>> T = T.copy()
588 >>> T.corners
589 (0, 1, 2, 3)
590 >>> T.rotate(0)
591 >>> T.corners
592 (0, 2, 3, 1)
593 >>> T.reset()
594 >>> T.corners
595 (0, 1, 2, 3)
596 """
597 self._corners = self.args[0]
600def _pgroup_calcs():
601 """Return the permutation groups for each of the polyhedra and the face
602 definitions: tetrahedron, cube, octahedron, dodecahedron, icosahedron,
603 tetrahedron_faces, cube_faces, octahedron_faces, dodecahedron_faces,
604 icosahedron_faces
606 Explanation
607 ===========
609 (This author did not find and did not know of a better way to do it though
610 there likely is such a way.)
612 Although only 2 permutations are needed for a polyhedron in order to
613 generate all the possible orientations, a group of permutations is
614 provided instead. A set of permutations is called a "group" if::
616 a*b = c (for any pair of permutations in the group, a and b, their
617 product, c, is in the group)
619 a*(b*c) = (a*b)*c (for any 3 permutations in the group associativity holds)
621 there is an identity permutation, I, such that I*a = a*I for all elements
622 in the group
624 a*b = I (the inverse of each permutation is also in the group)
626 None of the polyhedron groups defined follow these definitions of a group.
627 Instead, they are selected to contain those permutations whose powers
628 alone will construct all orientations of the polyhedron, i.e. for
629 permutations ``a``, ``b``, etc... in the group, ``a, a**2, ..., a**o_a``,
630 ``b, b**2, ..., b**o_b``, etc... (where ``o_i`` is the order of
631 permutation ``i``) generate all permutations of the polyhedron instead of
632 mixed products like ``a*b``, ``a*b**2``, etc....
634 Note that for a polyhedron with n vertices, the valid permutations of the
635 vertices exclude those that do not maintain its faces. e.g. the
636 permutation BCDE of a square's four corners, ABCD, is a valid
637 permutation while CBDE is not (because this would twist the square).
639 Examples
640 ========
642 The is_group checks for: closure, the presence of the Identity permutation,
643 and the presence of the inverse for each of the elements in the group. This
644 confirms that none of the polyhedra are true groups:
646 >>> from sympy.combinatorics.polyhedron import (
647 ... tetrahedron, cube, octahedron, dodecahedron, icosahedron)
648 ...
649 >>> polyhedra = (tetrahedron, cube, octahedron, dodecahedron, icosahedron)
650 >>> [h.pgroup.is_group for h in polyhedra]
651 ...
652 [True, True, True, True, True]
654 Although tests in polyhedron's test suite check that powers of the
655 permutations in the groups generate all permutations of the vertices
656 of the polyhedron, here we also demonstrate the powers of the given
657 permutations create a complete group for the tetrahedron:
659 >>> from sympy.combinatorics import Permutation, PermutationGroup
660 >>> for h in polyhedra[:1]:
661 ... G = h.pgroup
662 ... perms = set()
663 ... for g in G:
664 ... for e in range(g.order()):
665 ... p = tuple((g**e).array_form)
666 ... perms.add(p)
667 ...
668 ... perms = [Permutation(p) for p in perms]
669 ... assert PermutationGroup(perms).is_group
671 In addition to doing the above, the tests in the suite confirm that the
672 faces are all present after the application of each permutation.
674 References
675 ==========
677 .. [1] https://dogschool.tripod.com/trianglegroup.html
679 """
680 def _pgroup_of_double(polyh, ordered_faces, pgroup):
681 n = len(ordered_faces[0])
682 # the vertices of the double which sits inside a give polyhedron
683 # can be found by tracking the faces of the outer polyhedron.
684 # A map between face and the vertex of the double is made so that
685 # after rotation the position of the vertices can be located
686 fmap = dict(zip(ordered_faces,
687 range(len(ordered_faces))))
688 flat_faces = flatten(ordered_faces)
689 new_pgroup = []
690 for i, p in enumerate(pgroup):
691 h = polyh.copy()
692 h.rotate(p)
693 c = h.corners
694 # reorder corners in the order they should appear when
695 # enumerating the faces
696 reorder = unflatten([c[j] for j in flat_faces], n)
697 # make them canonical
698 reorder = [tuple(map(as_int,
699 minlex(f, directed=False)))
700 for f in reorder]
701 # map face to vertex: the resulting list of vertices are the
702 # permutation that we seek for the double
703 new_pgroup.append(Perm([fmap[f] for f in reorder]))
704 return new_pgroup
706 tetrahedron_faces = [
707 (0, 1, 2), (0, 2, 3), (0, 3, 1), # upper 3
708 (1, 2, 3), # bottom
709 ]
711 # cw from top
712 #
713 _t_pgroup = [
714 Perm([[1, 2, 3], [0]]), # cw from top
715 Perm([[0, 1, 2], [3]]), # cw from front face
716 Perm([[0, 3, 2], [1]]), # cw from back right face
717 Perm([[0, 3, 1], [2]]), # cw from back left face
718 Perm([[0, 1], [2, 3]]), # through front left edge
719 Perm([[0, 2], [1, 3]]), # through front right edge
720 Perm([[0, 3], [1, 2]]), # through back edge
721 ]
723 tetrahedron = Polyhedron(
724 range(4),
725 tetrahedron_faces,
726 _t_pgroup)
728 cube_faces = [
729 (0, 1, 2, 3), # upper
730 (0, 1, 5, 4), (1, 2, 6, 5), (2, 3, 7, 6), (0, 3, 7, 4), # middle 4
731 (4, 5, 6, 7), # lower
732 ]
734 # U, D, F, B, L, R = up, down, front, back, left, right
735 _c_pgroup = [Perm(p) for p in
736 [
737 [1, 2, 3, 0, 5, 6, 7, 4], # cw from top, U
738 [4, 0, 3, 7, 5, 1, 2, 6], # cw from F face
739 [4, 5, 1, 0, 7, 6, 2, 3], # cw from R face
741 [1, 0, 4, 5, 2, 3, 7, 6], # cw through UF edge
742 [6, 2, 1, 5, 7, 3, 0, 4], # cw through UR edge
743 [6, 7, 3, 2, 5, 4, 0, 1], # cw through UB edge
744 [3, 7, 4, 0, 2, 6, 5, 1], # cw through UL edge
745 [4, 7, 6, 5, 0, 3, 2, 1], # cw through FL edge
746 [6, 5, 4, 7, 2, 1, 0, 3], # cw through FR edge
748 [0, 3, 7, 4, 1, 2, 6, 5], # cw through UFL vertex
749 [5, 1, 0, 4, 6, 2, 3, 7], # cw through UFR vertex
750 [5, 6, 2, 1, 4, 7, 3, 0], # cw through UBR vertex
751 [7, 4, 0, 3, 6, 5, 1, 2], # cw through UBL
752 ]]
754 cube = Polyhedron(
755 range(8),
756 cube_faces,
757 _c_pgroup)
759 octahedron_faces = [
760 (0, 1, 2), (0, 2, 3), (0, 3, 4), (0, 1, 4), # top 4
761 (1, 2, 5), (2, 3, 5), (3, 4, 5), (1, 4, 5), # bottom 4
762 ]
764 octahedron = Polyhedron(
765 range(6),
766 octahedron_faces,
767 _pgroup_of_double(cube, cube_faces, _c_pgroup))
769 dodecahedron_faces = [
770 (0, 1, 2, 3, 4), # top
771 (0, 1, 6, 10, 5), (1, 2, 7, 11, 6), (2, 3, 8, 12, 7), # upper 5
772 (3, 4, 9, 13, 8), (0, 4, 9, 14, 5),
773 (5, 10, 16, 15, 14), (6, 10, 16, 17, 11), (7, 11, 17, 18,
774 12), # lower 5
775 (8, 12, 18, 19, 13), (9, 13, 19, 15, 14),
776 (15, 16, 17, 18, 19) # bottom
777 ]
779 def _string_to_perm(s):
780 rv = [Perm(range(20))]
781 p = None
782 for si in s:
783 if si not in '01':
784 count = int(si) - 1
785 else:
786 count = 1
787 if si == '0':
788 p = _f0
789 elif si == '1':
790 p = _f1
791 rv.extend([p]*count)
792 return Perm.rmul(*rv)
794 # top face cw
795 _f0 = Perm([
796 1, 2, 3, 4, 0, 6, 7, 8, 9, 5, 11,
797 12, 13, 14, 10, 16, 17, 18, 19, 15])
798 # front face cw
799 _f1 = Perm([
800 5, 0, 4, 9, 14, 10, 1, 3, 13, 15,
801 6, 2, 8, 19, 16, 17, 11, 7, 12, 18])
802 # the strings below, like 0104 are shorthand for F0*F1*F0**4 and are
803 # the remaining 4 face rotations, 15 edge permutations, and the
804 # 10 vertex rotations.
805 _dodeca_pgroup = [_f0, _f1] + [_string_to_perm(s) for s in '''
806 0104 140 014 0410
807 010 1403 03104 04103 102
808 120 1304 01303 021302 03130
809 0412041 041204103 04120410 041204104 041204102
810 10 01 1402 0140 04102 0412 1204 1302 0130 03120'''.strip().split()]
812 dodecahedron = Polyhedron(
813 range(20),
814 dodecahedron_faces,
815 _dodeca_pgroup)
817 icosahedron_faces = [
818 (0, 1, 2), (0, 2, 3), (0, 3, 4), (0, 4, 5), (0, 1, 5),
819 (1, 6, 7), (1, 2, 7), (2, 7, 8), (2, 3, 8), (3, 8, 9),
820 (3, 4, 9), (4, 9, 10), (4, 5, 10), (5, 6, 10), (1, 5, 6),
821 (6, 7, 11), (7, 8, 11), (8, 9, 11), (9, 10, 11), (6, 10, 11)]
823 icosahedron = Polyhedron(
824 range(12),
825 icosahedron_faces,
826 _pgroup_of_double(
827 dodecahedron, dodecahedron_faces, _dodeca_pgroup))
829 return (tetrahedron, cube, octahedron, dodecahedron, icosahedron,
830 tetrahedron_faces, cube_faces, octahedron_faces,
831 dodecahedron_faces, icosahedron_faces)
833# -----------------------------------------------------------------------
834# Standard Polyhedron groups
835#
836# These are generated using _pgroup_calcs() above. However to save
837# import time we encode them explicitly here.
838# -----------------------------------------------------------------------
840tetrahedron = Polyhedron(
841 Tuple(0, 1, 2, 3),
842 Tuple(
843 Tuple(0, 1, 2),
844 Tuple(0, 2, 3),
845 Tuple(0, 1, 3),
846 Tuple(1, 2, 3)),
847 Tuple(
848 Perm(1, 2, 3),
849 Perm(3)(0, 1, 2),
850 Perm(0, 3, 2),
851 Perm(0, 3, 1),
852 Perm(0, 1)(2, 3),
853 Perm(0, 2)(1, 3),
854 Perm(0, 3)(1, 2)
855 ))
857cube = Polyhedron(
858 Tuple(0, 1, 2, 3, 4, 5, 6, 7),
859 Tuple(
860 Tuple(0, 1, 2, 3),
861 Tuple(0, 1, 5, 4),
862 Tuple(1, 2, 6, 5),
863 Tuple(2, 3, 7, 6),
864 Tuple(0, 3, 7, 4),
865 Tuple(4, 5, 6, 7)),
866 Tuple(
867 Perm(0, 1, 2, 3)(4, 5, 6, 7),
868 Perm(0, 4, 5, 1)(2, 3, 7, 6),
869 Perm(0, 4, 7, 3)(1, 5, 6, 2),
870 Perm(0, 1)(2, 4)(3, 5)(6, 7),
871 Perm(0, 6)(1, 2)(3, 5)(4, 7),
872 Perm(0, 6)(1, 7)(2, 3)(4, 5),
873 Perm(0, 3)(1, 7)(2, 4)(5, 6),
874 Perm(0, 4)(1, 7)(2, 6)(3, 5),
875 Perm(0, 6)(1, 5)(2, 4)(3, 7),
876 Perm(1, 3, 4)(2, 7, 5),
877 Perm(7)(0, 5, 2)(3, 4, 6),
878 Perm(0, 5, 7)(1, 6, 3),
879 Perm(0, 7, 2)(1, 4, 6)))
881octahedron = Polyhedron(
882 Tuple(0, 1, 2, 3, 4, 5),
883 Tuple(
884 Tuple(0, 1, 2),
885 Tuple(0, 2, 3),
886 Tuple(0, 3, 4),
887 Tuple(0, 1, 4),
888 Tuple(1, 2, 5),
889 Tuple(2, 3, 5),
890 Tuple(3, 4, 5),
891 Tuple(1, 4, 5)),
892 Tuple(
893 Perm(5)(1, 2, 3, 4),
894 Perm(0, 4, 5, 2),
895 Perm(0, 1, 5, 3),
896 Perm(0, 1)(2, 4)(3, 5),
897 Perm(0, 2)(1, 3)(4, 5),
898 Perm(0, 3)(1, 5)(2, 4),
899 Perm(0, 4)(1, 3)(2, 5),
900 Perm(0, 5)(1, 4)(2, 3),
901 Perm(0, 5)(1, 2)(3, 4),
902 Perm(0, 4, 1)(2, 3, 5),
903 Perm(0, 1, 2)(3, 4, 5),
904 Perm(0, 2, 3)(1, 5, 4),
905 Perm(0, 4, 3)(1, 5, 2)))
907dodecahedron = Polyhedron(
908 Tuple(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19),
909 Tuple(
910 Tuple(0, 1, 2, 3, 4),
911 Tuple(0, 1, 6, 10, 5),
912 Tuple(1, 2, 7, 11, 6),
913 Tuple(2, 3, 8, 12, 7),
914 Tuple(3, 4, 9, 13, 8),
915 Tuple(0, 4, 9, 14, 5),
916 Tuple(5, 10, 16, 15, 14),
917 Tuple(6, 10, 16, 17, 11),
918 Tuple(7, 11, 17, 18, 12),
919 Tuple(8, 12, 18, 19, 13),
920 Tuple(9, 13, 19, 15, 14),
921 Tuple(15, 16, 17, 18, 19)),
922 Tuple(
923 Perm(0, 1, 2, 3, 4)(5, 6, 7, 8, 9)(10, 11, 12, 13, 14)(15, 16, 17, 18, 19),
924 Perm(0, 5, 10, 6, 1)(2, 4, 14, 16, 11)(3, 9, 15, 17, 7)(8, 13, 19, 18, 12),
925 Perm(0, 10, 17, 12, 3)(1, 6, 11, 7, 2)(4, 5, 16, 18, 8)(9, 14, 15, 19, 13),
926 Perm(0, 6, 17, 19, 9)(1, 11, 18, 13, 4)(2, 7, 12, 8, 3)(5, 10, 16, 15, 14),
927 Perm(0, 2, 12, 19, 14)(1, 7, 18, 15, 5)(3, 8, 13, 9, 4)(6, 11, 17, 16, 10),
928 Perm(0, 4, 9, 14, 5)(1, 3, 13, 15, 10)(2, 8, 19, 16, 6)(7, 12, 18, 17, 11),
929 Perm(0, 1)(2, 5)(3, 10)(4, 6)(7, 14)(8, 16)(9, 11)(12, 15)(13, 17)(18, 19),
930 Perm(0, 7)(1, 2)(3, 6)(4, 11)(5, 12)(8, 10)(9, 17)(13, 16)(14, 18)(15, 19),
931 Perm(0, 12)(1, 8)(2, 3)(4, 7)(5, 18)(6, 13)(9, 11)(10, 19)(14, 17)(15, 16),
932 Perm(0, 8)(1, 13)(2, 9)(3, 4)(5, 12)(6, 19)(7, 14)(10, 18)(11, 15)(16, 17),
933 Perm(0, 4)(1, 9)(2, 14)(3, 5)(6, 13)(7, 15)(8, 10)(11, 19)(12, 16)(17, 18),
934 Perm(0, 5)(1, 14)(2, 15)(3, 16)(4, 10)(6, 9)(7, 19)(8, 17)(11, 13)(12, 18),
935 Perm(0, 11)(1, 6)(2, 10)(3, 16)(4, 17)(5, 7)(8, 15)(9, 18)(12, 14)(13, 19),
936 Perm(0, 18)(1, 12)(2, 7)(3, 11)(4, 17)(5, 19)(6, 8)(9, 16)(10, 13)(14, 15),
937 Perm(0, 18)(1, 19)(2, 13)(3, 8)(4, 12)(5, 17)(6, 15)(7, 9)(10, 16)(11, 14),
938 Perm(0, 13)(1, 19)(2, 15)(3, 14)(4, 9)(5, 8)(6, 18)(7, 16)(10, 12)(11, 17),
939 Perm(0, 16)(1, 15)(2, 19)(3, 18)(4, 17)(5, 10)(6, 14)(7, 13)(8, 12)(9, 11),
940 Perm(0, 18)(1, 17)(2, 16)(3, 15)(4, 19)(5, 12)(6, 11)(7, 10)(8, 14)(9, 13),
941 Perm(0, 15)(1, 19)(2, 18)(3, 17)(4, 16)(5, 14)(6, 13)(7, 12)(8, 11)(9, 10),
942 Perm(0, 17)(1, 16)(2, 15)(3, 19)(4, 18)(5, 11)(6, 10)(7, 14)(8, 13)(9, 12),
943 Perm(0, 19)(1, 18)(2, 17)(3, 16)(4, 15)(5, 13)(6, 12)(7, 11)(8, 10)(9, 14),
944 Perm(1, 4, 5)(2, 9, 10)(3, 14, 6)(7, 13, 16)(8, 15, 11)(12, 19, 17),
945 Perm(19)(0, 6, 2)(3, 5, 11)(4, 10, 7)(8, 14, 17)(9, 16, 12)(13, 15, 18),
946 Perm(0, 11, 8)(1, 7, 3)(4, 6, 12)(5, 17, 13)(9, 10, 18)(14, 16, 19),
947 Perm(0, 7, 13)(1, 12, 9)(2, 8, 4)(5, 11, 19)(6, 18, 14)(10, 17, 15),
948 Perm(0, 3, 9)(1, 8, 14)(2, 13, 5)(6, 12, 15)(7, 19, 10)(11, 18, 16),
949 Perm(0, 14, 10)(1, 9, 16)(2, 13, 17)(3, 19, 11)(4, 15, 6)(7, 8, 18),
950 Perm(0, 16, 7)(1, 10, 11)(2, 5, 17)(3, 14, 18)(4, 15, 12)(8, 9, 19),
951 Perm(0, 16, 13)(1, 17, 8)(2, 11, 12)(3, 6, 18)(4, 10, 19)(5, 15, 9),
952 Perm(0, 11, 15)(1, 17, 14)(2, 18, 9)(3, 12, 13)(4, 7, 19)(5, 6, 16),
953 Perm(0, 8, 15)(1, 12, 16)(2, 18, 10)(3, 19, 5)(4, 13, 14)(6, 7, 17)))
955icosahedron = Polyhedron(
956 Tuple(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
957 Tuple(
958 Tuple(0, 1, 2),
959 Tuple(0, 2, 3),
960 Tuple(0, 3, 4),
961 Tuple(0, 4, 5),
962 Tuple(0, 1, 5),
963 Tuple(1, 6, 7),
964 Tuple(1, 2, 7),
965 Tuple(2, 7, 8),
966 Tuple(2, 3, 8),
967 Tuple(3, 8, 9),
968 Tuple(3, 4, 9),
969 Tuple(4, 9, 10),
970 Tuple(4, 5, 10),
971 Tuple(5, 6, 10),
972 Tuple(1, 5, 6),
973 Tuple(6, 7, 11),
974 Tuple(7, 8, 11),
975 Tuple(8, 9, 11),
976 Tuple(9, 10, 11),
977 Tuple(6, 10, 11)),
978 Tuple(
979 Perm(11)(1, 2, 3, 4, 5)(6, 7, 8, 9, 10),
980 Perm(0, 5, 6, 7, 2)(3, 4, 10, 11, 8),
981 Perm(0, 1, 7, 8, 3)(4, 5, 6, 11, 9),
982 Perm(0, 2, 8, 9, 4)(1, 7, 11, 10, 5),
983 Perm(0, 3, 9, 10, 5)(1, 2, 8, 11, 6),
984 Perm(0, 4, 10, 6, 1)(2, 3, 9, 11, 7),
985 Perm(0, 1)(2, 5)(3, 6)(4, 7)(8, 10)(9, 11),
986 Perm(0, 2)(1, 3)(4, 7)(5, 8)(6, 9)(10, 11),
987 Perm(0, 3)(1, 9)(2, 4)(5, 8)(6, 11)(7, 10),
988 Perm(0, 4)(1, 9)(2, 10)(3, 5)(6, 8)(7, 11),
989 Perm(0, 5)(1, 4)(2, 10)(3, 6)(7, 9)(8, 11),
990 Perm(0, 6)(1, 5)(2, 10)(3, 11)(4, 7)(8, 9),
991 Perm(0, 7)(1, 2)(3, 6)(4, 11)(5, 8)(9, 10),
992 Perm(0, 8)(1, 9)(2, 3)(4, 7)(5, 11)(6, 10),
993 Perm(0, 9)(1, 11)(2, 10)(3, 4)(5, 8)(6, 7),
994 Perm(0, 10)(1, 9)(2, 11)(3, 6)(4, 5)(7, 8),
995 Perm(0, 11)(1, 6)(2, 10)(3, 9)(4, 8)(5, 7),
996 Perm(0, 11)(1, 8)(2, 7)(3, 6)(4, 10)(5, 9),
997 Perm(0, 11)(1, 10)(2, 9)(3, 8)(4, 7)(5, 6),
998 Perm(0, 11)(1, 7)(2, 6)(3, 10)(4, 9)(5, 8),
999 Perm(0, 11)(1, 9)(2, 8)(3, 7)(4, 6)(5, 10),
1000 Perm(0, 5, 1)(2, 4, 6)(3, 10, 7)(8, 9, 11),
1001 Perm(0, 1, 2)(3, 5, 7)(4, 6, 8)(9, 10, 11),
1002 Perm(0, 2, 3)(1, 8, 4)(5, 7, 9)(6, 11, 10),
1003 Perm(0, 3, 4)(1, 8, 10)(2, 9, 5)(6, 7, 11),
1004 Perm(0, 4, 5)(1, 3, 10)(2, 9, 6)(7, 8, 11),
1005 Perm(0, 10, 7)(1, 5, 6)(2, 4, 11)(3, 9, 8),
1006 Perm(0, 6, 8)(1, 7, 2)(3, 5, 11)(4, 10, 9),
1007 Perm(0, 7, 9)(1, 11, 4)(2, 8, 3)(5, 6, 10),
1008 Perm(0, 8, 10)(1, 7, 6)(2, 11, 5)(3, 9, 4),
1009 Perm(0, 9, 6)(1, 3, 11)(2, 8, 7)(4, 10, 5)))
1011tetrahedron_faces = [tuple(arg) for arg in tetrahedron.faces]
1013cube_faces = [tuple(arg) for arg in cube.faces]
1015octahedron_faces = [tuple(arg) for arg in octahedron.faces]
1017dodecahedron_faces = [tuple(arg) for arg in dodecahedron.faces]
1019icosahedron_faces = [tuple(arg) for arg in icosahedron.faces]