12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019 |
- 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] http://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
- @property
- 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
- @property
- 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]
- @property
- 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
- @property
- def size(self):
- """
- Get the number of corners of the Polyhedron.
- """
- return len(self._corners)
- @property
- def faces(self):
- """
- Get the faces of the Polyhedron.
- """
- return self._faces
- @property
- def pgroup(self):
- """
- Get the permutations of the Polyhedron.
- """
- return self._pgroup
- @property
- 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 didn't find and didn't 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] http://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 i, p in enumerate(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 = list(tuple(arg) for arg in tetrahedron.faces)
- cube_faces = list(tuple(arg) for arg in cube.faces)
- octahedron_faces = list(tuple(arg) for arg in octahedron.faces)
- dodecahedron_faces = list(tuple(arg) for arg in dodecahedron.faces)
- icosahedron_faces = list(tuple(arg) for arg in icosahedron.faces)
|