Spaces:
Sleeping
Sleeping
| from sympy.combinatorics import Permutation as Perm | |
| from sympy.combinatorics.perm_groups import PermutationGroup | |
| from sympy.core import Basic, Tuple, default_sort_key | |
| from sympy.sets import FiniteSet | |
| from sympy.utilities.iterables import (minlex, unflatten, flatten) | |
| from sympy.utilities.misc import as_int | |
| rmul = Perm.rmul | |
| class Polyhedron(Basic): | |
| """ | |
| Represents the polyhedral symmetry group (PSG). | |
| Explanation | |
| =========== | |
| The PSG is one of the symmetry groups of the Platonic solids. | |
| There are three polyhedral groups: the tetrahedral group | |
| of order 12, the octahedral group of order 24, and the | |
| icosahedral group of order 60. | |
| All doctests have been given in the docstring of the | |
| constructor of the object. | |
| References | |
| ========== | |
| .. [1] https://mathworld.wolfram.com/PolyhedralGroup.html | |
| """ | |
| _edges = None | |
| def __new__(cls, corners, faces=(), pgroup=()): | |
| """ | |
| The constructor of the Polyhedron group object. | |
| Explanation | |
| =========== | |
| It takes up to three parameters: the corners, faces, and | |
| allowed transformations. | |
| The corners/vertices are entered as a list of arbitrary | |
| expressions that are used to identify each vertex. | |
| The faces are entered as a list of tuples of indices; a tuple | |
| of indices identifies the vertices which define the face. They | |
| should be entered in a cw or ccw order; they will be standardized | |
| by reversal and rotation to be give the lowest lexical ordering. | |
| If no faces are given then no edges will be computed. | |
| >>> from sympy.combinatorics.polyhedron import Polyhedron | |
| >>> Polyhedron(list('abc'), [(1, 2, 0)]).faces | |
| {(0, 1, 2)} | |
| >>> Polyhedron(list('abc'), [(1, 0, 2)]).faces | |
| {(0, 1, 2)} | |
| The allowed transformations are entered as allowable permutations | |
| of the vertices for the polyhedron. Instance of Permutations | |
| (as with faces) should refer to the supplied vertices by index. | |
| These permutation are stored as a PermutationGroup. | |
| Examples | |
| ======== | |
| >>> from sympy.combinatorics.permutations import Permutation | |
| >>> from sympy import init_printing | |
| >>> from sympy.abc import w, x, y, z | |
| >>> init_printing(pretty_print=False, perm_cyclic=False) | |
| Here we construct the Polyhedron object for a tetrahedron. | |
| >>> corners = [w, x, y, z] | |
| >>> faces = [(0, 1, 2), (0, 2, 3), (0, 3, 1), (1, 2, 3)] | |
| Next, allowed transformations of the polyhedron must be given. This | |
| is given as permutations of vertices. | |
| Although the vertices of a tetrahedron can be numbered in 24 (4!) | |
| different ways, there are only 12 different orientations for a | |
| physical tetrahedron. The following permutations, applied once or | |
| twice, will generate all 12 of the orientations. (The identity | |
| permutation, Permutation(range(4)), is not included since it does | |
| not change the orientation of the vertices.) | |
| >>> pgroup = [Permutation([[0, 1, 2], [3]]), \ | |
| Permutation([[0, 1, 3], [2]]), \ | |
| Permutation([[0, 2, 3], [1]]), \ | |
| Permutation([[1, 2, 3], [0]]), \ | |
| Permutation([[0, 1], [2, 3]]), \ | |
| Permutation([[0, 2], [1, 3]]), \ | |
| Permutation([[0, 3], [1, 2]])] | |
| The Polyhedron is now constructed and demonstrated: | |
| >>> tetra = Polyhedron(corners, faces, pgroup) | |
| >>> tetra.size | |
| 4 | |
| >>> tetra.edges | |
| {(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)} | |
| >>> tetra.corners | |
| (w, x, y, z) | |
| It can be rotated with an arbitrary permutation of vertices, e.g. | |
| the following permutation is not in the pgroup: | |
| >>> tetra.rotate(Permutation([0, 1, 3, 2])) | |
| >>> tetra.corners | |
| (w, x, z, y) | |
| An allowed permutation of the vertices can be constructed by | |
| repeatedly applying permutations from the pgroup to the vertices. | |
| Here is a demonstration that applying p and p**2 for every p in | |
| pgroup generates all the orientations of a tetrahedron and no others: | |
| >>> all = ( (w, x, y, z), \ | |
| (x, y, w, z), \ | |
| (y, w, x, z), \ | |
| (w, z, x, y), \ | |
| (z, w, y, x), \ | |
| (w, y, z, x), \ | |
| (y, z, w, x), \ | |
| (x, z, y, w), \ | |
| (z, y, x, w), \ | |
| (y, x, z, w), \ | |
| (x, w, z, y), \ | |
| (z, x, w, y) ) | |
| >>> got = [] | |
| >>> for p in (pgroup + [p**2 for p in pgroup]): | |
| ... h = Polyhedron(corners) | |
| ... h.rotate(p) | |
| ... got.append(h.corners) | |
| ... | |
| >>> set(got) == set(all) | |
| True | |
| The make_perm method of a PermutationGroup will randomly pick | |
| permutations, multiply them together, and return the permutation that | |
| can be applied to the polyhedron to give the orientation produced | |
| by those individual permutations. | |
| Here, 3 permutations are used: | |
| >>> tetra.pgroup.make_perm(3) # doctest: +SKIP | |
| Permutation([0, 3, 1, 2]) | |
| To select the permutations that should be used, supply a list | |
| of indices to the permutations in pgroup in the order they should | |
| be applied: | |
| >>> use = [0, 0, 2] | |
| >>> p002 = tetra.pgroup.make_perm(3, use) | |
| >>> p002 | |
| Permutation([1, 0, 3, 2]) | |
| Apply them one at a time: | |
| >>> tetra.reset() | |
| >>> for i in use: | |
| ... tetra.rotate(pgroup[i]) | |
| ... | |
| >>> tetra.vertices | |
| (x, w, z, y) | |
| >>> sequentially = tetra.vertices | |
| Apply the composite permutation: | |
| >>> tetra.reset() | |
| >>> tetra.rotate(p002) | |
| >>> tetra.corners | |
| (x, w, z, y) | |
| >>> tetra.corners in all and tetra.corners == sequentially | |
| True | |
| Notes | |
| ===== | |
| Defining permutation groups | |
| --------------------------- | |
| It is not necessary to enter any permutations, nor is necessary to | |
| enter a complete set of transformations. In fact, for a polyhedron, | |
| all configurations can be constructed from just two permutations. | |
| For example, the orientations of a tetrahedron can be generated from | |
| an axis passing through a vertex and face and another axis passing | |
| through a different vertex or from an axis passing through the | |
| midpoints of two edges opposite of each other. | |
| For simplicity of presentation, consider a square -- | |
| not a cube -- with vertices 1, 2, 3, and 4: | |
| 1-----2 We could think of axes of rotation being: | |
| | | 1) through the face | |
| | | 2) from midpoint 1-2 to 3-4 or 1-3 to 2-4 | |
| 3-----4 3) lines 1-4 or 2-3 | |
| To determine how to write the permutations, imagine 4 cameras, | |
| one at each corner, labeled A-D: | |
| A B A B | |
| 1-----2 1-----3 vertex index: | |
| | | | | 1 0 | |
| | | | | 2 1 | |
| 3-----4 2-----4 3 2 | |
| C D C D 4 3 | |
| original after rotation | |
| along 1-4 | |
| A diagonal and a face axis will be chosen for the "permutation group" | |
| from which any orientation can be constructed. | |
| >>> pgroup = [] | |
| Imagine a clockwise rotation when viewing 1-4 from camera A. The new | |
| orientation is (in camera-order): 1, 3, 2, 4 so the permutation is | |
| given using the *indices* of the vertices as: | |
| >>> pgroup.append(Permutation((0, 2, 1, 3))) | |
| Now imagine rotating clockwise when looking down an axis entering the | |
| center of the square as viewed. The new camera-order would be | |
| 3, 1, 4, 2 so the permutation is (using indices): | |
| >>> pgroup.append(Permutation((2, 0, 3, 1))) | |
| The square can now be constructed: | |
| ** use real-world labels for the vertices, entering them in | |
| camera order | |
| ** for the faces we use zero-based indices of the vertices | |
| in *edge-order* as the face is traversed; neither the | |
| direction nor the starting point matter -- the faces are | |
| only used to define edges (if so desired). | |
| >>> square = Polyhedron((1, 2, 3, 4), [(0, 1, 3, 2)], pgroup) | |
| To rotate the square with a single permutation we can do: | |
| >>> square.rotate(square.pgroup[0]) | |
| >>> square.corners | |
| (1, 3, 2, 4) | |
| To use more than one permutation (or to use one permutation more | |
| than once) it is more convenient to use the make_perm method: | |
| >>> p011 = square.pgroup.make_perm([0, 1, 1]) # diag flip + 2 rotations | |
| >>> square.reset() # return to initial orientation | |
| >>> square.rotate(p011) | |
| >>> square.corners | |
| (4, 2, 3, 1) | |
| Thinking outside the box | |
| ------------------------ | |
| Although the Polyhedron object has a direct physical meaning, it | |
| actually has broader application. In the most general sense it is | |
| just a decorated PermutationGroup, allowing one to connect the | |
| permutations to something physical. For example, a Rubik's cube is | |
| not a proper polyhedron, but the Polyhedron class can be used to | |
| represent it in a way that helps to visualize the Rubik's cube. | |
| >>> from sympy import flatten, unflatten, symbols | |
| >>> from sympy.combinatorics import RubikGroup | |
| >>> facelets = flatten([symbols(s+'1:5') for s in 'UFRBLD']) | |
| >>> def show(): | |
| ... pairs = unflatten(r2.corners, 2) | |
| ... print(pairs[::2]) | |
| ... print(pairs[1::2]) | |
| ... | |
| >>> r2 = Polyhedron(facelets, pgroup=RubikGroup(2)) | |
| >>> show() | |
| [(U1, U2), (F1, F2), (R1, R2), (B1, B2), (L1, L2), (D1, D2)] | |
| [(U3, U4), (F3, F4), (R3, R4), (B3, B4), (L3, L4), (D3, D4)] | |
| >>> r2.rotate(0) # cw rotation of F | |
| >>> show() | |
| [(U1, U2), (F3, F1), (U3, R2), (B1, B2), (L1, D1), (R3, R1)] | |
| [(L4, L2), (F4, F2), (U4, R4), (B3, B4), (L3, D2), (D3, D4)] | |
| Predefined Polyhedra | |
| ==================== | |
| For convenience, the vertices and faces are defined for the following | |
| standard solids along with a permutation group for transformations. | |
| When the polyhedron is oriented as indicated below, the vertices in | |
| a given horizontal plane are numbered in ccw direction, starting from | |
| the vertex that will give the lowest indices in a given face. (In the | |
| net of the vertices, indices preceded by "-" indicate replication of | |
| the lhs index in the net.) | |
| tetrahedron, tetrahedron_faces | |
| ------------------------------ | |
| 4 vertices (vertex up) net: | |
| 0 0-0 | |
| 1 2 3-1 | |
| 4 faces: | |
| (0, 1, 2) (0, 2, 3) (0, 3, 1) (1, 2, 3) | |
| cube, cube_faces | |
| ---------------- | |
| 8 vertices (face up) net: | |
| 0 1 2 3-0 | |
| 4 5 6 7-4 | |
| 6 faces: | |
| (0, 1, 2, 3) | |
| (0, 1, 5, 4) (1, 2, 6, 5) (2, 3, 7, 6) (0, 3, 7, 4) | |
| (4, 5, 6, 7) | |
| octahedron, octahedron_faces | |
| ---------------------------- | |
| 6 vertices (vertex up) net: | |
| 0 0 0-0 | |
| 1 2 3 4-1 | |
| 5 5 5-5 | |
| 8 faces: | |
| (0, 1, 2) (0, 2, 3) (0, 3, 4) (0, 1, 4) | |
| (1, 2, 5) (2, 3, 5) (3, 4, 5) (1, 4, 5) | |
| dodecahedron, dodecahedron_faces | |
| -------------------------------- | |
| 20 vertices (vertex up) net: | |
| 0 1 2 3 4 -0 | |
| 5 6 7 8 9 -5 | |
| 14 10 11 12 13-14 | |
| 15 16 17 18 19-15 | |
| 12 faces: | |
| (0, 1, 2, 3, 4) (0, 1, 6, 10, 5) (1, 2, 7, 11, 6) | |
| (2, 3, 8, 12, 7) (3, 4, 9, 13, 8) (0, 4, 9, 14, 5) | |
| (5, 10, 16, 15, 14) (6, 10, 16, 17, 11) (7, 11, 17, 18, 12) | |
| (8, 12, 18, 19, 13) (9, 13, 19, 15, 14)(15, 16, 17, 18, 19) | |
| icosahedron, icosahedron_faces | |
| ------------------------------ | |
| 12 vertices (face up) net: | |
| 0 0 0 0 -0 | |
| 1 2 3 4 5 -1 | |
| 6 7 8 9 10 -6 | |
| 11 11 11 11 -11 | |
| 20 faces: | |
| (0, 1, 2) (0, 2, 3) (0, 3, 4) | |
| (0, 4, 5) (0, 1, 5) (1, 2, 6) | |
| (2, 3, 7) (3, 4, 8) (4, 5, 9) | |
| (1, 5, 10) (2, 6, 7) (3, 7, 8) | |
| (4, 8, 9) (5, 9, 10) (1, 6, 10) | |
| (6, 7, 11) (7, 8, 11) (8, 9, 11) | |
| (9, 10, 11) (6, 10, 11) | |
| >>> from sympy.combinatorics.polyhedron import cube | |
| >>> cube.edges | |
| {(0, 1), (0, 3), (0, 4), (1, 2), (1, 5), (2, 3), (2, 6), (3, 7), (4, 5), (4, 7), (5, 6), (6, 7)} | |
| If you want to use letters or other names for the corners you | |
| can still use the pre-calculated faces: | |
| >>> corners = list('abcdefgh') | |
| >>> Polyhedron(corners, cube.faces).corners | |
| (a, b, c, d, e, f, g, h) | |
| References | |
| ========== | |
| .. [1] www.ocf.berkeley.edu/~wwu/articles/platonicsolids.pdf | |
| """ | |
| faces = [minlex(f, directed=False, key=default_sort_key) for f in faces] | |
| corners, faces, pgroup = args = \ | |
| [Tuple(*a) for a in (corners, faces, pgroup)] | |
| obj = Basic.__new__(cls, *args) | |
| obj._corners = tuple(corners) # in order given | |
| obj._faces = FiniteSet(*faces) | |
| if pgroup and pgroup[0].size != len(corners): | |
| raise ValueError("Permutation size unequal to number of corners.") | |
| # use the identity permutation if none are given | |
| obj._pgroup = PermutationGroup( | |
| pgroup or [Perm(range(len(corners)))] ) | |
| return obj | |
| def corners(self): | |
| """ | |
| Get the corners of the Polyhedron. | |
| The method ``vertices`` is an alias for ``corners``. | |
| Examples | |
| ======== | |
| >>> from sympy.combinatorics import Polyhedron | |
| >>> from sympy.abc import a, b, c, d | |
| >>> p = Polyhedron(list('abcd')) | |
| >>> p.corners == p.vertices == (a, b, c, d) | |
| True | |
| See Also | |
| ======== | |
| array_form, cyclic_form | |
| """ | |
| return self._corners | |
| vertices = corners | |
| def array_form(self): | |
| """Return the indices of the corners. | |
| The indices are given relative to the original position of corners. | |
| Examples | |
| ======== | |
| >>> from sympy.combinatorics.polyhedron import tetrahedron | |
| >>> tetrahedron = tetrahedron.copy() | |
| >>> tetrahedron.array_form | |
| [0, 1, 2, 3] | |
| >>> tetrahedron.rotate(0) | |
| >>> tetrahedron.array_form | |
| [0, 2, 3, 1] | |
| >>> tetrahedron.pgroup[0].array_form | |
| [0, 2, 3, 1] | |
| See Also | |
| ======== | |
| corners, cyclic_form | |
| """ | |
| corners = list(self.args[0]) | |
| return [corners.index(c) for c in self.corners] | |
| def cyclic_form(self): | |
| """Return the indices of the corners in cyclic notation. | |
| The indices are given relative to the original position of corners. | |
| See Also | |
| ======== | |
| corners, array_form | |
| """ | |
| return Perm._af_new(self.array_form).cyclic_form | |
| def size(self): | |
| """ | |
| Get the number of corners of the Polyhedron. | |
| """ | |
| return len(self._corners) | |
| def faces(self): | |
| """ | |
| Get the faces of the Polyhedron. | |
| """ | |
| return self._faces | |
| def pgroup(self): | |
| """ | |
| Get the permutations of the Polyhedron. | |
| """ | |
| return self._pgroup | |
| def edges(self): | |
| """ | |
| Given the faces of the polyhedra we can get the edges. | |
| Examples | |
| ======== | |
| >>> from sympy.combinatorics import Polyhedron | |
| >>> from sympy.abc import a, b, c | |
| >>> corners = (a, b, c) | |
| >>> faces = [(0, 1, 2)] | |
| >>> Polyhedron(corners, faces).edges | |
| {(0, 1), (0, 2), (1, 2)} | |
| """ | |
| if self._edges is None: | |
| output = set() | |
| for face in self.faces: | |
| for i in range(len(face)): | |
| edge = tuple(sorted([face[i], face[i - 1]])) | |
| output.add(edge) | |
| self._edges = FiniteSet(*output) | |
| return self._edges | |
| def rotate(self, perm): | |
| """ | |
| Apply a permutation to the polyhedron *in place*. The permutation | |
| may be given as a Permutation instance or an integer indicating | |
| which permutation from pgroup of the Polyhedron should be | |
| applied. | |
| This is an operation that is analogous to rotation about | |
| an axis by a fixed increment. | |
| Notes | |
| ===== | |
| When a Permutation is applied, no check is done to see if that | |
| is a valid permutation for the Polyhedron. For example, a cube | |
| could be given a permutation which effectively swaps only 2 | |
| vertices. A valid permutation (that rotates the object in a | |
| physical way) will be obtained if one only uses | |
| permutations from the ``pgroup`` of the Polyhedron. On the other | |
| hand, allowing arbitrary rotations (applications of permutations) | |
| gives a way to follow named elements rather than indices since | |
| Polyhedron allows vertices to be named while Permutation works | |
| only with indices. | |
| Examples | |
| ======== | |
| >>> from sympy.combinatorics import Polyhedron, Permutation | |
| >>> from sympy.combinatorics.polyhedron import cube | |
| >>> cube = cube.copy() | |
| >>> cube.corners | |
| (0, 1, 2, 3, 4, 5, 6, 7) | |
| >>> cube.rotate(0) | |
| >>> cube.corners | |
| (1, 2, 3, 0, 5, 6, 7, 4) | |
| A non-physical "rotation" that is not prohibited by this method: | |
| >>> cube.reset() | |
| >>> cube.rotate(Permutation([[1, 2]], size=8)) | |
| >>> cube.corners | |
| (0, 2, 1, 3, 4, 5, 6, 7) | |
| Polyhedron can be used to follow elements of set that are | |
| identified by letters instead of integers: | |
| >>> shadow = h5 = Polyhedron(list('abcde')) | |
| >>> p = Permutation([3, 0, 1, 2, 4]) | |
| >>> h5.rotate(p) | |
| >>> h5.corners | |
| (d, a, b, c, e) | |
| >>> _ == shadow.corners | |
| True | |
| >>> copy = h5.copy() | |
| >>> h5.rotate(p) | |
| >>> h5.corners == copy.corners | |
| False | |
| """ | |
| if not isinstance(perm, Perm): | |
| perm = self.pgroup[perm] | |
| # and we know it's valid | |
| else: | |
| if perm.size != self.size: | |
| raise ValueError('Polyhedron and Permutation sizes differ.') | |
| a = perm.array_form | |
| corners = [self.corners[a[i]] for i in range(len(self.corners))] | |
| self._corners = tuple(corners) | |
| def reset(self): | |
| """Return corners to their original positions. | |
| Examples | |
| ======== | |
| >>> from sympy.combinatorics.polyhedron import tetrahedron as T | |
| >>> T = T.copy() | |
| >>> T.corners | |
| (0, 1, 2, 3) | |
| >>> T.rotate(0) | |
| >>> T.corners | |
| (0, 2, 3, 1) | |
| >>> T.reset() | |
| >>> T.corners | |
| (0, 1, 2, 3) | |
| """ | |
| self._corners = self.args[0] | |
| def _pgroup_calcs(): | |
| """Return the permutation groups for each of the polyhedra and the face | |
| definitions: tetrahedron, cube, octahedron, dodecahedron, icosahedron, | |
| tetrahedron_faces, cube_faces, octahedron_faces, dodecahedron_faces, | |
| icosahedron_faces | |
| Explanation | |
| =========== | |
| (This author did not find and did not know of a better way to do it though | |
| there likely is such a way.) | |
| Although only 2 permutations are needed for a polyhedron in order to | |
| generate all the possible orientations, a group of permutations is | |
| provided instead. A set of permutations is called a "group" if:: | |
| a*b = c (for any pair of permutations in the group, a and b, their | |
| product, c, is in the group) | |
| a*(b*c) = (a*b)*c (for any 3 permutations in the group associativity holds) | |
| there is an identity permutation, I, such that I*a = a*I for all elements | |
| in the group | |
| a*b = I (the inverse of each permutation is also in the group) | |
| None of the polyhedron groups defined follow these definitions of a group. | |
| Instead, they are selected to contain those permutations whose powers | |
| alone will construct all orientations of the polyhedron, i.e. for | |
| permutations ``a``, ``b``, etc... in the group, ``a, a**2, ..., a**o_a``, | |
| ``b, b**2, ..., b**o_b``, etc... (where ``o_i`` is the order of | |
| permutation ``i``) generate all permutations of the polyhedron instead of | |
| mixed products like ``a*b``, ``a*b**2``, etc.... | |
| Note that for a polyhedron with n vertices, the valid permutations of the | |
| vertices exclude those that do not maintain its faces. e.g. the | |
| permutation BCDE of a square's four corners, ABCD, is a valid | |
| permutation while CBDE is not (because this would twist the square). | |
| Examples | |
| ======== | |
| The is_group checks for: closure, the presence of the Identity permutation, | |
| and the presence of the inverse for each of the elements in the group. This | |
| confirms that none of the polyhedra are true groups: | |
| >>> from sympy.combinatorics.polyhedron import ( | |
| ... tetrahedron, cube, octahedron, dodecahedron, icosahedron) | |
| ... | |
| >>> polyhedra = (tetrahedron, cube, octahedron, dodecahedron, icosahedron) | |
| >>> [h.pgroup.is_group for h in polyhedra] | |
| ... | |
| [True, True, True, True, True] | |
| Although tests in polyhedron's test suite check that powers of the | |
| permutations in the groups generate all permutations of the vertices | |
| of the polyhedron, here we also demonstrate the powers of the given | |
| permutations create a complete group for the tetrahedron: | |
| >>> from sympy.combinatorics import Permutation, PermutationGroup | |
| >>> for h in polyhedra[:1]: | |
| ... G = h.pgroup | |
| ... perms = set() | |
| ... for g in G: | |
| ... for e in range(g.order()): | |
| ... p = tuple((g**e).array_form) | |
| ... perms.add(p) | |
| ... | |
| ... perms = [Permutation(p) for p in perms] | |
| ... assert PermutationGroup(perms).is_group | |
| In addition to doing the above, the tests in the suite confirm that the | |
| faces are all present after the application of each permutation. | |
| References | |
| ========== | |
| .. [1] https://dogschool.tripod.com/trianglegroup.html | |
| """ | |
| def _pgroup_of_double(polyh, ordered_faces, pgroup): | |
| n = len(ordered_faces[0]) | |
| # the vertices of the double which sits inside a give polyhedron | |
| # can be found by tracking the faces of the outer polyhedron. | |
| # A map between face and the vertex of the double is made so that | |
| # after rotation the position of the vertices can be located | |
| fmap = dict(zip(ordered_faces, | |
| range(len(ordered_faces)))) | |
| flat_faces = flatten(ordered_faces) | |
| new_pgroup = [] | |
| for p in pgroup: | |
| h = polyh.copy() | |
| h.rotate(p) | |
| c = h.corners | |
| # reorder corners in the order they should appear when | |
| # enumerating the faces | |
| reorder = unflatten([c[j] for j in flat_faces], n) | |
| # make them canonical | |
| reorder = [tuple(map(as_int, | |
| minlex(f, directed=False))) | |
| for f in reorder] | |
| # map face to vertex: the resulting list of vertices are the | |
| # permutation that we seek for the double | |
| new_pgroup.append(Perm([fmap[f] for f in reorder])) | |
| return new_pgroup | |
| tetrahedron_faces = [ | |
| (0, 1, 2), (0, 2, 3), (0, 3, 1), # upper 3 | |
| (1, 2, 3), # bottom | |
| ] | |
| # cw from top | |
| # | |
| _t_pgroup = [ | |
| Perm([[1, 2, 3], [0]]), # cw from top | |
| Perm([[0, 1, 2], [3]]), # cw from front face | |
| Perm([[0, 3, 2], [1]]), # cw from back right face | |
| Perm([[0, 3, 1], [2]]), # cw from back left face | |
| Perm([[0, 1], [2, 3]]), # through front left edge | |
| Perm([[0, 2], [1, 3]]), # through front right edge | |
| Perm([[0, 3], [1, 2]]), # through back edge | |
| ] | |
| tetrahedron = Polyhedron( | |
| range(4), | |
| tetrahedron_faces, | |
| _t_pgroup) | |
| cube_faces = [ | |
| (0, 1, 2, 3), # upper | |
| (0, 1, 5, 4), (1, 2, 6, 5), (2, 3, 7, 6), (0, 3, 7, 4), # middle 4 | |
| (4, 5, 6, 7), # lower | |
| ] | |
| # U, D, F, B, L, R = up, down, front, back, left, right | |
| _c_pgroup = [Perm(p) for p in | |
| [ | |
| [1, 2, 3, 0, 5, 6, 7, 4], # cw from top, U | |
| [4, 0, 3, 7, 5, 1, 2, 6], # cw from F face | |
| [4, 5, 1, 0, 7, 6, 2, 3], # cw from R face | |
| [1, 0, 4, 5, 2, 3, 7, 6], # cw through UF edge | |
| [6, 2, 1, 5, 7, 3, 0, 4], # cw through UR edge | |
| [6, 7, 3, 2, 5, 4, 0, 1], # cw through UB edge | |
| [3, 7, 4, 0, 2, 6, 5, 1], # cw through UL edge | |
| [4, 7, 6, 5, 0, 3, 2, 1], # cw through FL edge | |
| [6, 5, 4, 7, 2, 1, 0, 3], # cw through FR edge | |
| [0, 3, 7, 4, 1, 2, 6, 5], # cw through UFL vertex | |
| [5, 1, 0, 4, 6, 2, 3, 7], # cw through UFR vertex | |
| [5, 6, 2, 1, 4, 7, 3, 0], # cw through UBR vertex | |
| [7, 4, 0, 3, 6, 5, 1, 2], # cw through UBL | |
| ]] | |
| cube = Polyhedron( | |
| range(8), | |
| cube_faces, | |
| _c_pgroup) | |
| octahedron_faces = [ | |
| (0, 1, 2), (0, 2, 3), (0, 3, 4), (0, 1, 4), # top 4 | |
| (1, 2, 5), (2, 3, 5), (3, 4, 5), (1, 4, 5), # bottom 4 | |
| ] | |
| octahedron = Polyhedron( | |
| range(6), | |
| octahedron_faces, | |
| _pgroup_of_double(cube, cube_faces, _c_pgroup)) | |
| dodecahedron_faces = [ | |
| (0, 1, 2, 3, 4), # top | |
| (0, 1, 6, 10, 5), (1, 2, 7, 11, 6), (2, 3, 8, 12, 7), # upper 5 | |
| (3, 4, 9, 13, 8), (0, 4, 9, 14, 5), | |
| (5, 10, 16, 15, 14), (6, 10, 16, 17, 11), (7, 11, 17, 18, | |
| 12), # lower 5 | |
| (8, 12, 18, 19, 13), (9, 13, 19, 15, 14), | |
| (15, 16, 17, 18, 19) # bottom | |
| ] | |
| def _string_to_perm(s): | |
| rv = [Perm(range(20))] | |
| p = None | |
| for si in s: | |
| if si not in '01': | |
| count = int(si) - 1 | |
| else: | |
| count = 1 | |
| if si == '0': | |
| p = _f0 | |
| elif si == '1': | |
| p = _f1 | |
| rv.extend([p]*count) | |
| return Perm.rmul(*rv) | |
| # top face cw | |
| _f0 = Perm([ | |
| 1, 2, 3, 4, 0, 6, 7, 8, 9, 5, 11, | |
| 12, 13, 14, 10, 16, 17, 18, 19, 15]) | |
| # front face cw | |
| _f1 = Perm([ | |
| 5, 0, 4, 9, 14, 10, 1, 3, 13, 15, | |
| 6, 2, 8, 19, 16, 17, 11, 7, 12, 18]) | |
| # the strings below, like 0104 are shorthand for F0*F1*F0**4 and are | |
| # the remaining 4 face rotations, 15 edge permutations, and the | |
| # 10 vertex rotations. | |
| _dodeca_pgroup = [_f0, _f1] + [_string_to_perm(s) for s in ''' | |
| 0104 140 014 0410 | |
| 010 1403 03104 04103 102 | |
| 120 1304 01303 021302 03130 | |
| 0412041 041204103 04120410 041204104 041204102 | |
| 10 01 1402 0140 04102 0412 1204 1302 0130 03120'''.strip().split()] | |
| dodecahedron = Polyhedron( | |
| range(20), | |
| dodecahedron_faces, | |
| _dodeca_pgroup) | |
| icosahedron_faces = [ | |
| (0, 1, 2), (0, 2, 3), (0, 3, 4), (0, 4, 5), (0, 1, 5), | |
| (1, 6, 7), (1, 2, 7), (2, 7, 8), (2, 3, 8), (3, 8, 9), | |
| (3, 4, 9), (4, 9, 10), (4, 5, 10), (5, 6, 10), (1, 5, 6), | |
| (6, 7, 11), (7, 8, 11), (8, 9, 11), (9, 10, 11), (6, 10, 11)] | |
| icosahedron = Polyhedron( | |
| range(12), | |
| icosahedron_faces, | |
| _pgroup_of_double( | |
| dodecahedron, dodecahedron_faces, _dodeca_pgroup)) | |
| return (tetrahedron, cube, octahedron, dodecahedron, icosahedron, | |
| tetrahedron_faces, cube_faces, octahedron_faces, | |
| dodecahedron_faces, icosahedron_faces) | |
| # ----------------------------------------------------------------------- | |
| # Standard Polyhedron groups | |
| # | |
| # These are generated using _pgroup_calcs() above. However to save | |
| # import time we encode them explicitly here. | |
| # ----------------------------------------------------------------------- | |
| tetrahedron = Polyhedron( | |
| Tuple(0, 1, 2, 3), | |
| Tuple( | |
| Tuple(0, 1, 2), | |
| Tuple(0, 2, 3), | |
| Tuple(0, 1, 3), | |
| Tuple(1, 2, 3)), | |
| Tuple( | |
| Perm(1, 2, 3), | |
| Perm(3)(0, 1, 2), | |
| Perm(0, 3, 2), | |
| Perm(0, 3, 1), | |
| Perm(0, 1)(2, 3), | |
| Perm(0, 2)(1, 3), | |
| Perm(0, 3)(1, 2) | |
| )) | |
| cube = Polyhedron( | |
| Tuple(0, 1, 2, 3, 4, 5, 6, 7), | |
| Tuple( | |
| Tuple(0, 1, 2, 3), | |
| Tuple(0, 1, 5, 4), | |
| Tuple(1, 2, 6, 5), | |
| Tuple(2, 3, 7, 6), | |
| Tuple(0, 3, 7, 4), | |
| Tuple(4, 5, 6, 7)), | |
| Tuple( | |
| Perm(0, 1, 2, 3)(4, 5, 6, 7), | |
| Perm(0, 4, 5, 1)(2, 3, 7, 6), | |
| Perm(0, 4, 7, 3)(1, 5, 6, 2), | |
| Perm(0, 1)(2, 4)(3, 5)(6, 7), | |
| Perm(0, 6)(1, 2)(3, 5)(4, 7), | |
| Perm(0, 6)(1, 7)(2, 3)(4, 5), | |
| Perm(0, 3)(1, 7)(2, 4)(5, 6), | |
| Perm(0, 4)(1, 7)(2, 6)(3, 5), | |
| Perm(0, 6)(1, 5)(2, 4)(3, 7), | |
| Perm(1, 3, 4)(2, 7, 5), | |
| Perm(7)(0, 5, 2)(3, 4, 6), | |
| Perm(0, 5, 7)(1, 6, 3), | |
| Perm(0, 7, 2)(1, 4, 6))) | |
| octahedron = Polyhedron( | |
| Tuple(0, 1, 2, 3, 4, 5), | |
| Tuple( | |
| Tuple(0, 1, 2), | |
| Tuple(0, 2, 3), | |
| Tuple(0, 3, 4), | |
| Tuple(0, 1, 4), | |
| Tuple(1, 2, 5), | |
| Tuple(2, 3, 5), | |
| Tuple(3, 4, 5), | |
| Tuple(1, 4, 5)), | |
| Tuple( | |
| Perm(5)(1, 2, 3, 4), | |
| Perm(0, 4, 5, 2), | |
| Perm(0, 1, 5, 3), | |
| Perm(0, 1)(2, 4)(3, 5), | |
| Perm(0, 2)(1, 3)(4, 5), | |
| Perm(0, 3)(1, 5)(2, 4), | |
| Perm(0, 4)(1, 3)(2, 5), | |
| Perm(0, 5)(1, 4)(2, 3), | |
| Perm(0, 5)(1, 2)(3, 4), | |
| Perm(0, 4, 1)(2, 3, 5), | |
| Perm(0, 1, 2)(3, 4, 5), | |
| Perm(0, 2, 3)(1, 5, 4), | |
| Perm(0, 4, 3)(1, 5, 2))) | |
| dodecahedron = Polyhedron( | |
| Tuple(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19), | |
| Tuple( | |
| Tuple(0, 1, 2, 3, 4), | |
| Tuple(0, 1, 6, 10, 5), | |
| Tuple(1, 2, 7, 11, 6), | |
| Tuple(2, 3, 8, 12, 7), | |
| Tuple(3, 4, 9, 13, 8), | |
| Tuple(0, 4, 9, 14, 5), | |
| Tuple(5, 10, 16, 15, 14), | |
| Tuple(6, 10, 16, 17, 11), | |
| Tuple(7, 11, 17, 18, 12), | |
| Tuple(8, 12, 18, 19, 13), | |
| Tuple(9, 13, 19, 15, 14), | |
| Tuple(15, 16, 17, 18, 19)), | |
| Tuple( | |
| Perm(0, 1, 2, 3, 4)(5, 6, 7, 8, 9)(10, 11, 12, 13, 14)(15, 16, 17, 18, 19), | |
| Perm(0, 5, 10, 6, 1)(2, 4, 14, 16, 11)(3, 9, 15, 17, 7)(8, 13, 19, 18, 12), | |
| Perm(0, 10, 17, 12, 3)(1, 6, 11, 7, 2)(4, 5, 16, 18, 8)(9, 14, 15, 19, 13), | |
| Perm(0, 6, 17, 19, 9)(1, 11, 18, 13, 4)(2, 7, 12, 8, 3)(5, 10, 16, 15, 14), | |
| Perm(0, 2, 12, 19, 14)(1, 7, 18, 15, 5)(3, 8, 13, 9, 4)(6, 11, 17, 16, 10), | |
| Perm(0, 4, 9, 14, 5)(1, 3, 13, 15, 10)(2, 8, 19, 16, 6)(7, 12, 18, 17, 11), | |
| Perm(0, 1)(2, 5)(3, 10)(4, 6)(7, 14)(8, 16)(9, 11)(12, 15)(13, 17)(18, 19), | |
| Perm(0, 7)(1, 2)(3, 6)(4, 11)(5, 12)(8, 10)(9, 17)(13, 16)(14, 18)(15, 19), | |
| Perm(0, 12)(1, 8)(2, 3)(4, 7)(5, 18)(6, 13)(9, 11)(10, 19)(14, 17)(15, 16), | |
| Perm(0, 8)(1, 13)(2, 9)(3, 4)(5, 12)(6, 19)(7, 14)(10, 18)(11, 15)(16, 17), | |
| Perm(0, 4)(1, 9)(2, 14)(3, 5)(6, 13)(7, 15)(8, 10)(11, 19)(12, 16)(17, 18), | |
| Perm(0, 5)(1, 14)(2, 15)(3, 16)(4, 10)(6, 9)(7, 19)(8, 17)(11, 13)(12, 18), | |
| Perm(0, 11)(1, 6)(2, 10)(3, 16)(4, 17)(5, 7)(8, 15)(9, 18)(12, 14)(13, 19), | |
| Perm(0, 18)(1, 12)(2, 7)(3, 11)(4, 17)(5, 19)(6, 8)(9, 16)(10, 13)(14, 15), | |
| Perm(0, 18)(1, 19)(2, 13)(3, 8)(4, 12)(5, 17)(6, 15)(7, 9)(10, 16)(11, 14), | |
| Perm(0, 13)(1, 19)(2, 15)(3, 14)(4, 9)(5, 8)(6, 18)(7, 16)(10, 12)(11, 17), | |
| Perm(0, 16)(1, 15)(2, 19)(3, 18)(4, 17)(5, 10)(6, 14)(7, 13)(8, 12)(9, 11), | |
| Perm(0, 18)(1, 17)(2, 16)(3, 15)(4, 19)(5, 12)(6, 11)(7, 10)(8, 14)(9, 13), | |
| Perm(0, 15)(1, 19)(2, 18)(3, 17)(4, 16)(5, 14)(6, 13)(7, 12)(8, 11)(9, 10), | |
| Perm(0, 17)(1, 16)(2, 15)(3, 19)(4, 18)(5, 11)(6, 10)(7, 14)(8, 13)(9, 12), | |
| Perm(0, 19)(1, 18)(2, 17)(3, 16)(4, 15)(5, 13)(6, 12)(7, 11)(8, 10)(9, 14), | |
| Perm(1, 4, 5)(2, 9, 10)(3, 14, 6)(7, 13, 16)(8, 15, 11)(12, 19, 17), | |
| Perm(19)(0, 6, 2)(3, 5, 11)(4, 10, 7)(8, 14, 17)(9, 16, 12)(13, 15, 18), | |
| Perm(0, 11, 8)(1, 7, 3)(4, 6, 12)(5, 17, 13)(9, 10, 18)(14, 16, 19), | |
| Perm(0, 7, 13)(1, 12, 9)(2, 8, 4)(5, 11, 19)(6, 18, 14)(10, 17, 15), | |
| Perm(0, 3, 9)(1, 8, 14)(2, 13, 5)(6, 12, 15)(7, 19, 10)(11, 18, 16), | |
| Perm(0, 14, 10)(1, 9, 16)(2, 13, 17)(3, 19, 11)(4, 15, 6)(7, 8, 18), | |
| Perm(0, 16, 7)(1, 10, 11)(2, 5, 17)(3, 14, 18)(4, 15, 12)(8, 9, 19), | |
| Perm(0, 16, 13)(1, 17, 8)(2, 11, 12)(3, 6, 18)(4, 10, 19)(5, 15, 9), | |
| Perm(0, 11, 15)(1, 17, 14)(2, 18, 9)(3, 12, 13)(4, 7, 19)(5, 6, 16), | |
| Perm(0, 8, 15)(1, 12, 16)(2, 18, 10)(3, 19, 5)(4, 13, 14)(6, 7, 17))) | |
| icosahedron = Polyhedron( | |
| Tuple(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), | |
| Tuple( | |
| Tuple(0, 1, 2), | |
| Tuple(0, 2, 3), | |
| Tuple(0, 3, 4), | |
| Tuple(0, 4, 5), | |
| Tuple(0, 1, 5), | |
| Tuple(1, 6, 7), | |
| Tuple(1, 2, 7), | |
| Tuple(2, 7, 8), | |
| Tuple(2, 3, 8), | |
| Tuple(3, 8, 9), | |
| Tuple(3, 4, 9), | |
| Tuple(4, 9, 10), | |
| Tuple(4, 5, 10), | |
| Tuple(5, 6, 10), | |
| Tuple(1, 5, 6), | |
| Tuple(6, 7, 11), | |
| Tuple(7, 8, 11), | |
| Tuple(8, 9, 11), | |
| Tuple(9, 10, 11), | |
| Tuple(6, 10, 11)), | |
| Tuple( | |
| Perm(11)(1, 2, 3, 4, 5)(6, 7, 8, 9, 10), | |
| Perm(0, 5, 6, 7, 2)(3, 4, 10, 11, 8), | |
| Perm(0, 1, 7, 8, 3)(4, 5, 6, 11, 9), | |
| Perm(0, 2, 8, 9, 4)(1, 7, 11, 10, 5), | |
| Perm(0, 3, 9, 10, 5)(1, 2, 8, 11, 6), | |
| Perm(0, 4, 10, 6, 1)(2, 3, 9, 11, 7), | |
| Perm(0, 1)(2, 5)(3, 6)(4, 7)(8, 10)(9, 11), | |
| Perm(0, 2)(1, 3)(4, 7)(5, 8)(6, 9)(10, 11), | |
| Perm(0, 3)(1, 9)(2, 4)(5, 8)(6, 11)(7, 10), | |
| Perm(0, 4)(1, 9)(2, 10)(3, 5)(6, 8)(7, 11), | |
| Perm(0, 5)(1, 4)(2, 10)(3, 6)(7, 9)(8, 11), | |
| Perm(0, 6)(1, 5)(2, 10)(3, 11)(4, 7)(8, 9), | |
| Perm(0, 7)(1, 2)(3, 6)(4, 11)(5, 8)(9, 10), | |
| Perm(0, 8)(1, 9)(2, 3)(4, 7)(5, 11)(6, 10), | |
| Perm(0, 9)(1, 11)(2, 10)(3, 4)(5, 8)(6, 7), | |
| Perm(0, 10)(1, 9)(2, 11)(3, 6)(4, 5)(7, 8), | |
| Perm(0, 11)(1, 6)(2, 10)(3, 9)(4, 8)(5, 7), | |
| Perm(0, 11)(1, 8)(2, 7)(3, 6)(4, 10)(5, 9), | |
| Perm(0, 11)(1, 10)(2, 9)(3, 8)(4, 7)(5, 6), | |
| Perm(0, 11)(1, 7)(2, 6)(3, 10)(4, 9)(5, 8), | |
| Perm(0, 11)(1, 9)(2, 8)(3, 7)(4, 6)(5, 10), | |
| Perm(0, 5, 1)(2, 4, 6)(3, 10, 7)(8, 9, 11), | |
| Perm(0, 1, 2)(3, 5, 7)(4, 6, 8)(9, 10, 11), | |
| Perm(0, 2, 3)(1, 8, 4)(5, 7, 9)(6, 11, 10), | |
| Perm(0, 3, 4)(1, 8, 10)(2, 9, 5)(6, 7, 11), | |
| Perm(0, 4, 5)(1, 3, 10)(2, 9, 6)(7, 8, 11), | |
| Perm(0, 10, 7)(1, 5, 6)(2, 4, 11)(3, 9, 8), | |
| Perm(0, 6, 8)(1, 7, 2)(3, 5, 11)(4, 10, 9), | |
| Perm(0, 7, 9)(1, 11, 4)(2, 8, 3)(5, 6, 10), | |
| Perm(0, 8, 10)(1, 7, 6)(2, 11, 5)(3, 9, 4), | |
| Perm(0, 9, 6)(1, 3, 11)(2, 8, 7)(4, 10, 5))) | |
| tetrahedron_faces = [tuple(arg) for arg in tetrahedron.faces] | |
| cube_faces = [tuple(arg) for arg in cube.faces] | |
| octahedron_faces = [tuple(arg) for arg in octahedron.faces] | |
| dodecahedron_faces = [tuple(arg) for arg in dodecahedron.faces] | |
| icosahedron_faces = [tuple(arg) for arg in icosahedron.faces] | |