polyhedron.py 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. from sympy.combinatorics import Permutation as Perm
  2. from sympy.combinatorics.perm_groups import PermutationGroup
  3. from sympy.core import Basic, Tuple, default_sort_key
  4. from sympy.sets import FiniteSet
  5. from sympy.utilities.iterables import (minlex, unflatten, flatten)
  6. from sympy.utilities.misc import as_int
  7. rmul = Perm.rmul
  8. class Polyhedron(Basic):
  9. """
  10. Represents the polyhedral symmetry group (PSG).
  11. Explanation
  12. ===========
  13. The PSG is one of the symmetry groups of the Platonic solids.
  14. There are three polyhedral groups: the tetrahedral group
  15. of order 12, the octahedral group of order 24, and the
  16. icosahedral group of order 60.
  17. All doctests have been given in the docstring of the
  18. constructor of the object.
  19. References
  20. ==========
  21. .. [1] http://mathworld.wolfram.com/PolyhedralGroup.html
  22. """
  23. _edges = None
  24. def __new__(cls, corners, faces=(), pgroup=()):
  25. """
  26. The constructor of the Polyhedron group object.
  27. Explanation
  28. ===========
  29. It takes up to three parameters: the corners, faces, and
  30. allowed transformations.
  31. The corners/vertices are entered as a list of arbitrary
  32. expressions that are used to identify each vertex.
  33. The faces are entered as a list of tuples of indices; a tuple
  34. of indices identifies the vertices which define the face. They
  35. should be entered in a cw or ccw order; they will be standardized
  36. by reversal and rotation to be give the lowest lexical ordering.
  37. If no faces are given then no edges will be computed.
  38. >>> from sympy.combinatorics.polyhedron import Polyhedron
  39. >>> Polyhedron(list('abc'), [(1, 2, 0)]).faces
  40. {(0, 1, 2)}
  41. >>> Polyhedron(list('abc'), [(1, 0, 2)]).faces
  42. {(0, 1, 2)}
  43. The allowed transformations are entered as allowable permutations
  44. of the vertices for the polyhedron. Instance of Permutations
  45. (as with faces) should refer to the supplied vertices by index.
  46. These permutation are stored as a PermutationGroup.
  47. Examples
  48. ========
  49. >>> from sympy.combinatorics.permutations import Permutation
  50. >>> from sympy import init_printing
  51. >>> from sympy.abc import w, x, y, z
  52. >>> init_printing(pretty_print=False, perm_cyclic=False)
  53. Here we construct the Polyhedron object for a tetrahedron.
  54. >>> corners = [w, x, y, z]
  55. >>> faces = [(0, 1, 2), (0, 2, 3), (0, 3, 1), (1, 2, 3)]
  56. Next, allowed transformations of the polyhedron must be given. This
  57. is given as permutations of vertices.
  58. Although the vertices of a tetrahedron can be numbered in 24 (4!)
  59. different ways, there are only 12 different orientations for a
  60. physical tetrahedron. The following permutations, applied once or
  61. twice, will generate all 12 of the orientations. (The identity
  62. permutation, Permutation(range(4)), is not included since it does
  63. not change the orientation of the vertices.)
  64. >>> pgroup = [Permutation([[0, 1, 2], [3]]), \
  65. Permutation([[0, 1, 3], [2]]), \
  66. Permutation([[0, 2, 3], [1]]), \
  67. Permutation([[1, 2, 3], [0]]), \
  68. Permutation([[0, 1], [2, 3]]), \
  69. Permutation([[0, 2], [1, 3]]), \
  70. Permutation([[0, 3], [1, 2]])]
  71. The Polyhedron is now constructed and demonstrated:
  72. >>> tetra = Polyhedron(corners, faces, pgroup)
  73. >>> tetra.size
  74. 4
  75. >>> tetra.edges
  76. {(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)}
  77. >>> tetra.corners
  78. (w, x, y, z)
  79. It can be rotated with an arbitrary permutation of vertices, e.g.
  80. the following permutation is not in the pgroup:
  81. >>> tetra.rotate(Permutation([0, 1, 3, 2]))
  82. >>> tetra.corners
  83. (w, x, z, y)
  84. An allowed permutation of the vertices can be constructed by
  85. repeatedly applying permutations from the pgroup to the vertices.
  86. Here is a demonstration that applying p and p**2 for every p in
  87. pgroup generates all the orientations of a tetrahedron and no others:
  88. >>> all = ( (w, x, y, z), \
  89. (x, y, w, z), \
  90. (y, w, x, z), \
  91. (w, z, x, y), \
  92. (z, w, y, x), \
  93. (w, y, z, x), \
  94. (y, z, w, x), \
  95. (x, z, y, w), \
  96. (z, y, x, w), \
  97. (y, x, z, w), \
  98. (x, w, z, y), \
  99. (z, x, w, y) )
  100. >>> got = []
  101. >>> for p in (pgroup + [p**2 for p in pgroup]):
  102. ... h = Polyhedron(corners)
  103. ... h.rotate(p)
  104. ... got.append(h.corners)
  105. ...
  106. >>> set(got) == set(all)
  107. True
  108. The make_perm method of a PermutationGroup will randomly pick
  109. permutations, multiply them together, and return the permutation that
  110. can be applied to the polyhedron to give the orientation produced
  111. by those individual permutations.
  112. Here, 3 permutations are used:
  113. >>> tetra.pgroup.make_perm(3) # doctest: +SKIP
  114. Permutation([0, 3, 1, 2])
  115. To select the permutations that should be used, supply a list
  116. of indices to the permutations in pgroup in the order they should
  117. be applied:
  118. >>> use = [0, 0, 2]
  119. >>> p002 = tetra.pgroup.make_perm(3, use)
  120. >>> p002
  121. Permutation([1, 0, 3, 2])
  122. Apply them one at a time:
  123. >>> tetra.reset()
  124. >>> for i in use:
  125. ... tetra.rotate(pgroup[i])
  126. ...
  127. >>> tetra.vertices
  128. (x, w, z, y)
  129. >>> sequentially = tetra.vertices
  130. Apply the composite permutation:
  131. >>> tetra.reset()
  132. >>> tetra.rotate(p002)
  133. >>> tetra.corners
  134. (x, w, z, y)
  135. >>> tetra.corners in all and tetra.corners == sequentially
  136. True
  137. Notes
  138. =====
  139. Defining permutation groups
  140. ---------------------------
  141. It is not necessary to enter any permutations, nor is necessary to
  142. enter a complete set of transformations. In fact, for a polyhedron,
  143. all configurations can be constructed from just two permutations.
  144. For example, the orientations of a tetrahedron can be generated from
  145. an axis passing through a vertex and face and another axis passing
  146. through a different vertex or from an axis passing through the
  147. midpoints of two edges opposite of each other.
  148. For simplicity of presentation, consider a square --
  149. not a cube -- with vertices 1, 2, 3, and 4:
  150. 1-----2 We could think of axes of rotation being:
  151. | | 1) through the face
  152. | | 2) from midpoint 1-2 to 3-4 or 1-3 to 2-4
  153. 3-----4 3) lines 1-4 or 2-3
  154. To determine how to write the permutations, imagine 4 cameras,
  155. one at each corner, labeled A-D:
  156. A B A B
  157. 1-----2 1-----3 vertex index:
  158. | | | | 1 0
  159. | | | | 2 1
  160. 3-----4 2-----4 3 2
  161. C D C D 4 3
  162. original after rotation
  163. along 1-4
  164. A diagonal and a face axis will be chosen for the "permutation group"
  165. from which any orientation can be constructed.
  166. >>> pgroup = []
  167. Imagine a clockwise rotation when viewing 1-4 from camera A. The new
  168. orientation is (in camera-order): 1, 3, 2, 4 so the permutation is
  169. given using the *indices* of the vertices as:
  170. >>> pgroup.append(Permutation((0, 2, 1, 3)))
  171. Now imagine rotating clockwise when looking down an axis entering the
  172. center of the square as viewed. The new camera-order would be
  173. 3, 1, 4, 2 so the permutation is (using indices):
  174. >>> pgroup.append(Permutation((2, 0, 3, 1)))
  175. The square can now be constructed:
  176. ** use real-world labels for the vertices, entering them in
  177. camera order
  178. ** for the faces we use zero-based indices of the vertices
  179. in *edge-order* as the face is traversed; neither the
  180. direction nor the starting point matter -- the faces are
  181. only used to define edges (if so desired).
  182. >>> square = Polyhedron((1, 2, 3, 4), [(0, 1, 3, 2)], pgroup)
  183. To rotate the square with a single permutation we can do:
  184. >>> square.rotate(square.pgroup[0])
  185. >>> square.corners
  186. (1, 3, 2, 4)
  187. To use more than one permutation (or to use one permutation more
  188. than once) it is more convenient to use the make_perm method:
  189. >>> p011 = square.pgroup.make_perm([0, 1, 1]) # diag flip + 2 rotations
  190. >>> square.reset() # return to initial orientation
  191. >>> square.rotate(p011)
  192. >>> square.corners
  193. (4, 2, 3, 1)
  194. Thinking outside the box
  195. ------------------------
  196. Although the Polyhedron object has a direct physical meaning, it
  197. actually has broader application. In the most general sense it is
  198. just a decorated PermutationGroup, allowing one to connect the
  199. permutations to something physical. For example, a Rubik's cube is
  200. not a proper polyhedron, but the Polyhedron class can be used to
  201. represent it in a way that helps to visualize the Rubik's cube.
  202. >>> from sympy import flatten, unflatten, symbols
  203. >>> from sympy.combinatorics import RubikGroup
  204. >>> facelets = flatten([symbols(s+'1:5') for s in 'UFRBLD'])
  205. >>> def show():
  206. ... pairs = unflatten(r2.corners, 2)
  207. ... print(pairs[::2])
  208. ... print(pairs[1::2])
  209. ...
  210. >>> r2 = Polyhedron(facelets, pgroup=RubikGroup(2))
  211. >>> show()
  212. [(U1, U2), (F1, F2), (R1, R2), (B1, B2), (L1, L2), (D1, D2)]
  213. [(U3, U4), (F3, F4), (R3, R4), (B3, B4), (L3, L4), (D3, D4)]
  214. >>> r2.rotate(0) # cw rotation of F
  215. >>> show()
  216. [(U1, U2), (F3, F1), (U3, R2), (B1, B2), (L1, D1), (R3, R1)]
  217. [(L4, L2), (F4, F2), (U4, R4), (B3, B4), (L3, D2), (D3, D4)]
  218. Predefined Polyhedra
  219. ====================
  220. For convenience, the vertices and faces are defined for the following
  221. standard solids along with a permutation group for transformations.
  222. When the polyhedron is oriented as indicated below, the vertices in
  223. a given horizontal plane are numbered in ccw direction, starting from
  224. the vertex that will give the lowest indices in a given face. (In the
  225. net of the vertices, indices preceded by "-" indicate replication of
  226. the lhs index in the net.)
  227. tetrahedron, tetrahedron_faces
  228. ------------------------------
  229. 4 vertices (vertex up) net:
  230. 0 0-0
  231. 1 2 3-1
  232. 4 faces:
  233. (0, 1, 2) (0, 2, 3) (0, 3, 1) (1, 2, 3)
  234. cube, cube_faces
  235. ----------------
  236. 8 vertices (face up) net:
  237. 0 1 2 3-0
  238. 4 5 6 7-4
  239. 6 faces:
  240. (0, 1, 2, 3)
  241. (0, 1, 5, 4) (1, 2, 6, 5) (2, 3, 7, 6) (0, 3, 7, 4)
  242. (4, 5, 6, 7)
  243. octahedron, octahedron_faces
  244. ----------------------------
  245. 6 vertices (vertex up) net:
  246. 0 0 0-0
  247. 1 2 3 4-1
  248. 5 5 5-5
  249. 8 faces:
  250. (0, 1, 2) (0, 2, 3) (0, 3, 4) (0, 1, 4)
  251. (1, 2, 5) (2, 3, 5) (3, 4, 5) (1, 4, 5)
  252. dodecahedron, dodecahedron_faces
  253. --------------------------------
  254. 20 vertices (vertex up) net:
  255. 0 1 2 3 4 -0
  256. 5 6 7 8 9 -5
  257. 14 10 11 12 13-14
  258. 15 16 17 18 19-15
  259. 12 faces:
  260. (0, 1, 2, 3, 4) (0, 1, 6, 10, 5) (1, 2, 7, 11, 6)
  261. (2, 3, 8, 12, 7) (3, 4, 9, 13, 8) (0, 4, 9, 14, 5)
  262. (5, 10, 16, 15, 14) (6, 10, 16, 17, 11) (7, 11, 17, 18, 12)
  263. (8, 12, 18, 19, 13) (9, 13, 19, 15, 14)(15, 16, 17, 18, 19)
  264. icosahedron, icosahedron_faces
  265. ------------------------------
  266. 12 vertices (face up) net:
  267. 0 0 0 0 -0
  268. 1 2 3 4 5 -1
  269. 6 7 8 9 10 -6
  270. 11 11 11 11 -11
  271. 20 faces:
  272. (0, 1, 2) (0, 2, 3) (0, 3, 4)
  273. (0, 4, 5) (0, 1, 5) (1, 2, 6)
  274. (2, 3, 7) (3, 4, 8) (4, 5, 9)
  275. (1, 5, 10) (2, 6, 7) (3, 7, 8)
  276. (4, 8, 9) (5, 9, 10) (1, 6, 10)
  277. (6, 7, 11) (7, 8, 11) (8, 9, 11)
  278. (9, 10, 11) (6, 10, 11)
  279. >>> from sympy.combinatorics.polyhedron import cube
  280. >>> cube.edges
  281. {(0, 1), (0, 3), (0, 4), (1, 2), (1, 5), (2, 3), (2, 6), (3, 7), (4, 5), (4, 7), (5, 6), (6, 7)}
  282. If you want to use letters or other names for the corners you
  283. can still use the pre-calculated faces:
  284. >>> corners = list('abcdefgh')
  285. >>> Polyhedron(corners, cube.faces).corners
  286. (a, b, c, d, e, f, g, h)
  287. References
  288. ==========
  289. .. [1] www.ocf.berkeley.edu/~wwu/articles/platonicsolids.pdf
  290. """
  291. faces = [minlex(f, directed=False, key=default_sort_key) for f in faces]
  292. corners, faces, pgroup = args = \
  293. [Tuple(*a) for a in (corners, faces, pgroup)]
  294. obj = Basic.__new__(cls, *args)
  295. obj._corners = tuple(corners) # in order given
  296. obj._faces = FiniteSet(*faces)
  297. if pgroup and pgroup[0].size != len(corners):
  298. raise ValueError("Permutation size unequal to number of corners.")
  299. # use the identity permutation if none are given
  300. obj._pgroup = PermutationGroup(
  301. pgroup or [Perm(range(len(corners)))] )
  302. return obj
  303. @property
  304. def corners(self):
  305. """
  306. Get the corners of the Polyhedron.
  307. The method ``vertices`` is an alias for ``corners``.
  308. Examples
  309. ========
  310. >>> from sympy.combinatorics import Polyhedron
  311. >>> from sympy.abc import a, b, c, d
  312. >>> p = Polyhedron(list('abcd'))
  313. >>> p.corners == p.vertices == (a, b, c, d)
  314. True
  315. See Also
  316. ========
  317. array_form, cyclic_form
  318. """
  319. return self._corners
  320. vertices = corners
  321. @property
  322. def array_form(self):
  323. """Return the indices of the corners.
  324. The indices are given relative to the original position of corners.
  325. Examples
  326. ========
  327. >>> from sympy.combinatorics.polyhedron import tetrahedron
  328. >>> tetrahedron = tetrahedron.copy()
  329. >>> tetrahedron.array_form
  330. [0, 1, 2, 3]
  331. >>> tetrahedron.rotate(0)
  332. >>> tetrahedron.array_form
  333. [0, 2, 3, 1]
  334. >>> tetrahedron.pgroup[0].array_form
  335. [0, 2, 3, 1]
  336. See Also
  337. ========
  338. corners, cyclic_form
  339. """
  340. corners = list(self.args[0])
  341. return [corners.index(c) for c in self.corners]
  342. @property
  343. def cyclic_form(self):
  344. """Return the indices of the corners in cyclic notation.
  345. The indices are given relative to the original position of corners.
  346. See Also
  347. ========
  348. corners, array_form
  349. """
  350. return Perm._af_new(self.array_form).cyclic_form
  351. @property
  352. def size(self):
  353. """
  354. Get the number of corners of the Polyhedron.
  355. """
  356. return len(self._corners)
  357. @property
  358. def faces(self):
  359. """
  360. Get the faces of the Polyhedron.
  361. """
  362. return self._faces
  363. @property
  364. def pgroup(self):
  365. """
  366. Get the permutations of the Polyhedron.
  367. """
  368. return self._pgroup
  369. @property
  370. def edges(self):
  371. """
  372. Given the faces of the polyhedra we can get the edges.
  373. Examples
  374. ========
  375. >>> from sympy.combinatorics import Polyhedron
  376. >>> from sympy.abc import a, b, c
  377. >>> corners = (a, b, c)
  378. >>> faces = [(0, 1, 2)]
  379. >>> Polyhedron(corners, faces).edges
  380. {(0, 1), (0, 2), (1, 2)}
  381. """
  382. if self._edges is None:
  383. output = set()
  384. for face in self.faces:
  385. for i in range(len(face)):
  386. edge = tuple(sorted([face[i], face[i - 1]]))
  387. output.add(edge)
  388. self._edges = FiniteSet(*output)
  389. return self._edges
  390. def rotate(self, perm):
  391. """
  392. Apply a permutation to the polyhedron *in place*. The permutation
  393. may be given as a Permutation instance or an integer indicating
  394. which permutation from pgroup of the Polyhedron should be
  395. applied.
  396. This is an operation that is analogous to rotation about
  397. an axis by a fixed increment.
  398. Notes
  399. =====
  400. When a Permutation is applied, no check is done to see if that
  401. is a valid permutation for the Polyhedron. For example, a cube
  402. could be given a permutation which effectively swaps only 2
  403. vertices. A valid permutation (that rotates the object in a
  404. physical way) will be obtained if one only uses
  405. permutations from the ``pgroup`` of the Polyhedron. On the other
  406. hand, allowing arbitrary rotations (applications of permutations)
  407. gives a way to follow named elements rather than indices since
  408. Polyhedron allows vertices to be named while Permutation works
  409. only with indices.
  410. Examples
  411. ========
  412. >>> from sympy.combinatorics import Polyhedron, Permutation
  413. >>> from sympy.combinatorics.polyhedron import cube
  414. >>> cube = cube.copy()
  415. >>> cube.corners
  416. (0, 1, 2, 3, 4, 5, 6, 7)
  417. >>> cube.rotate(0)
  418. >>> cube.corners
  419. (1, 2, 3, 0, 5, 6, 7, 4)
  420. A non-physical "rotation" that is not prohibited by this method:
  421. >>> cube.reset()
  422. >>> cube.rotate(Permutation([[1, 2]], size=8))
  423. >>> cube.corners
  424. (0, 2, 1, 3, 4, 5, 6, 7)
  425. Polyhedron can be used to follow elements of set that are
  426. identified by letters instead of integers:
  427. >>> shadow = h5 = Polyhedron(list('abcde'))
  428. >>> p = Permutation([3, 0, 1, 2, 4])
  429. >>> h5.rotate(p)
  430. >>> h5.corners
  431. (d, a, b, c, e)
  432. >>> _ == shadow.corners
  433. True
  434. >>> copy = h5.copy()
  435. >>> h5.rotate(p)
  436. >>> h5.corners == copy.corners
  437. False
  438. """
  439. if not isinstance(perm, Perm):
  440. perm = self.pgroup[perm]
  441. # and we know it's valid
  442. else:
  443. if perm.size != self.size:
  444. raise ValueError('Polyhedron and Permutation sizes differ.')
  445. a = perm.array_form
  446. corners = [self.corners[a[i]] for i in range(len(self.corners))]
  447. self._corners = tuple(corners)
  448. def reset(self):
  449. """Return corners to their original positions.
  450. Examples
  451. ========
  452. >>> from sympy.combinatorics.polyhedron import tetrahedron as T
  453. >>> T = T.copy()
  454. >>> T.corners
  455. (0, 1, 2, 3)
  456. >>> T.rotate(0)
  457. >>> T.corners
  458. (0, 2, 3, 1)
  459. >>> T.reset()
  460. >>> T.corners
  461. (0, 1, 2, 3)
  462. """
  463. self._corners = self.args[0]
  464. def _pgroup_calcs():
  465. """Return the permutation groups for each of the polyhedra and the face
  466. definitions: tetrahedron, cube, octahedron, dodecahedron, icosahedron,
  467. tetrahedron_faces, cube_faces, octahedron_faces, dodecahedron_faces,
  468. icosahedron_faces
  469. Explanation
  470. ===========
  471. (This author didn't find and didn't know of a better way to do it though
  472. there likely is such a way.)
  473. Although only 2 permutations are needed for a polyhedron in order to
  474. generate all the possible orientations, a group of permutations is
  475. provided instead. A set of permutations is called a "group" if::
  476. a*b = c (for any pair of permutations in the group, a and b, their
  477. product, c, is in the group)
  478. a*(b*c) = (a*b)*c (for any 3 permutations in the group associativity holds)
  479. there is an identity permutation, I, such that I*a = a*I for all elements
  480. in the group
  481. a*b = I (the inverse of each permutation is also in the group)
  482. None of the polyhedron groups defined follow these definitions of a group.
  483. Instead, they are selected to contain those permutations whose powers
  484. alone will construct all orientations of the polyhedron, i.e. for
  485. permutations ``a``, ``b``, etc... in the group, ``a, a**2, ..., a**o_a``,
  486. ``b, b**2, ..., b**o_b``, etc... (where ``o_i`` is the order of
  487. permutation ``i``) generate all permutations of the polyhedron instead of
  488. mixed products like ``a*b``, ``a*b**2``, etc....
  489. Note that for a polyhedron with n vertices, the valid permutations of the
  490. vertices exclude those that do not maintain its faces. e.g. the
  491. permutation BCDE of a square's four corners, ABCD, is a valid
  492. permutation while CBDE is not (because this would twist the square).
  493. Examples
  494. ========
  495. The is_group checks for: closure, the presence of the Identity permutation,
  496. and the presence of the inverse for each of the elements in the group. This
  497. confirms that none of the polyhedra are true groups:
  498. >>> from sympy.combinatorics.polyhedron import (
  499. ... tetrahedron, cube, octahedron, dodecahedron, icosahedron)
  500. ...
  501. >>> polyhedra = (tetrahedron, cube, octahedron, dodecahedron, icosahedron)
  502. >>> [h.pgroup.is_group for h in polyhedra]
  503. ...
  504. [True, True, True, True, True]
  505. Although tests in polyhedron's test suite check that powers of the
  506. permutations in the groups generate all permutations of the vertices
  507. of the polyhedron, here we also demonstrate the powers of the given
  508. permutations create a complete group for the tetrahedron:
  509. >>> from sympy.combinatorics import Permutation, PermutationGroup
  510. >>> for h in polyhedra[:1]:
  511. ... G = h.pgroup
  512. ... perms = set()
  513. ... for g in G:
  514. ... for e in range(g.order()):
  515. ... p = tuple((g**e).array_form)
  516. ... perms.add(p)
  517. ...
  518. ... perms = [Permutation(p) for p in perms]
  519. ... assert PermutationGroup(perms).is_group
  520. In addition to doing the above, the tests in the suite confirm that the
  521. faces are all present after the application of each permutation.
  522. References
  523. ==========
  524. .. [1] http://dogschool.tripod.com/trianglegroup.html
  525. """
  526. def _pgroup_of_double(polyh, ordered_faces, pgroup):
  527. n = len(ordered_faces[0])
  528. # the vertices of the double which sits inside a give polyhedron
  529. # can be found by tracking the faces of the outer polyhedron.
  530. # A map between face and the vertex of the double is made so that
  531. # after rotation the position of the vertices can be located
  532. fmap = dict(zip(ordered_faces,
  533. range(len(ordered_faces))))
  534. flat_faces = flatten(ordered_faces)
  535. new_pgroup = []
  536. for i, p in enumerate(pgroup):
  537. h = polyh.copy()
  538. h.rotate(p)
  539. c = h.corners
  540. # reorder corners in the order they should appear when
  541. # enumerating the faces
  542. reorder = unflatten([c[j] for j in flat_faces], n)
  543. # make them canonical
  544. reorder = [tuple(map(as_int,
  545. minlex(f, directed=False)))
  546. for f in reorder]
  547. # map face to vertex: the resulting list of vertices are the
  548. # permutation that we seek for the double
  549. new_pgroup.append(Perm([fmap[f] for f in reorder]))
  550. return new_pgroup
  551. tetrahedron_faces = [
  552. (0, 1, 2), (0, 2, 3), (0, 3, 1), # upper 3
  553. (1, 2, 3), # bottom
  554. ]
  555. # cw from top
  556. #
  557. _t_pgroup = [
  558. Perm([[1, 2, 3], [0]]), # cw from top
  559. Perm([[0, 1, 2], [3]]), # cw from front face
  560. Perm([[0, 3, 2], [1]]), # cw from back right face
  561. Perm([[0, 3, 1], [2]]), # cw from back left face
  562. Perm([[0, 1], [2, 3]]), # through front left edge
  563. Perm([[0, 2], [1, 3]]), # through front right edge
  564. Perm([[0, 3], [1, 2]]), # through back edge
  565. ]
  566. tetrahedron = Polyhedron(
  567. range(4),
  568. tetrahedron_faces,
  569. _t_pgroup)
  570. cube_faces = [
  571. (0, 1, 2, 3), # upper
  572. (0, 1, 5, 4), (1, 2, 6, 5), (2, 3, 7, 6), (0, 3, 7, 4), # middle 4
  573. (4, 5, 6, 7), # lower
  574. ]
  575. # U, D, F, B, L, R = up, down, front, back, left, right
  576. _c_pgroup = [Perm(p) for p in
  577. [
  578. [1, 2, 3, 0, 5, 6, 7, 4], # cw from top, U
  579. [4, 0, 3, 7, 5, 1, 2, 6], # cw from F face
  580. [4, 5, 1, 0, 7, 6, 2, 3], # cw from R face
  581. [1, 0, 4, 5, 2, 3, 7, 6], # cw through UF edge
  582. [6, 2, 1, 5, 7, 3, 0, 4], # cw through UR edge
  583. [6, 7, 3, 2, 5, 4, 0, 1], # cw through UB edge
  584. [3, 7, 4, 0, 2, 6, 5, 1], # cw through UL edge
  585. [4, 7, 6, 5, 0, 3, 2, 1], # cw through FL edge
  586. [6, 5, 4, 7, 2, 1, 0, 3], # cw through FR edge
  587. [0, 3, 7, 4, 1, 2, 6, 5], # cw through UFL vertex
  588. [5, 1, 0, 4, 6, 2, 3, 7], # cw through UFR vertex
  589. [5, 6, 2, 1, 4, 7, 3, 0], # cw through UBR vertex
  590. [7, 4, 0, 3, 6, 5, 1, 2], # cw through UBL
  591. ]]
  592. cube = Polyhedron(
  593. range(8),
  594. cube_faces,
  595. _c_pgroup)
  596. octahedron_faces = [
  597. (0, 1, 2), (0, 2, 3), (0, 3, 4), (0, 1, 4), # top 4
  598. (1, 2, 5), (2, 3, 5), (3, 4, 5), (1, 4, 5), # bottom 4
  599. ]
  600. octahedron = Polyhedron(
  601. range(6),
  602. octahedron_faces,
  603. _pgroup_of_double(cube, cube_faces, _c_pgroup))
  604. dodecahedron_faces = [
  605. (0, 1, 2, 3, 4), # top
  606. (0, 1, 6, 10, 5), (1, 2, 7, 11, 6), (2, 3, 8, 12, 7), # upper 5
  607. (3, 4, 9, 13, 8), (0, 4, 9, 14, 5),
  608. (5, 10, 16, 15, 14), (6, 10, 16, 17, 11), (7, 11, 17, 18,
  609. 12), # lower 5
  610. (8, 12, 18, 19, 13), (9, 13, 19, 15, 14),
  611. (15, 16, 17, 18, 19) # bottom
  612. ]
  613. def _string_to_perm(s):
  614. rv = [Perm(range(20))]
  615. p = None
  616. for si in s:
  617. if si not in '01':
  618. count = int(si) - 1
  619. else:
  620. count = 1
  621. if si == '0':
  622. p = _f0
  623. elif si == '1':
  624. p = _f1
  625. rv.extend([p]*count)
  626. return Perm.rmul(*rv)
  627. # top face cw
  628. _f0 = Perm([
  629. 1, 2, 3, 4, 0, 6, 7, 8, 9, 5, 11,
  630. 12, 13, 14, 10, 16, 17, 18, 19, 15])
  631. # front face cw
  632. _f1 = Perm([
  633. 5, 0, 4, 9, 14, 10, 1, 3, 13, 15,
  634. 6, 2, 8, 19, 16, 17, 11, 7, 12, 18])
  635. # the strings below, like 0104 are shorthand for F0*F1*F0**4 and are
  636. # the remaining 4 face rotations, 15 edge permutations, and the
  637. # 10 vertex rotations.
  638. _dodeca_pgroup = [_f0, _f1] + [_string_to_perm(s) for s in '''
  639. 0104 140 014 0410
  640. 010 1403 03104 04103 102
  641. 120 1304 01303 021302 03130
  642. 0412041 041204103 04120410 041204104 041204102
  643. 10 01 1402 0140 04102 0412 1204 1302 0130 03120'''.strip().split()]
  644. dodecahedron = Polyhedron(
  645. range(20),
  646. dodecahedron_faces,
  647. _dodeca_pgroup)
  648. icosahedron_faces = [
  649. (0, 1, 2), (0, 2, 3), (0, 3, 4), (0, 4, 5), (0, 1, 5),
  650. (1, 6, 7), (1, 2, 7), (2, 7, 8), (2, 3, 8), (3, 8, 9),
  651. (3, 4, 9), (4, 9, 10), (4, 5, 10), (5, 6, 10), (1, 5, 6),
  652. (6, 7, 11), (7, 8, 11), (8, 9, 11), (9, 10, 11), (6, 10, 11)]
  653. icosahedron = Polyhedron(
  654. range(12),
  655. icosahedron_faces,
  656. _pgroup_of_double(
  657. dodecahedron, dodecahedron_faces, _dodeca_pgroup))
  658. return (tetrahedron, cube, octahedron, dodecahedron, icosahedron,
  659. tetrahedron_faces, cube_faces, octahedron_faces,
  660. dodecahedron_faces, icosahedron_faces)
  661. # -----------------------------------------------------------------------
  662. # Standard Polyhedron groups
  663. #
  664. # These are generated using _pgroup_calcs() above. However to save
  665. # import time we encode them explicitly here.
  666. # -----------------------------------------------------------------------
  667. tetrahedron = Polyhedron(
  668. Tuple(0, 1, 2, 3),
  669. Tuple(
  670. Tuple(0, 1, 2),
  671. Tuple(0, 2, 3),
  672. Tuple(0, 1, 3),
  673. Tuple(1, 2, 3)),
  674. Tuple(
  675. Perm(1, 2, 3),
  676. Perm(3)(0, 1, 2),
  677. Perm(0, 3, 2),
  678. Perm(0, 3, 1),
  679. Perm(0, 1)(2, 3),
  680. Perm(0, 2)(1, 3),
  681. Perm(0, 3)(1, 2)
  682. ))
  683. cube = Polyhedron(
  684. Tuple(0, 1, 2, 3, 4, 5, 6, 7),
  685. Tuple(
  686. Tuple(0, 1, 2, 3),
  687. Tuple(0, 1, 5, 4),
  688. Tuple(1, 2, 6, 5),
  689. Tuple(2, 3, 7, 6),
  690. Tuple(0, 3, 7, 4),
  691. Tuple(4, 5, 6, 7)),
  692. Tuple(
  693. Perm(0, 1, 2, 3)(4, 5, 6, 7),
  694. Perm(0, 4, 5, 1)(2, 3, 7, 6),
  695. Perm(0, 4, 7, 3)(1, 5, 6, 2),
  696. Perm(0, 1)(2, 4)(3, 5)(6, 7),
  697. Perm(0, 6)(1, 2)(3, 5)(4, 7),
  698. Perm(0, 6)(1, 7)(2, 3)(4, 5),
  699. Perm(0, 3)(1, 7)(2, 4)(5, 6),
  700. Perm(0, 4)(1, 7)(2, 6)(3, 5),
  701. Perm(0, 6)(1, 5)(2, 4)(3, 7),
  702. Perm(1, 3, 4)(2, 7, 5),
  703. Perm(7)(0, 5, 2)(3, 4, 6),
  704. Perm(0, 5, 7)(1, 6, 3),
  705. Perm(0, 7, 2)(1, 4, 6)))
  706. octahedron = Polyhedron(
  707. Tuple(0, 1, 2, 3, 4, 5),
  708. Tuple(
  709. Tuple(0, 1, 2),
  710. Tuple(0, 2, 3),
  711. Tuple(0, 3, 4),
  712. Tuple(0, 1, 4),
  713. Tuple(1, 2, 5),
  714. Tuple(2, 3, 5),
  715. Tuple(3, 4, 5),
  716. Tuple(1, 4, 5)),
  717. Tuple(
  718. Perm(5)(1, 2, 3, 4),
  719. Perm(0, 4, 5, 2),
  720. Perm(0, 1, 5, 3),
  721. Perm(0, 1)(2, 4)(3, 5),
  722. Perm(0, 2)(1, 3)(4, 5),
  723. Perm(0, 3)(1, 5)(2, 4),
  724. Perm(0, 4)(1, 3)(2, 5),
  725. Perm(0, 5)(1, 4)(2, 3),
  726. Perm(0, 5)(1, 2)(3, 4),
  727. Perm(0, 4, 1)(2, 3, 5),
  728. Perm(0, 1, 2)(3, 4, 5),
  729. Perm(0, 2, 3)(1, 5, 4),
  730. Perm(0, 4, 3)(1, 5, 2)))
  731. dodecahedron = Polyhedron(
  732. Tuple(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19),
  733. Tuple(
  734. Tuple(0, 1, 2, 3, 4),
  735. Tuple(0, 1, 6, 10, 5),
  736. Tuple(1, 2, 7, 11, 6),
  737. Tuple(2, 3, 8, 12, 7),
  738. Tuple(3, 4, 9, 13, 8),
  739. Tuple(0, 4, 9, 14, 5),
  740. Tuple(5, 10, 16, 15, 14),
  741. Tuple(6, 10, 16, 17, 11),
  742. Tuple(7, 11, 17, 18, 12),
  743. Tuple(8, 12, 18, 19, 13),
  744. Tuple(9, 13, 19, 15, 14),
  745. Tuple(15, 16, 17, 18, 19)),
  746. Tuple(
  747. Perm(0, 1, 2, 3, 4)(5, 6, 7, 8, 9)(10, 11, 12, 13, 14)(15, 16, 17, 18, 19),
  748. Perm(0, 5, 10, 6, 1)(2, 4, 14, 16, 11)(3, 9, 15, 17, 7)(8, 13, 19, 18, 12),
  749. Perm(0, 10, 17, 12, 3)(1, 6, 11, 7, 2)(4, 5, 16, 18, 8)(9, 14, 15, 19, 13),
  750. Perm(0, 6, 17, 19, 9)(1, 11, 18, 13, 4)(2, 7, 12, 8, 3)(5, 10, 16, 15, 14),
  751. Perm(0, 2, 12, 19, 14)(1, 7, 18, 15, 5)(3, 8, 13, 9, 4)(6, 11, 17, 16, 10),
  752. Perm(0, 4, 9, 14, 5)(1, 3, 13, 15, 10)(2, 8, 19, 16, 6)(7, 12, 18, 17, 11),
  753. Perm(0, 1)(2, 5)(3, 10)(4, 6)(7, 14)(8, 16)(9, 11)(12, 15)(13, 17)(18, 19),
  754. Perm(0, 7)(1, 2)(3, 6)(4, 11)(5, 12)(8, 10)(9, 17)(13, 16)(14, 18)(15, 19),
  755. Perm(0, 12)(1, 8)(2, 3)(4, 7)(5, 18)(6, 13)(9, 11)(10, 19)(14, 17)(15, 16),
  756. Perm(0, 8)(1, 13)(2, 9)(3, 4)(5, 12)(6, 19)(7, 14)(10, 18)(11, 15)(16, 17),
  757. Perm(0, 4)(1, 9)(2, 14)(3, 5)(6, 13)(7, 15)(8, 10)(11, 19)(12, 16)(17, 18),
  758. Perm(0, 5)(1, 14)(2, 15)(3, 16)(4, 10)(6, 9)(7, 19)(8, 17)(11, 13)(12, 18),
  759. Perm(0, 11)(1, 6)(2, 10)(3, 16)(4, 17)(5, 7)(8, 15)(9, 18)(12, 14)(13, 19),
  760. Perm(0, 18)(1, 12)(2, 7)(3, 11)(4, 17)(5, 19)(6, 8)(9, 16)(10, 13)(14, 15),
  761. Perm(0, 18)(1, 19)(2, 13)(3, 8)(4, 12)(5, 17)(6, 15)(7, 9)(10, 16)(11, 14),
  762. Perm(0, 13)(1, 19)(2, 15)(3, 14)(4, 9)(5, 8)(6, 18)(7, 16)(10, 12)(11, 17),
  763. Perm(0, 16)(1, 15)(2, 19)(3, 18)(4, 17)(5, 10)(6, 14)(7, 13)(8, 12)(9, 11),
  764. Perm(0, 18)(1, 17)(2, 16)(3, 15)(4, 19)(5, 12)(6, 11)(7, 10)(8, 14)(9, 13),
  765. Perm(0, 15)(1, 19)(2, 18)(3, 17)(4, 16)(5, 14)(6, 13)(7, 12)(8, 11)(9, 10),
  766. Perm(0, 17)(1, 16)(2, 15)(3, 19)(4, 18)(5, 11)(6, 10)(7, 14)(8, 13)(9, 12),
  767. Perm(0, 19)(1, 18)(2, 17)(3, 16)(4, 15)(5, 13)(6, 12)(7, 11)(8, 10)(9, 14),
  768. Perm(1, 4, 5)(2, 9, 10)(3, 14, 6)(7, 13, 16)(8, 15, 11)(12, 19, 17),
  769. Perm(19)(0, 6, 2)(3, 5, 11)(4, 10, 7)(8, 14, 17)(9, 16, 12)(13, 15, 18),
  770. Perm(0, 11, 8)(1, 7, 3)(4, 6, 12)(5, 17, 13)(9, 10, 18)(14, 16, 19),
  771. Perm(0, 7, 13)(1, 12, 9)(2, 8, 4)(5, 11, 19)(6, 18, 14)(10, 17, 15),
  772. Perm(0, 3, 9)(1, 8, 14)(2, 13, 5)(6, 12, 15)(7, 19, 10)(11, 18, 16),
  773. Perm(0, 14, 10)(1, 9, 16)(2, 13, 17)(3, 19, 11)(4, 15, 6)(7, 8, 18),
  774. Perm(0, 16, 7)(1, 10, 11)(2, 5, 17)(3, 14, 18)(4, 15, 12)(8, 9, 19),
  775. Perm(0, 16, 13)(1, 17, 8)(2, 11, 12)(3, 6, 18)(4, 10, 19)(5, 15, 9),
  776. Perm(0, 11, 15)(1, 17, 14)(2, 18, 9)(3, 12, 13)(4, 7, 19)(5, 6, 16),
  777. Perm(0, 8, 15)(1, 12, 16)(2, 18, 10)(3, 19, 5)(4, 13, 14)(6, 7, 17)))
  778. icosahedron = Polyhedron(
  779. Tuple(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11),
  780. Tuple(
  781. Tuple(0, 1, 2),
  782. Tuple(0, 2, 3),
  783. Tuple(0, 3, 4),
  784. Tuple(0, 4, 5),
  785. Tuple(0, 1, 5),
  786. Tuple(1, 6, 7),
  787. Tuple(1, 2, 7),
  788. Tuple(2, 7, 8),
  789. Tuple(2, 3, 8),
  790. Tuple(3, 8, 9),
  791. Tuple(3, 4, 9),
  792. Tuple(4, 9, 10),
  793. Tuple(4, 5, 10),
  794. Tuple(5, 6, 10),
  795. Tuple(1, 5, 6),
  796. Tuple(6, 7, 11),
  797. Tuple(7, 8, 11),
  798. Tuple(8, 9, 11),
  799. Tuple(9, 10, 11),
  800. Tuple(6, 10, 11)),
  801. Tuple(
  802. Perm(11)(1, 2, 3, 4, 5)(6, 7, 8, 9, 10),
  803. Perm(0, 5, 6, 7, 2)(3, 4, 10, 11, 8),
  804. Perm(0, 1, 7, 8, 3)(4, 5, 6, 11, 9),
  805. Perm(0, 2, 8, 9, 4)(1, 7, 11, 10, 5),
  806. Perm(0, 3, 9, 10, 5)(1, 2, 8, 11, 6),
  807. Perm(0, 4, 10, 6, 1)(2, 3, 9, 11, 7),
  808. Perm(0, 1)(2, 5)(3, 6)(4, 7)(8, 10)(9, 11),
  809. Perm(0, 2)(1, 3)(4, 7)(5, 8)(6, 9)(10, 11),
  810. Perm(0, 3)(1, 9)(2, 4)(5, 8)(6, 11)(7, 10),
  811. Perm(0, 4)(1, 9)(2, 10)(3, 5)(6, 8)(7, 11),
  812. Perm(0, 5)(1, 4)(2, 10)(3, 6)(7, 9)(8, 11),
  813. Perm(0, 6)(1, 5)(2, 10)(3, 11)(4, 7)(8, 9),
  814. Perm(0, 7)(1, 2)(3, 6)(4, 11)(5, 8)(9, 10),
  815. Perm(0, 8)(1, 9)(2, 3)(4, 7)(5, 11)(6, 10),
  816. Perm(0, 9)(1, 11)(2, 10)(3, 4)(5, 8)(6, 7),
  817. Perm(0, 10)(1, 9)(2, 11)(3, 6)(4, 5)(7, 8),
  818. Perm(0, 11)(1, 6)(2, 10)(3, 9)(4, 8)(5, 7),
  819. Perm(0, 11)(1, 8)(2, 7)(3, 6)(4, 10)(5, 9),
  820. Perm(0, 11)(1, 10)(2, 9)(3, 8)(4, 7)(5, 6),
  821. Perm(0, 11)(1, 7)(2, 6)(3, 10)(4, 9)(5, 8),
  822. Perm(0, 11)(1, 9)(2, 8)(3, 7)(4, 6)(5, 10),
  823. Perm(0, 5, 1)(2, 4, 6)(3, 10, 7)(8, 9, 11),
  824. Perm(0, 1, 2)(3, 5, 7)(4, 6, 8)(9, 10, 11),
  825. Perm(0, 2, 3)(1, 8, 4)(5, 7, 9)(6, 11, 10),
  826. Perm(0, 3, 4)(1, 8, 10)(2, 9, 5)(6, 7, 11),
  827. Perm(0, 4, 5)(1, 3, 10)(2, 9, 6)(7, 8, 11),
  828. Perm(0, 10, 7)(1, 5, 6)(2, 4, 11)(3, 9, 8),
  829. Perm(0, 6, 8)(1, 7, 2)(3, 5, 11)(4, 10, 9),
  830. Perm(0, 7, 9)(1, 11, 4)(2, 8, 3)(5, 6, 10),
  831. Perm(0, 8, 10)(1, 7, 6)(2, 11, 5)(3, 9, 4),
  832. Perm(0, 9, 6)(1, 3, 11)(2, 8, 7)(4, 10, 5)))
  833. tetrahedron_faces = list(tuple(arg) for arg in tetrahedron.faces)
  834. cube_faces = list(tuple(arg) for arg in cube.faces)
  835. octahedron_faces = list(tuple(arg) for arg in octahedron.faces)
  836. dodecahedron_faces = list(tuple(arg) for arg in dodecahedron.faces)
  837. icosahedron_faces = list(tuple(arg) for arg in icosahedron.faces)