passagemath-polyhedra 10.6.31rc3__cp314-cp314-musllinux_1_2_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of passagemath-polyhedra might be problematic. Click here for more details.

Files changed (208) hide show
  1. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA +367 -0
  2. passagemath_polyhedra-10.6.31rc3.dist-info/METADATA.bak +369 -0
  3. passagemath_polyhedra-10.6.31rc3.dist-info/RECORD +208 -0
  4. passagemath_polyhedra-10.6.31rc3.dist-info/WHEEL +5 -0
  5. passagemath_polyhedra-10.6.31rc3.dist-info/top_level.txt +2 -0
  6. passagemath_polyhedra.libs/libgcc_s-0cd532bd.so.1 +0 -0
  7. passagemath_polyhedra.libs/libgmp-0e7fc84e.so.10.5.0 +0 -0
  8. passagemath_polyhedra.libs/libgomp-8949ffbe.so.1.0.0 +0 -0
  9. passagemath_polyhedra.libs/libstdc++-5d72f927.so.6.0.33 +0 -0
  10. sage/all__sagemath_polyhedra.py +50 -0
  11. sage/game_theory/all.py +8 -0
  12. sage/game_theory/catalog.py +6 -0
  13. sage/game_theory/catalog_normal_form_games.py +923 -0
  14. sage/game_theory/cooperative_game.py +844 -0
  15. sage/game_theory/matching_game.py +1181 -0
  16. sage/game_theory/normal_form_game.py +2697 -0
  17. sage/game_theory/parser.py +275 -0
  18. sage/geometry/all__sagemath_polyhedra.py +22 -0
  19. sage/geometry/cone.py +6940 -0
  20. sage/geometry/cone_catalog.py +847 -0
  21. sage/geometry/cone_critical_angles.py +1027 -0
  22. sage/geometry/convex_set.py +1119 -0
  23. sage/geometry/fan.py +3743 -0
  24. sage/geometry/fan_isomorphism.py +389 -0
  25. sage/geometry/fan_morphism.py +1884 -0
  26. sage/geometry/hasse_diagram.py +202 -0
  27. sage/geometry/hyperplane_arrangement/affine_subspace.py +390 -0
  28. sage/geometry/hyperplane_arrangement/all.py +1 -0
  29. sage/geometry/hyperplane_arrangement/arrangement.py +3895 -0
  30. sage/geometry/hyperplane_arrangement/check_freeness.py +145 -0
  31. sage/geometry/hyperplane_arrangement/hyperplane.py +773 -0
  32. sage/geometry/hyperplane_arrangement/library.py +825 -0
  33. sage/geometry/hyperplane_arrangement/ordered_arrangement.py +642 -0
  34. sage/geometry/hyperplane_arrangement/plot.py +520 -0
  35. sage/geometry/integral_points.py +35 -0
  36. sage/geometry/integral_points_generic_dense.cpython-314-x86_64-linux-musl.so +0 -0
  37. sage/geometry/integral_points_generic_dense.pyx +7 -0
  38. sage/geometry/lattice_polytope.py +5894 -0
  39. sage/geometry/linear_expression.py +773 -0
  40. sage/geometry/newton_polygon.py +767 -0
  41. sage/geometry/point_collection.cpython-314-x86_64-linux-musl.so +0 -0
  42. sage/geometry/point_collection.pyx +1008 -0
  43. sage/geometry/polyhedral_complex.py +2616 -0
  44. sage/geometry/polyhedron/all.py +8 -0
  45. sage/geometry/polyhedron/backend_cdd.py +460 -0
  46. sage/geometry/polyhedron/backend_cdd_rdf.py +231 -0
  47. sage/geometry/polyhedron/backend_field.py +347 -0
  48. sage/geometry/polyhedron/backend_normaliz.py +2503 -0
  49. sage/geometry/polyhedron/backend_number_field.py +168 -0
  50. sage/geometry/polyhedron/backend_polymake.py +765 -0
  51. sage/geometry/polyhedron/backend_ppl.py +582 -0
  52. sage/geometry/polyhedron/base.py +1206 -0
  53. sage/geometry/polyhedron/base0.py +1444 -0
  54. sage/geometry/polyhedron/base1.py +886 -0
  55. sage/geometry/polyhedron/base2.py +812 -0
  56. sage/geometry/polyhedron/base3.py +1845 -0
  57. sage/geometry/polyhedron/base4.py +1262 -0
  58. sage/geometry/polyhedron/base5.py +2700 -0
  59. sage/geometry/polyhedron/base6.py +1741 -0
  60. sage/geometry/polyhedron/base7.py +997 -0
  61. sage/geometry/polyhedron/base_QQ.py +1258 -0
  62. sage/geometry/polyhedron/base_RDF.py +98 -0
  63. sage/geometry/polyhedron/base_ZZ.py +934 -0
  64. sage/geometry/polyhedron/base_mutable.py +215 -0
  65. sage/geometry/polyhedron/base_number_field.py +122 -0
  66. sage/geometry/polyhedron/cdd_file_format.py +155 -0
  67. sage/geometry/polyhedron/combinatorial_polyhedron/all.py +1 -0
  68. sage/geometry/polyhedron/combinatorial_polyhedron/base.cpython-314-x86_64-linux-musl.so +0 -0
  69. sage/geometry/polyhedron/combinatorial_polyhedron/base.pxd +76 -0
  70. sage/geometry/polyhedron/combinatorial_polyhedron/base.pyx +3859 -0
  71. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.cpython-314-x86_64-linux-musl.so +0 -0
  72. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pxd +39 -0
  73. sage/geometry/polyhedron/combinatorial_polyhedron/combinatorial_face.pyx +1038 -0
  74. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.cpython-314-x86_64-linux-musl.so +0 -0
  75. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pxd +9 -0
  76. sage/geometry/polyhedron/combinatorial_polyhedron/conversions.pyx +501 -0
  77. sage/geometry/polyhedron/combinatorial_polyhedron/face_data_structure.pxd +207 -0
  78. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.cpython-314-x86_64-linux-musl.so +0 -0
  79. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pxd +102 -0
  80. sage/geometry/polyhedron/combinatorial_polyhedron/face_iterator.pyx +2274 -0
  81. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.cpython-314-x86_64-linux-musl.so +0 -0
  82. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pxd +370 -0
  83. sage/geometry/polyhedron/combinatorial_polyhedron/face_list_data_structure.pyx +84 -0
  84. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.cpython-314-x86_64-linux-musl.so +0 -0
  85. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pxd +31 -0
  86. sage/geometry/polyhedron/combinatorial_polyhedron/list_of_faces.pyx +587 -0
  87. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.cpython-314-x86_64-linux-musl.so +0 -0
  88. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pxd +52 -0
  89. sage/geometry/polyhedron/combinatorial_polyhedron/polyhedron_face_lattice.pyx +560 -0
  90. sage/geometry/polyhedron/constructor.py +773 -0
  91. sage/geometry/polyhedron/double_description.py +753 -0
  92. sage/geometry/polyhedron/double_description_inhomogeneous.py +564 -0
  93. sage/geometry/polyhedron/face.py +1060 -0
  94. sage/geometry/polyhedron/generating_function.py +1810 -0
  95. sage/geometry/polyhedron/lattice_euclidean_group_element.py +178 -0
  96. sage/geometry/polyhedron/library.py +3502 -0
  97. sage/geometry/polyhedron/misc.py +121 -0
  98. sage/geometry/polyhedron/modules/all.py +1 -0
  99. sage/geometry/polyhedron/modules/formal_polyhedra_module.py +155 -0
  100. sage/geometry/polyhedron/palp_database.py +447 -0
  101. sage/geometry/polyhedron/parent.py +1279 -0
  102. sage/geometry/polyhedron/plot.py +1986 -0
  103. sage/geometry/polyhedron/ppl_lattice_polygon.py +556 -0
  104. sage/geometry/polyhedron/ppl_lattice_polytope.py +1257 -0
  105. sage/geometry/polyhedron/representation.py +1723 -0
  106. sage/geometry/pseudolines.py +515 -0
  107. sage/geometry/relative_interior.py +445 -0
  108. sage/geometry/toric_plotter.py +1103 -0
  109. sage/geometry/triangulation/all.py +2 -0
  110. sage/geometry/triangulation/base.cpython-314-x86_64-linux-musl.so +0 -0
  111. sage/geometry/triangulation/base.pyx +963 -0
  112. sage/geometry/triangulation/data.h +147 -0
  113. sage/geometry/triangulation/data.pxd +4 -0
  114. sage/geometry/triangulation/element.py +914 -0
  115. sage/geometry/triangulation/functions.h +10 -0
  116. sage/geometry/triangulation/functions.pxd +4 -0
  117. sage/geometry/triangulation/point_configuration.py +2256 -0
  118. sage/geometry/triangulation/triangulations.h +49 -0
  119. sage/geometry/triangulation/triangulations.pxd +7 -0
  120. sage/geometry/voronoi_diagram.py +319 -0
  121. sage/interfaces/all__sagemath_polyhedra.py +1 -0
  122. sage/interfaces/polymake.py +2028 -0
  123. sage/numerical/all.py +13 -0
  124. sage/numerical/all__sagemath_polyhedra.py +11 -0
  125. sage/numerical/backends/all.py +1 -0
  126. sage/numerical/backends/all__sagemath_polyhedra.py +1 -0
  127. sage/numerical/backends/cvxopt_backend.cpython-314-x86_64-linux-musl.so +0 -0
  128. sage/numerical/backends/cvxopt_backend.pyx +1006 -0
  129. sage/numerical/backends/cvxopt_backend_test.py +19 -0
  130. sage/numerical/backends/cvxopt_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
  131. sage/numerical/backends/cvxopt_sdp_backend.pyx +382 -0
  132. sage/numerical/backends/cvxpy_backend.cpython-314-x86_64-linux-musl.so +0 -0
  133. sage/numerical/backends/cvxpy_backend.pxd +41 -0
  134. sage/numerical/backends/cvxpy_backend.pyx +934 -0
  135. sage/numerical/backends/cvxpy_backend_test.py +13 -0
  136. sage/numerical/backends/generic_backend_test.py +24 -0
  137. sage/numerical/backends/interactivelp_backend.cpython-314-x86_64-linux-musl.so +0 -0
  138. sage/numerical/backends/interactivelp_backend.pxd +36 -0
  139. sage/numerical/backends/interactivelp_backend.pyx +1231 -0
  140. sage/numerical/backends/interactivelp_backend_test.py +12 -0
  141. sage/numerical/backends/logging_backend.py +391 -0
  142. sage/numerical/backends/matrix_sdp_backend.cpython-314-x86_64-linux-musl.so +0 -0
  143. sage/numerical/backends/matrix_sdp_backend.pxd +15 -0
  144. sage/numerical/backends/matrix_sdp_backend.pyx +478 -0
  145. sage/numerical/backends/ppl_backend.cpython-314-x86_64-linux-musl.so +0 -0
  146. sage/numerical/backends/ppl_backend.pyx +1126 -0
  147. sage/numerical/backends/ppl_backend_test.py +13 -0
  148. sage/numerical/backends/scip_backend.cpython-314-x86_64-linux-musl.so +0 -0
  149. sage/numerical/backends/scip_backend.pxd +22 -0
  150. sage/numerical/backends/scip_backend.pyx +1289 -0
  151. sage/numerical/backends/scip_backend_test.py +13 -0
  152. sage/numerical/interactive_simplex_method.py +5338 -0
  153. sage/numerical/knapsack.py +665 -0
  154. sage/numerical/linear_functions.cpython-314-x86_64-linux-musl.so +0 -0
  155. sage/numerical/linear_functions.pxd +31 -0
  156. sage/numerical/linear_functions.pyx +1648 -0
  157. sage/numerical/linear_tensor.py +470 -0
  158. sage/numerical/linear_tensor_constraints.py +448 -0
  159. sage/numerical/linear_tensor_element.cpython-314-x86_64-linux-musl.so +0 -0
  160. sage/numerical/linear_tensor_element.pxd +6 -0
  161. sage/numerical/linear_tensor_element.pyx +459 -0
  162. sage/numerical/mip.cpython-314-x86_64-linux-musl.so +0 -0
  163. sage/numerical/mip.pxd +40 -0
  164. sage/numerical/mip.pyx +3667 -0
  165. sage/numerical/sdp.cpython-314-x86_64-linux-musl.so +0 -0
  166. sage/numerical/sdp.pxd +39 -0
  167. sage/numerical/sdp.pyx +1433 -0
  168. sage/rings/all__sagemath_polyhedra.py +3 -0
  169. sage/rings/polynomial/all__sagemath_polyhedra.py +10 -0
  170. sage/rings/polynomial/omega.py +982 -0
  171. sage/schemes/all__sagemath_polyhedra.py +2 -0
  172. sage/schemes/toric/all.py +10 -0
  173. sage/schemes/toric/chow_group.py +1248 -0
  174. sage/schemes/toric/divisor.py +2082 -0
  175. sage/schemes/toric/divisor_class.cpython-314-x86_64-linux-musl.so +0 -0
  176. sage/schemes/toric/divisor_class.pyx +322 -0
  177. sage/schemes/toric/fano_variety.py +1606 -0
  178. sage/schemes/toric/homset.py +650 -0
  179. sage/schemes/toric/ideal.py +451 -0
  180. sage/schemes/toric/library.py +1322 -0
  181. sage/schemes/toric/morphism.py +1958 -0
  182. sage/schemes/toric/points.py +1032 -0
  183. sage/schemes/toric/sheaf/all.py +1 -0
  184. sage/schemes/toric/sheaf/constructor.py +302 -0
  185. sage/schemes/toric/sheaf/klyachko.py +921 -0
  186. sage/schemes/toric/toric_subscheme.py +905 -0
  187. sage/schemes/toric/variety.py +3460 -0
  188. sage/schemes/toric/weierstrass.py +1078 -0
  189. sage/schemes/toric/weierstrass_covering.py +457 -0
  190. sage/schemes/toric/weierstrass_higher.py +288 -0
  191. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.info +10 -0
  192. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v03 +0 -0
  193. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v04 +0 -0
  194. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v05 +1 -0
  195. sage_wheels/share/reflexive_polytopes/Full2d/zzdb.v06 +1 -0
  196. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.info +22 -0
  197. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v04 +0 -0
  198. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v05 +0 -0
  199. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v06 +0 -0
  200. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v07 +0 -0
  201. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v08 +0 -0
  202. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v09 +0 -0
  203. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v10 +0 -0
  204. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v11 +1 -0
  205. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v12 +1 -0
  206. sage_wheels/share/reflexive_polytopes/Full3d/zzdb.v13 +1 -0
  207. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_2d +80 -0
  208. sage_wheels/share/reflexive_polytopes/reflexive_polytopes_3d +37977 -0
@@ -0,0 +1,3502 @@
1
+ # sage_setup: distribution = sagemath-polyhedra
2
+ r"""
3
+ Library of commonly used, famous, or interesting polytopes
4
+
5
+ This module gathers several constructors of polytopes that can be reached
6
+ through ``polytopes.<tab>``. For example, here is the hypercube in dimension 5::
7
+
8
+ sage: polytopes.hypercube(5)
9
+ A 5-dimensional polyhedron in ZZ^5 defined as the convex hull of 32 vertices
10
+
11
+ The following constructions are available
12
+
13
+ .. csv-table::
14
+ :class: contentstable
15
+ :widths: 30
16
+ :delim: |
17
+
18
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.Birkhoff_polytope`
19
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.associahedron`
20
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.bitruncated_six_hundred_cell`
21
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.buckyball`
22
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.cantellated_one_hundred_twenty_cell`
23
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.cantellated_six_hundred_cell`
24
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.cantitruncated_one_hundred_twenty_cell`
25
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.cantitruncated_six_hundred_cell`
26
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.cross_polytope`
27
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.cube`
28
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.cuboctahedron`
29
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.cyclic_polytope`
30
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.dodecahedron`
31
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.flow_polytope`
32
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.Gosset_3_21`
33
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.grand_antiprism`
34
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.great_rhombicuboctahedron`
35
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.hypercube`
36
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.hypersimplex`
37
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.icosahedron`
38
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.icosidodecahedron`
39
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.Kirkman_icosahedron`
40
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.octahedron`
41
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.omnitruncated_one_hundred_twenty_cell`
42
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.omnitruncated_six_hundred_cell`
43
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.one_hundred_twenty_cell`
44
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.parallelotope`
45
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.pentakis_dodecahedron`
46
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.permutahedron`
47
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.generalized_permutahedron`
48
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.rectified_one_hundred_twenty_cell`
49
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.rectified_six_hundred_cell`
50
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.regular_polygon`
51
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.rhombic_dodecahedron`
52
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.rhombicosidodecahedron`
53
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.runcinated_one_hundred_twenty_cell`
54
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.runcitruncated_one_hundred_twenty_cell`
55
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.runcitruncated_six_hundred_cell`
56
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.simplex`
57
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.six_hundred_cell`
58
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.small_rhombicuboctahedron`
59
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.snub_cube`
60
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.snub_dodecahedron`
61
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.tetrahedron`
62
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_cube`
63
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_dodecahedron`
64
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_icosidodecahedron`
65
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_tetrahedron`
66
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_octahedron`
67
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_one_hundred_twenty_cell`
68
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.truncated_six_hundred_cell`
69
+ :meth:`~sage.geometry.polyhedron.library.Polytopes.twenty_four_cell`
70
+ """
71
+ ########################################################################
72
+ # Copyright (C) 2008 Marshall Hampton <hamptonio@gmail.com>
73
+ # 2011 Volker Braun <vbraun.name@gmail.com>
74
+ # 2015 Vincent Delecroix <20100.delecroix@gmail.com>
75
+ # 2019 Jean-Philippe Labbé <labbe@math.fu-berlin.de>
76
+ #
77
+ # Distributed under the terms of the GNU General Public License (GPL)
78
+ #
79
+ # https://www.gnu.org/licenses/
80
+ ########################################################################
81
+
82
+ import itertools
83
+
84
+ from sage.rings.integer_ring import ZZ
85
+ from sage.misc.lazy_import import lazy_import
86
+ from sage.rings.rational_field import QQ
87
+ lazy_import('sage.combinat.permutation', 'Permutations')
88
+ lazy_import('sage.groups.perm_gps.permgroup_named', 'AlternatingGroup')
89
+ from .constructor import Polyhedron
90
+ from .parent import Polyhedra
91
+ lazy_import('sage.graphs.digraph', 'DiGraph')
92
+ lazy_import('sage.graphs.graph', 'Graph')
93
+ lazy_import('sage.combinat.root_system.associahedron', 'Associahedron')
94
+
95
+
96
+ def zero_sum_projection(d, base_ring=None):
97
+ r"""
98
+ Return a matrix corresponding to the projection on the orthogonal of
99
+ `(1,1,\ldots,1)` in dimension `d`.
100
+
101
+ The projection maps the orthonormal basis `(1,-1,0,\ldots,0) / \sqrt(2)`,
102
+ `(1,1,-1,0,\ldots,0) / \sqrt(3)`, \ldots, `(1,1,\ldots,1,-1) / \sqrt(d)` to
103
+ the canonical basis in `\RR^{d-1}`.
104
+
105
+ OUTPUT:
106
+
107
+ A matrix of dimensions `(d-1)\times d` defined over ``base_ring`` (default:
108
+ :class:`RDF <sage.rings.real_double.RealDoubleField_class>`).
109
+
110
+ EXAMPLES::
111
+
112
+ sage: from sage.geometry.polyhedron.library import zero_sum_projection
113
+ sage: zero_sum_projection(2)
114
+ [ 0.7071067811865475 -0.7071067811865475]
115
+ sage: zero_sum_projection(3)
116
+ [ 0.7071067811865475 -0.7071067811865475 0.0]
117
+ [ 0.4082482904638631 0.4082482904638631 -0.8164965809277261]
118
+
119
+ Exact computation in :class:`AA <sage.rings.qqbar.AlgebraicRealField>`::
120
+
121
+ sage: zero_sum_projection(3, base_ring=AA) # needs sage.rings.number_field
122
+ [ 0.7071067811865475? -0.7071067811865475? 0]
123
+ [ 0.4082482904638630? 0.4082482904638630? -0.8164965809277260?]
124
+ """
125
+ from sage.matrix.constructor import matrix
126
+ from sage.modules.free_module_element import vector
127
+ if base_ring is None:
128
+ from sage.rings.real_double import RDF as base_ring
129
+ basis = [vector(base_ring, [1]*i + [-i] + [0]*(d-i-1)) for i in range(1, d)]
130
+ return matrix(base_ring, [v / v.norm() for v in basis])
131
+
132
+
133
+ def project_points(*points, **kwds):
134
+ """
135
+ Projects a set of points into a vector space of dimension one less.
136
+
137
+ INPUT:
138
+
139
+ - ``points``... -- the points to project
140
+
141
+ - ``base_ring`` -- (defaults to ``RDF`` if keyword is ``None`` or not
142
+ provided in ``kwds``) the base ring to use
143
+
144
+ The projection is isometric to the orthogonal projection on the hyperplane
145
+ made of zero sum vector. Hence, if the set of points have all equal sums,
146
+ then their projection is isometric (as a set of points).
147
+
148
+ The projection used is the matrix given by :func:`zero_sum_projection`.
149
+
150
+ EXAMPLES::
151
+
152
+ sage: from sage.geometry.polyhedron.library import project_points
153
+ sage: project_points([2,-1,3,2]) # abs tol 1e-15
154
+ [(2.1213203435596424, -2.041241452319315, -0.577350269189626)]
155
+ sage: project_points([1,2,3],[3,3,5]) # abs tol 1e-15
156
+ [(-0.7071067811865475, -1.2247448713915892), (0.0, -1.6329931618554523)]
157
+
158
+ These projections are compatible with the restriction. More precisely,
159
+ given a vector `v`, the projection of `v` restricted to the first `i`
160
+ coordinates will be equal to the projection of the first `i+1` coordinates
161
+ of `v`::
162
+
163
+ sage: project_points([1,2]) # abs tol 1e-15
164
+ [(-0.7071067811865475)]
165
+ sage: project_points([1,2,3]) # abs tol 1e-15
166
+ [(-0.7071067811865475, -1.2247448713915892)]
167
+ sage: project_points([1,2,3,4]) # abs tol 1e-15
168
+ [(-0.7071067811865475, -1.2247448713915892, -1.7320508075688776)]
169
+ sage: project_points([1,2,3,4,0]) # abs tol 1e-15
170
+ [(-0.7071067811865475, -1.2247448713915892, -1.7320508075688776, 2.23606797749979)]
171
+
172
+ Check that it is (almost) an isometry::
173
+
174
+ sage: V = list(map(vector, IntegerVectors(n=5, length=3)))
175
+ sage: P = project_points(*V)
176
+ sage: for i in range(21): # needs sage.combinat sage.symbolic
177
+ ....: for j in range(21):
178
+ ....: assert abs((V[i]-V[j]).norm() - (P[i]-P[j]).norm()) < 0.00001
179
+
180
+ Example with exact computation::
181
+
182
+ sage: V = [ vector(v) for v in IntegerVectors(n=4, length=2) ]
183
+ sage: P = project_points(*V, base_ring=AA) # needs sage.combinat sage.rings.number_field
184
+ sage: for i in range(len(V)): # needs sage.combinat sage.rings.number_field sage.symbolic
185
+ ....: for j in range(len(V)):
186
+ ....: assert (V[i]-V[j]).norm() == (P[i]-P[j]).norm()
187
+ """
188
+ if not points:
189
+ return []
190
+ base_ring = kwds.pop('base_ring', None)
191
+ if base_ring is None:
192
+ from sage.rings.real_double import RDF as base_ring
193
+ from sage.modules.free_module_element import vector
194
+ vecs = [vector(base_ring, p) for p in points]
195
+ m = zero_sum_projection(len(vecs[0]), base_ring=base_ring)
196
+ return [m * v for v in vecs]
197
+
198
+
199
+ def gale_transform_to_polytope(vectors, base_ring=None, backend=None):
200
+ r"""
201
+ Return the polytope associated to the list of vectors forming a Gale transform.
202
+
203
+ This function is the inverse of
204
+ :meth:`~sage.geometry.polyhedron.base.Polyhedron_base.gale_transform`
205
+ up to projective transformation.
206
+
207
+ INPUT:
208
+
209
+ - ``vectors`` -- the vectors of the Gale transform
210
+
211
+ - ``base_ring`` -- string (default: ``None``);
212
+ the base ring to be used for the construction
213
+
214
+ - ``backend`` -- string (default: ``None``);
215
+ the backend to use to create the polytope
216
+
217
+ .. NOTE::
218
+
219
+ The order of the input vectors will not be preserved.
220
+
221
+ If the center of the (input) vectors is the origin,
222
+ the function is much faster and might give a nicer representation
223
+ of the polytope.
224
+
225
+ If this is not the case, the vectors will be scaled
226
+ (each by a positive scalar) accordingly to obtain the polytope.
227
+
228
+ .. SEEALSO::
229
+
230
+ :func`~sage.geometry.polyhedron.library.gale_transform_to_primal`.
231
+
232
+ EXAMPLES::
233
+
234
+ sage: from sage.geometry.polyhedron.library import gale_transform_to_polytope
235
+ sage: points = polytopes.octahedron().gale_transform()
236
+ sage: points
237
+ ((0, -1), (-1, 0), (1, 1), (1, 1), (-1, 0), (0, -1))
238
+ sage: P = gale_transform_to_polytope(points); P
239
+ A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 6 vertices
240
+ sage: P.vertices()
241
+ (A vertex at (-1, 0, 0),
242
+ A vertex at (0, -1, 0),
243
+ A vertex at (0, 0, -1),
244
+ A vertex at (0, 0, 1),
245
+ A vertex at (0, 1, 0),
246
+ A vertex at (1, 0, 0))
247
+
248
+ One can specify the base ring::
249
+
250
+ sage: gale_transform_to_polytope( # needs sage.libs.pari
251
+ ....: [(1,1), (-1,-1), (1,0),
252
+ ....: (-1,0), (1,-1), (-2,1)]).vertices()
253
+ (A vertex at (-25, 0, 0),
254
+ A vertex at (-15, 50, -60),
255
+ A vertex at (0, -25, 0),
256
+ A vertex at (0, 0, -25),
257
+ A vertex at (16, -35, 54),
258
+ A vertex at (24, 10, 31))
259
+ sage: gale_transform_to_polytope( # needs cddexec
260
+ ....: [(1,1), (-1,-1), (1,0),
261
+ ....: (-1,0), (1,-1), (-2,1)],
262
+ ....: base_ring=RDF).vertices()
263
+ (A vertex at (-0.64, 1.4, -2.16),
264
+ A vertex at (-0.96, -0.4, -1.24),
265
+ A vertex at (0.6, -2.0, 2.4),
266
+ A vertex at (1.0, 0.0, 0.0),
267
+ A vertex at (0.0, 1.0, 0.0),
268
+ A vertex at (0.0, 0.0, 1.0))
269
+
270
+ One can also specify the backend::
271
+
272
+ sage: gale_transform_to_polytope(
273
+ ....: [(1,1), (-1,-1), (1,0),
274
+ ....: (-1,0), (1,-1), (-2,1)],
275
+ ....: backend='field').backend()
276
+ 'field'
277
+ sage: gale_transform_to_polytope( # needs cddexec
278
+ ....: [(1,1), (-1,-1), (1,0),
279
+ ....: (-1,0), (1,-1), (-2,1)],
280
+ ....: backend='cdd', base_ring=RDF).backend()
281
+ 'cdd'
282
+
283
+ A gale transform corresponds to a polytope if and only if
284
+ every oriented (linear) hyperplane
285
+ has at least two vectors on each side.
286
+ See Theorem 6.19 of [Zie2007]_.
287
+ If this is not the case, one of two errors is raised.
288
+
289
+ If there is such a hyperplane with no vector on one side,
290
+ the vectors are not totally cyclic::
291
+
292
+ sage: gale_transform_to_polytope([(0,1), (1,1), (1,0), (-1,0)])
293
+ Traceback (most recent call last):
294
+ ...
295
+ ValueError: input vectors not totally cyclic
296
+
297
+ If every hyperplane has at least one vector on each side, then the gale
298
+ transform corresponds to a point configuration.
299
+ It corresponds to a polytope if and only if this point configuration is
300
+ convex and if and only if every hyperplane contains at least two vectors of
301
+ the gale transform on each side.
302
+
303
+ If this is not the case, an error is raised::
304
+
305
+ sage: gale_transform_to_polytope([(0,1), (1,1), (1,0), (-1,-1)])
306
+ Traceback (most recent call last):
307
+ ...
308
+ ValueError: the gale transform does not correspond to a polytope
309
+ """
310
+ vertices = gale_transform_to_primal(vectors, base_ring, backend)
311
+ P = Polyhedron(vertices=vertices, base_ring=base_ring, backend=backend)
312
+
313
+ if not P.n_vertices() == len(vertices):
314
+ # If the input vectors are not totally cyclic, ``gale_transform_to_primal``
315
+ # raises an error.
316
+ # As no error was raised so far, the gale transform corresponds to
317
+ # to a point configuration.
318
+ # It corresponds to a polytope if and only if
319
+ # ``vertices`` are in convex position.
320
+ raise ValueError("the gale transform does not correspond to a polytope")
321
+
322
+ return P
323
+
324
+
325
+ def gale_transform_to_primal(vectors, base_ring=None, backend=None):
326
+ r"""
327
+ Return a point configuration dual to a totally cyclic vector configuration.
328
+
329
+ This is the dehomogenized vector configuration dual to the input.
330
+ The dual vector configuration is acyclic and can therefore
331
+ be dehomogenized as the input is totally cyclic.
332
+
333
+ INPUT:
334
+
335
+ - ``vectors`` -- the ordered vectors of the Gale transform
336
+
337
+ - ``base_ring`` -- string (default: ``None``);
338
+ the base ring to be used for the construction
339
+
340
+ - ``backend`` -- string (default: ``None``);
341
+ the backend to be use to construct a polyhedral,
342
+ used internally in case the center is not the origin,
343
+ see :func:`~sage.geometry.polyhedron.constructor.Polyhedron`
344
+
345
+ OUTPUT: an ordered point configuration as list of vectors
346
+
347
+ .. NOTE::
348
+
349
+ If the center of the (input) vectors is the origin,
350
+ the function is much faster and might give a nicer representation
351
+ of the point configuration.
352
+
353
+ If this is not the case, the vectors will be scaled
354
+ (each by a positive scalar) accordingly.
355
+
356
+ ALGORITHM:
357
+
358
+ Step 1: If the center of the (input) vectors is not the origin,
359
+ we do an appropriate transformation to make it so.
360
+
361
+ Step 2: We add a row of ones on top of ``Matrix(vectors)``.
362
+ The right kernel of this larger matrix is the dual configuration space,
363
+ and a basis of this space provides the dual point configuration.
364
+
365
+ More concretely, the dual vector configuration (inhomogeneous)
366
+ is obtained by taking a basis of the right kernel of ``Matrix(vectors)``.
367
+ If the center of the (input) vectors is the origin,
368
+ there exists a basis of the right kernel of the form
369
+ ``[[1], [V]]``, where ``[1]`` represents a row of ones.
370
+ Then, ``V`` is a dehomogenization and thus the dual point configuration.
371
+
372
+ To extend ``[1]`` to a basis of ``Matrix(vectors)``, we add
373
+ a row of ones to ``Matrix(vectors)`` and calculate a basis of the
374
+ right kernel of the obtained matrix.
375
+
376
+ REFERENCES:
377
+
378
+ For more information, see Section 6.4 of [Zie2007]_
379
+ or Definition 2.5.1 and Definition 4.1.35 of [DLRS2010]_.
380
+
381
+ .. SEEALSO::
382
+
383
+ :func`~sage.geometry.polyhedron.library.gale_transform_to_polytope`.
384
+
385
+ EXAMPLES::
386
+
387
+ sage: from sage.geometry.polyhedron.library import gale_transform_to_primal
388
+ sage: points = ((0, -1), (-1, 0), (1, 1), (1, 1), (-1, 0), (0, -1))
389
+ sage: gale_transform_to_primal(points)
390
+ [(0, 0, 1), (0, 1, 0), (1, 0, 0), (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
391
+
392
+ One can specify the base ring::
393
+
394
+ sage: p = [(1,1), (-1,-1), (1,0), (-1,0), (1,-1), (-2,1)]
395
+ sage: gtpp = gale_transform_to_primal(p); gtpp # needs sage.libs.pari
396
+ [(16, -35, 54),
397
+ (24, 10, 31),
398
+ (-15, 50, -60),
399
+ (-25, 0, 0),
400
+ (0, -25, 0),
401
+ (0, 0, -25)]
402
+ sage: (matrix(RDF, gtpp)/25 + # needs sage.libs.pari
403
+ ....: matrix(gale_transform_to_primal(p, base_ring=RDF))).norm() < 1e-15
404
+ True
405
+
406
+ One can also specify the backend to be used internally::
407
+
408
+ sage: gale_transform_to_primal(p, backend='field') # needs sage.libs.pari
409
+ [(48, -71, 88),
410
+ (84, -28, 99),
411
+ (-77, 154, -132),
412
+ (-55, 0, 0),
413
+ (0, -55, 0),
414
+ (0, 0, -55)]
415
+ sage: gale_transform_to_primal(p, backend='normaliz') # optional - pynormaliz, needs sage.libs.pari
416
+ [(16, -35, 54),
417
+ (24, 10, 31),
418
+ (-15, 50, -60),
419
+ (-25, 0, 0),
420
+ (0, -25, 0),
421
+ (0, 0, -25)]
422
+
423
+ The input vectors should be totally cyclic::
424
+
425
+ sage: gale_transform_to_primal([(0,1), (1,0), (1,1), (-1,0)])
426
+ Traceback (most recent call last):
427
+ ...
428
+ ValueError: input vectors not totally cyclic
429
+
430
+ sage: gale_transform_to_primal(
431
+ ....: [(1,1,0), (-1,-1,0), (1,0,0),
432
+ ....: (-1,0,0), (1,-1,0), (-2,1,0)], backend='field')
433
+ Traceback (most recent call last):
434
+ ...
435
+ ValueError: input vectors not totally cyclic
436
+ """
437
+ from sage.modules.free_module_element import vector
438
+ from sage.matrix.constructor import matrix
439
+ if base_ring:
440
+ vectors = tuple(vector(base_ring, x) for x in vectors)
441
+ else:
442
+ vectors = tuple(vector(x) for x in vectors)
443
+
444
+ if not sum(vectors).is_zero():
445
+ # The center of the input vectors shall be the origin.
446
+ # If this is not the case, we scale them accordingly.
447
+ # This has the advantage that right kernel of ``vectors`` can be
448
+ # presented in the form ``[[1], [V]]``, where ``V`` are the points
449
+ # in the dual point configuration.
450
+ # (Dehomogenization is straightforward.)
451
+
452
+ # Scaling of the vectors is equivalent to finding a hyperplane that intersects
453
+ # all vectors of the dual point configuration. But if the input is already provided
454
+ # such that the vectors add up to zero, the coordinates might be nicer.
455
+ # (And this is faster.)
456
+
457
+ if base_ring:
458
+ ker = matrix(base_ring, vectors).left_kernel()
459
+ else:
460
+ ker = matrix(vectors).left_kernel()
461
+ solutions = Polyhedron(lines=tuple(ker.basis_matrix()), base_ring=base_ring, backend=backend)
462
+
463
+ from sage.matrix.special import identity_matrix
464
+ pos_orthant = Polyhedron(rays=identity_matrix(len(vectors)), base_ring=base_ring, backend=backend)
465
+ pos_solutions = solutions.intersection(pos_orthant)
466
+ if base_ring is ZZ:
467
+ pos_solutions = pos_solutions.change_ring(ZZ)
468
+
469
+ # Any integral point in ``pos_solutions`` will correspond to scaling-factors
470
+ # that make ``sum(vectors)`` zero.
471
+ x = pos_solutions.representative_point()
472
+ if not all(y > 0 for y in x):
473
+ raise ValueError("input vectors not totally cyclic")
474
+ vectors = tuple(vec*x[i] for i, vec in enumerate(vectors))
475
+
476
+ # The right kernel of ``vectors`` has a basis of the form ``[[1], [V]]``,
477
+ # where ``V`` is the dehomogenized dual point configuration.
478
+ # If we append a row of ones to ``vectors``, ``V`` is just the right kernel.
479
+ if base_ring:
480
+ m = matrix(base_ring, vectors).transpose().stack(matrix(base_ring, [[1]*len(vectors)]))
481
+ else:
482
+ m = matrix(vectors).transpose().stack(matrix([[1]*len(vectors)]))
483
+
484
+ if m.rank() != len(vectors[0]) + 1:
485
+ # The given vectors do not span the ambient space,
486
+ # then there exists a nonnegative value vector.
487
+ raise ValueError("input vectors not totally cyclic")
488
+
489
+ return m.right_kernel_matrix(basis='computed').columns()
490
+
491
+
492
+ class Polytopes:
493
+ """
494
+ A class of constructors for commonly used, famous, or interesting
495
+ polytopes.
496
+ """
497
+
498
+ def regular_polygon(self, n, exact=True, base_ring=None, backend=None):
499
+ """
500
+ Return a regular polygon with `n` vertices.
501
+
502
+ INPUT:
503
+
504
+ - ``n`` -- positive integer; the number of vertices
505
+
506
+ - ``exact`` -- boolean (default: ``True``); if ``False`` floating point
507
+ numbers are used for coordinates
508
+
509
+ - ``base_ring`` -- a ring in which the coordinates will lie. It is
510
+ ``None`` by default. If it is not provided and ``exact`` is ``True``
511
+ then it will be the field of real algebraic number, if ``exact`` is
512
+ ``False`` it will be the real double field.
513
+
514
+ - ``backend`` -- the backend to use to create the polytope
515
+
516
+ EXAMPLES::
517
+
518
+ sage: # needs sage.rings.number_field
519
+ sage: octagon = polytopes.regular_polygon(8)
520
+ sage: octagon
521
+ A 2-dimensional polyhedron in AA^2 defined as the convex hull of 8 vertices
522
+ sage: octagon.n_vertices()
523
+ 8
524
+ sage: v = octagon.volume()
525
+ sage: v
526
+ 2.828427124746190?
527
+ sage: v == 2*QQbar(2).sqrt()
528
+ True
529
+
530
+ Its non exact version::
531
+
532
+ sage: polytopes.regular_polygon(3, exact=False).vertices() # needs cddexec
533
+ (A vertex at (0.0, 1.0),
534
+ A vertex at (0.8660254038, -0.5),
535
+ A vertex at (-0.8660254038, -0.5))
536
+ sage: polytopes.regular_polygon(25, exact=False).n_vertices() # needs cddexec
537
+ 25
538
+
539
+ TESTS::
540
+
541
+ sage: # optional - pynormaliz, needs sage.rings.number_field
542
+ sage: octagon = polytopes.regular_polygon(8, backend='normaliz')
543
+ sage: octagon
544
+ A 2-dimensional polyhedron in AA^2 defined as the convex hull of 8 vertices
545
+ sage: octagon.n_vertices()
546
+ 8
547
+ sage: octagon.volume()
548
+ 2*a
549
+ sage: TestSuite(octagon).run() # long time
550
+
551
+ sage: TestSuite(polytopes.regular_polygon(5, exact=False)).run() # needs scipy
552
+ """
553
+ n = ZZ(n)
554
+ if n <= 2:
555
+ raise ValueError("n (={}) must be an integer greater than 2".format(n))
556
+
557
+ if base_ring is None:
558
+ if exact:
559
+ from sage.rings.qqbar import AA as base_ring
560
+ else:
561
+ from sage.rings.real_double import RDF as base_ring
562
+
563
+ try:
564
+ omega = 2*base_ring.pi() / n
565
+ verts = [((i*omega).sin(), (i*omega).cos()) for i in range(n)]
566
+ except AttributeError:
567
+ from sage.rings.qqbar import QQbar
568
+ z = QQbar.zeta(n)
569
+ verts = [(base_ring((z**k).imag()), base_ring((z**k).real())) for k in range(n)]
570
+
571
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
572
+
573
+ def Birkhoff_polytope(self, n, backend=None):
574
+ """
575
+ Return the Birkhoff polytope with `n!` vertices.
576
+
577
+ The vertices of this polyhedron are the (flattened) `n` by `n`
578
+ permutation matrices. So the ambient vector space has dimension `n^2`
579
+ but the dimension of the polyhedron is `(n-1)^2`.
580
+
581
+ INPUT:
582
+
583
+ - ``n`` -- positive integer giving the size of the permutation matrices
584
+
585
+ - ``backend`` -- the backend to use to create the polytope
586
+
587
+ .. SEEALSO::
588
+
589
+ :meth:`sage.matrix.matrix2.Matrix.as_sum_of_permutations` -- return
590
+ the current matrix as a sum of permutation matrices
591
+
592
+ EXAMPLES::
593
+
594
+ sage: b3 = polytopes.Birkhoff_polytope(3)
595
+ sage: b3.f_vector()
596
+ (1, 6, 15, 18, 9, 1)
597
+ sage: b3.ambient_dim(), b3.dim()
598
+ (9, 4)
599
+ sage: b3.is_lattice_polytope()
600
+ True
601
+ sage: p3 = b3.ehrhart_polynomial() # optional - latte_int
602
+ sage: p3 # optional - latte_int
603
+ 1/8*t^4 + 3/4*t^3 + 15/8*t^2 + 9/4*t + 1
604
+ sage: [p3(i) for i in [1,2,3,4]] # optional - latte_int
605
+ [6, 21, 55, 120]
606
+ sage: [len((i*b3).integral_points()) for i in [1,2,3,4]] # needs sage.libs.pari
607
+ [6, 21, 55, 120]
608
+
609
+ sage: b4 = polytopes.Birkhoff_polytope(4)
610
+ sage: b4.n_vertices(), b4.ambient_dim(), b4.dim()
611
+ (24, 16, 9)
612
+
613
+ TESTS::
614
+
615
+ sage: b4norm = polytopes.Birkhoff_polytope(4,backend='normaliz') # optional - pynormaliz
616
+ sage: TestSuite(b4norm).run() # optional - pynormaliz
617
+ sage: TestSuite(polytopes.Birkhoff_polytope(3)).run()
618
+ """
619
+ from itertools import permutations
620
+ verts = []
621
+ for p in permutations(range(n)):
622
+ verts.append([ZZ.one() if p[i] == j else ZZ.zero()
623
+ for j in range(n) for i in range(n)])
624
+ return Polyhedron(vertices=verts, base_ring=ZZ, backend=backend)
625
+
626
+ def simplex(self, dim=3, project=False, base_ring=None, backend=None):
627
+ r"""
628
+ Return the ``dim`` dimensional simplex.
629
+
630
+ The `d`-simplex is the convex hull in `\RR^{d+1}` of the standard basis
631
+ `(1,0,\ldots,0)`, `(0,1,\ldots,0)`, \ldots, `(0,0,\ldots,1)`. For more
632
+ information, see the :wikipedia:`Simplex`.
633
+
634
+ INPUT:
635
+
636
+ - ``dim`` -- the dimension of the simplex, a positive
637
+ integer
638
+
639
+ - ``project`` -- boolean (default: ``False``); if ``True``, the polytope
640
+ is (isometrically) projected to a vector space of dimension
641
+ ``dim-1``. This corresponds to the projection given by the matrix
642
+ from :func:`zero_sum_projection`. By default, this operation turns
643
+ the coordinates into floating point approximations (see
644
+ ``base_ring``).
645
+
646
+ - ``base_ring`` -- the base ring to use to create the polytope;
647
+ if ``project`` is ``False``, this defaults to `\ZZ`.
648
+ Otherwise, it defaults to ``RDF``.
649
+
650
+ - ``backend`` -- the backend to use to create the polytope
651
+
652
+ .. SEEALSO::
653
+
654
+ :meth:`tetrahedron`
655
+
656
+ EXAMPLES::
657
+
658
+ sage: s5 = polytopes.simplex(5); s5
659
+ A 5-dimensional polyhedron in ZZ^6 defined as the convex hull of 6 vertices
660
+ sage: s5.f_vector()
661
+ (1, 6, 15, 20, 15, 6, 1)
662
+
663
+ sage: s5 = polytopes.simplex(5, project=True); s5 # needs cddexec
664
+ A 5-dimensional polyhedron in RDF^5 defined as the convex hull of 6 vertices
665
+
666
+ Its volume is `\sqrt{d+1} / d!`::
667
+
668
+ sage: s5 = polytopes.simplex(5, project=True) # needs cddexec
669
+ sage: s5.volume() # abs tol 1e-10 # needs cddexec scipy
670
+ 0.0204124145231931
671
+ sage: sqrt(6.) / factorial(5)
672
+ 0.0204124145231931
673
+
674
+ sage: s6 = polytopes.simplex(6, project=True) # needs cddexec
675
+ sage: s6.volume() # abs tol 1e-10 # needs cddexec scipy
676
+ 0.00367465459870082
677
+ sage: sqrt(7.) / factorial(6)
678
+ 0.00367465459870082
679
+
680
+ Computation in algebraic reals::
681
+
682
+ sage: s3 = polytopes.simplex(3, project=True, base_ring=AA) # needs sage.rings.number_field
683
+ sage: s3.volume() == sqrt(3+1) / factorial(3) # needs sage.rings.number_field
684
+ True
685
+
686
+ TESTS::
687
+
688
+ sage: s6norm = polytopes.simplex(6,backend='normaliz') # optional - pynormaliz
689
+ sage: TestSuite(s6norm).run() # optional - pynormaliz
690
+ sage: TestSuite(polytopes.simplex(5)).run()
691
+ """
692
+ verts = list((ZZ**(dim + 1)).basis())
693
+ if project:
694
+ # Handling of default in base_ring is delegated to project_points
695
+ verts = project_points(*verts, base_ring=base_ring)
696
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
697
+
698
+ def icosahedron(self, exact=True, base_ring=None, backend=None):
699
+ r"""
700
+ Return an icosahedron with edge length 1.
701
+
702
+ The icosahedron is one of the Platonic solids. It has 20 faces
703
+ and is dual to the :meth:`dodecahedron`.
704
+
705
+ INPUT:
706
+
707
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
708
+ approximate ring for the coordinates
709
+
710
+ - ``base_ring`` -- (optional) the ring in which the coordinates will
711
+ belong to. Note that this ring must contain `\sqrt(5)`. If it is not
712
+ provided and ``exact=True`` it will be the number field
713
+ `\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double
714
+ field.
715
+
716
+ - ``backend`` -- the backend to use to create the polytope
717
+
718
+ EXAMPLES::
719
+
720
+ sage: ico = polytopes.icosahedron() # needs sage.groups sage.rings.number_field
721
+ sage: ico.f_vector() # needs sage.groups sage.rings.number_field
722
+ (1, 12, 30, 20, 1)
723
+ sage: ico.volume() # needs sage.groups sage.rings.number_field
724
+ 5/12*sqrt5 + 5/4
725
+
726
+ Its non exact version::
727
+
728
+ sage: ico = polytopes.icosahedron(exact=False) # needs sage.groups
729
+ sage: ico.base_ring() # needs sage.groups
730
+ Real Double Field
731
+ sage: ico.volume() # known bug # needs sage.groups
732
+ 2.181694990...
733
+
734
+ A version using `AA <sage.rings.qqbar.AlgebraicRealField>`::
735
+
736
+ sage: ico = polytopes.icosahedron(base_ring=AA) # long time # needs sage.groups sage.rings.number_field
737
+ sage: ico.base_ring() # long time # needs sage.groups sage.rings.number_field
738
+ Algebraic Real Field
739
+ sage: ico.volume() # long time # needs sage.groups sage.rings.number_field
740
+ 2.181694990624913?
741
+
742
+ Note that if base ring is provided it must contain the square root of
743
+ `5`. Otherwise you will get an error::
744
+
745
+ sage: polytopes.icosahedron(base_ring=QQ) # needs sage.symbolic
746
+ Traceback (most recent call last):
747
+ ...
748
+ TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational
749
+
750
+ TESTS::
751
+
752
+ sage: # optional - pynormaliz, needs sage.groups sage.rings.number_field
753
+ sage: ico = polytopes.icosahedron(backend='normaliz')
754
+ sage: ico.f_vector()
755
+ (1, 12, 30, 20, 1)
756
+ sage: ico.volume()
757
+ 5/12*sqrt5 + 5/4
758
+ sage: TestSuite(ico).run()
759
+
760
+ sage: ico = polytopes.icosahedron(exact=False) # needs sage.groups
761
+ sage: TestSuite(ico).run(skip='_test_lawrence') # needs sage.groups
762
+ """
763
+ if base_ring is None and exact:
764
+ from sage.rings.number_field.number_field import QuadraticField
765
+ K = QuadraticField(5, 'sqrt5')
766
+ sqrt5 = K.gen()
767
+ g = (1 + sqrt5) / 2
768
+ base_ring = K
769
+ else:
770
+ if base_ring is None:
771
+ from sage.rings.real_double import RDF as base_ring
772
+ g = (1 + base_ring(5).sqrt()) / 2
773
+
774
+ r12 = base_ring.one() / 2
775
+ z = base_ring.zero()
776
+ pts = [[z, s1 * r12, s2 * g / 2]
777
+ for s1, s2 in itertools.product([1, -1], repeat=2)]
778
+ verts = [p(v) for p in AlternatingGroup(3) for v in pts]
779
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
780
+
781
+ def dodecahedron(self, exact=True, base_ring=None, backend=None):
782
+ r"""
783
+ Return a dodecahedron.
784
+
785
+ The dodecahedron is the Platonic solid dual to the :meth:`icosahedron`.
786
+
787
+ INPUT:
788
+
789
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
790
+ approximate ring for the coordinates
791
+
792
+ - ``base_ring`` -- (optional) the ring in which the coordinates will
793
+ belong to. Note that this ring must contain `\sqrt(5)`. If it is not
794
+ provided and ``exact=True`` it will be the number field
795
+ `\QQ[\sqrt(5)]` and if ``exact=False`` it will be the real double
796
+ field.
797
+
798
+ - ``backend`` -- the backend to use to create the polytope
799
+
800
+ EXAMPLES::
801
+
802
+ sage: # needs sage.groups sage.rings.number_field
803
+ sage: d12 = polytopes.dodecahedron()
804
+ sage: d12.f_vector()
805
+ (1, 20, 30, 12, 1)
806
+ sage: d12.volume()
807
+ -176*sqrt5 + 400
808
+ sage: numerical_approx(_)
809
+ 6.45203596003699
810
+
811
+ sage: d12 = polytopes.dodecahedron(exact=False) # needs sage.groups
812
+ sage: d12.base_ring() # needs sage.groups
813
+ Real Double Field
814
+
815
+ Here is an error with a field that does not contain `\sqrt(5)`::
816
+
817
+ sage: polytopes.dodecahedron(base_ring=QQ) # needs sage.groups sage.symbolic
818
+ Traceback (most recent call last):
819
+ ...
820
+ TypeError: unable to convert 1/4*sqrt(5) + 1/4 to a rational
821
+
822
+ TESTS::
823
+
824
+ sage: d12 = polytopes.dodecahedron(backend='normaliz') # optional - pynormaliz, needs sage.groups sage.rings.number_field
825
+ sage: d12.f_vector() # optional - pynormaliz, needs sage.groups sage.rings.number_field
826
+ (1, 20, 30, 12, 1)
827
+ sage: TestSuite(d12).run() # optional - pynormaliz, needs sage.groups sage.rings.number_field
828
+ """
829
+ return self.icosahedron(exact=exact, base_ring=base_ring, backend=backend).polar()
830
+
831
+ def small_rhombicuboctahedron(self, exact=True, base_ring=None, backend=None):
832
+ r"""
833
+ Return the (small) rhombicuboctahedron.
834
+
835
+ The rhombicuboctahedron is an Archimedean solid with 24 vertices and 26
836
+ faces. See the :wikipedia:`Rhombicuboctahedron` for more information.
837
+
838
+ INPUT:
839
+
840
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
841
+ approximate ring for the coordinates
842
+
843
+ - ``base_ring`` -- the ring in which the coordinates will belong to. If
844
+ it is not provided and ``exact=True`` it will be a the number field
845
+ `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
846
+ it will be the real double field.
847
+
848
+ - ``backend`` -- the backend to use to create the polytope
849
+
850
+ EXAMPLES::
851
+
852
+ sage: sr = polytopes.small_rhombicuboctahedron() # needs sage.rings.number_field
853
+ sage: sr.f_vector() # needs sage.rings.number_field
854
+ (1, 24, 48, 26, 1)
855
+ sage: sr.volume() # needs sage.rings.number_field
856
+ 80/3*sqrt2 + 32
857
+
858
+ The faces are `8` equilateral triangles and `18` squares::
859
+
860
+ sage: sum(1 for f in sr.facets() if len(f.vertices()) == 3) # needs sage.rings.number_field
861
+ 8
862
+ sage: sum(1 for f in sr.facets() if len(f.vertices()) == 4) # needs sage.rings.number_field
863
+ 18
864
+
865
+ Its non exact version::
866
+
867
+ sage: sr = polytopes.small_rhombicuboctahedron(False); sr # needs cddexec
868
+ A 3-dimensional polyhedron in RDF^3 defined as the convex hull of
869
+ 24 vertices
870
+ sage: sr.f_vector() # needs cddexec
871
+ (1, 24, 48, 26, 1)
872
+
873
+ TESTS::
874
+
875
+ sage: # optional - pynormaliz, needs sage.rings.number_field
876
+ sage: sr = polytopes.small_rhombicuboctahedron(backend='normaliz')
877
+ sage: sr.f_vector()
878
+ (1, 24, 48, 26, 1)
879
+ sage: sr.volume()
880
+ 80/3*sqrt2 + 32
881
+ sage: TestSuite(sr).run() # long time
882
+ """
883
+ if base_ring is None and exact:
884
+ from sage.rings.number_field.number_field import QuadraticField
885
+ K = QuadraticField(2, 'sqrt2')
886
+ sqrt2 = K.gen()
887
+ base_ring = K
888
+ else:
889
+ if base_ring is None:
890
+ from sage.rings.real_double import RDF as base_ring
891
+ sqrt2 = base_ring(2).sqrt()
892
+
893
+ one = base_ring.one()
894
+ a = sqrt2 + one
895
+ verts = []
896
+ verts.extend([s1*one, s2*one, s3*a] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
897
+ verts.extend([s1*one, s3*a, s2*one] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
898
+ verts.extend([s1*a, s2*one, s3*one] for s1, s2, s3 in itertools.product([1, -1], repeat=3))
899
+ return Polyhedron(vertices=verts, backend=backend)
900
+
901
+ def great_rhombicuboctahedron(self, exact=True, base_ring=None, backend=None):
902
+ r"""
903
+ Return the great rhombicuboctahedron.
904
+
905
+ The great rhombicuboctahedron (or truncated cuboctahedron) is an
906
+ Archimedean solid with 48 vertices and 26 faces. For more information
907
+ see the :wikipedia:`Truncated_cuboctahedron`.
908
+
909
+ INPUT:
910
+
911
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
912
+ approximate ring for the coordinates
913
+
914
+ - ``base_ring`` -- the ring in which the coordinates will belong to. If
915
+ it is not provided and ``exact=True`` it will be a the number field
916
+ `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
917
+ it will be the real double field.
918
+
919
+ - ``backend`` -- the backend to use to create the polytope
920
+
921
+ EXAMPLES::
922
+
923
+ sage: gr = polytopes.great_rhombicuboctahedron() # long time # needs sage.rings.number_field
924
+ sage: gr.f_vector() # long time # needs sage.rings.number_field
925
+ (1, 48, 72, 26, 1)
926
+
927
+ A faster implementation is obtained by setting ``exact=False``::
928
+
929
+ sage: # needs cddexec
930
+ sage: gr = polytopes.great_rhombicuboctahedron(exact=False)
931
+ sage: gr.f_vector()
932
+ (1, 48, 72, 26, 1)
933
+
934
+ Its facets are 4 squares, 8 regular hexagons and 6 regular octagons::
935
+
936
+ sage: # needs cddexec
937
+ sage: sum(1 for f in gr.facets() if len(f.vertices()) == 4)
938
+ 12
939
+ sage: sum(1 for f in gr.facets() if len(f.vertices()) == 6)
940
+ 8
941
+ sage: sum(1 for f in gr.facets() if len(f.vertices()) == 8)
942
+ 6
943
+ """
944
+ if base_ring is None and exact:
945
+ from sage.rings.number_field.number_field import QuadraticField
946
+ base_ring = QuadraticField(2, 'sqrt2')
947
+ sqrt2 = base_ring.gen()
948
+ else:
949
+ if base_ring is None:
950
+ from sage.rings.real_double import RDF as base_ring
951
+ sqrt2 = base_ring(2).sqrt()
952
+
953
+ one = base_ring.one()
954
+ v1 = sqrt2 + 1
955
+ v2 = 2 * sqrt2 + 1
956
+ verts = [[s1 * z1, s2 * z2, s3 * z3]
957
+ for z1, z2, z3 in itertools.permutations([one, v1, v2])
958
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
959
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
960
+
961
+ def rhombic_dodecahedron(self, backend=None):
962
+ """
963
+ Return the rhombic dodecahedron.
964
+
965
+ The rhombic dodecahedron is a polytope dual to the cuboctahedron. It
966
+ has 14 vertices and 12 faces. For more information see
967
+ the :wikipedia:`Rhombic_dodecahedron`.
968
+
969
+ INPUT:
970
+
971
+ - ``backend`` -- the backend to use to create the polytope
972
+
973
+ .. SEEALSO::
974
+
975
+ :meth:`cuboctahedron`
976
+
977
+ EXAMPLES::
978
+
979
+ sage: rd = polytopes.rhombic_dodecahedron()
980
+ sage: rd.f_vector()
981
+ (1, 14, 24, 12, 1)
982
+
983
+ Its facets are 12 quadrilaterals (not all identical)::
984
+
985
+ sage: sum(1 for f in rd.facets() if len(f.vertices()) == 4)
986
+ 12
987
+
988
+ Some more computations::
989
+
990
+ sage: p = rd.ehrhart_polynomial() # optional - latte_int
991
+ sage: p # optional - latte_int
992
+ 16*t^3 + 12*t^2 + 4*t + 1
993
+ sage: [p(i) for i in [1,2,3,4]] # optional - latte_int
994
+ [33, 185, 553, 1233]
995
+ sage: [len((i*rd).integral_points()) for i in [1,2,3,4]]
996
+ [33, 185, 553, 1233]
997
+
998
+ TESTS::
999
+
1000
+ sage: rd_norm = polytopes.rhombic_dodecahedron(backend='normaliz') # optional - pynormaliz
1001
+ sage: TestSuite(rd_norm).run() # optional - pynormaliz
1002
+ """
1003
+ v = [[2,0,0],[-2,0,0],[0,2,0],[0,-2,0],[0,0,2],[0,0,-2]]
1004
+ v.extend(itertools.product([1, -1], repeat=3))
1005
+ return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
1006
+
1007
+ def cuboctahedron(self, backend=None):
1008
+ r"""
1009
+ Return the cuboctahedron.
1010
+
1011
+ The cuboctahedron is an Archimedean solid with 12 vertices and 14 faces
1012
+ dual to the rhombic dodecahedron. It can be defined as the convex hull
1013
+ of the twelve vertices `(0, \pm 1, \pm 1)`, `(\pm 1, 0, \pm 1)` and
1014
+ `(\pm 1, \pm 1, 0)`. For more information, see the
1015
+ :wikipedia:`Cuboctahedron`.
1016
+
1017
+ INPUT:
1018
+
1019
+ - ``backend`` -- the backend to use to create the polytope
1020
+
1021
+ .. SEEALSO::
1022
+
1023
+ :meth:`rhombic_dodecahedron`
1024
+
1025
+ EXAMPLES::
1026
+
1027
+ sage: co = polytopes.cuboctahedron()
1028
+ sage: co.f_vector()
1029
+ (1, 12, 24, 14, 1)
1030
+
1031
+ Its facets are 8 triangles and 6 squares::
1032
+
1033
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
1034
+ 8
1035
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 4)
1036
+ 6
1037
+
1038
+ Some more computation::
1039
+
1040
+ sage: co.volume()
1041
+ 20/3
1042
+ sage: co.ehrhart_polynomial() # optional - latte_int
1043
+ 20/3*t^3 + 8*t^2 + 10/3*t + 1
1044
+
1045
+ TESTS::
1046
+
1047
+ sage: co_norm = polytopes.cuboctahedron(backend='normaliz') # optional - pynormaliz
1048
+ sage: TestSuite(co_norm).run() # optional - pynormaliz
1049
+ """
1050
+ v = [[0, -1, -1], [0, 1, -1], [0, -1, 1], [0, 1, 1],
1051
+ [-1, -1, 0], [1, -1, 0], [-1, 1, 0], [1, 1, 0],
1052
+ [-1, 0, -1], [1, 0, -1], [-1, 0, 1], [1, 0, 1]]
1053
+ return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
1054
+
1055
+ def truncated_cube(self, exact=True, base_ring=None, backend=None):
1056
+ r"""
1057
+ Return the truncated cube.
1058
+
1059
+ The truncated cube is an Archimedean solid with 24 vertices
1060
+ and 14 faces. It can be defined as the convex hull of the 24 vertices
1061
+ `(\pm x, \pm 1, \pm 1), (\pm 1, \pm x, \pm 1), (\pm 1, \pm 1, \pm x)`
1062
+ where `x = \sqrt(2) - 1`. For more information, see the
1063
+ :wikipedia:`Truncated_cube`.
1064
+
1065
+ INPUT:
1066
+
1067
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
1068
+ approximate ring for the coordinates
1069
+
1070
+ - ``base_ring`` -- the ring in which the coordinates will belong to. If
1071
+ it is not provided and ``exact=True`` it will be a the number field
1072
+ `\QQ[\sqrt{2}]` and if ``exact=False`` it
1073
+ will be the real double field.
1074
+
1075
+ - ``backend`` -- the backend to use to create the polytope
1076
+
1077
+ EXAMPLES::
1078
+
1079
+ sage: co = polytopes.truncated_cube() # needs sage.rings.number_field
1080
+ sage: co.f_vector() # needs sage.rings.number_field
1081
+ (1, 24, 36, 14, 1)
1082
+
1083
+ Its facets are 8 triangles and 6 octogons::
1084
+
1085
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 3) # needs sage.rings.number_field
1086
+ 8
1087
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 8) # needs sage.rings.number_field
1088
+ 6
1089
+
1090
+ Some more computation::
1091
+
1092
+ sage: co.volume() # needs sage.rings.number_field
1093
+ 56/3*sqrt2 - 56/3
1094
+
1095
+ TESTS::
1096
+
1097
+ sage: co = polytopes.truncated_cube(backend='normaliz') # optional - pynormaliz, needs sage.rings.number_field
1098
+ sage: co.f_vector() # optional - pynormaliz # needs sage.rings.number_field
1099
+ (1, 24, 36, 14, 1)
1100
+ sage: TestSuite(co).run() # optional - pynormaliz # needs sage.rings.number_field
1101
+ """
1102
+ if base_ring is None and exact:
1103
+ from sage.rings.number_field.number_field import QuadraticField
1104
+ K = QuadraticField(2, 'sqrt2')
1105
+ sqrt2 = K.gen()
1106
+ g = sqrt2 - 1
1107
+ base_ring = K
1108
+ else:
1109
+ if base_ring is None:
1110
+ from sage.rings.real_double import RDF as base_ring
1111
+ g = base_ring(2).sqrt() - 1
1112
+
1113
+ v = [[a * g, b, c] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
1114
+ v += [[a, b * g, c] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
1115
+ v += [[a, b, c * g] for a in [-1, 1] for b in [-1, 1] for c in [-1, 1]]
1116
+ return Polyhedron(vertices=v, base_ring=base_ring, backend=backend)
1117
+
1118
+ def tetrahedron(self, backend=None):
1119
+ """
1120
+ Return the tetrahedron.
1121
+
1122
+ The tetrahedron is a Platonic solid with 4 vertices and 4 faces
1123
+ dual to itself. It can be defined as the convex hull
1124
+ of the 4 vertices `(0, 0, 0)`, `(1, 1, 0)`, `(1, 0, 1)` and
1125
+ `(0, 1, 1)`. For more information, see the
1126
+ :wikipedia:`Tetrahedron`.
1127
+
1128
+ INPUT:
1129
+
1130
+ - ``backend`` -- the backend to use to create the polytope
1131
+
1132
+ .. SEEALSO::
1133
+
1134
+ :meth:`simplex`
1135
+
1136
+ EXAMPLES::
1137
+
1138
+ sage: co = polytopes.tetrahedron()
1139
+ sage: co.f_vector()
1140
+ (1, 4, 6, 4, 1)
1141
+
1142
+ Its facets are 4 triangles::
1143
+
1144
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
1145
+ 4
1146
+
1147
+ Some more computation::
1148
+
1149
+ sage: co.volume()
1150
+ 1/3
1151
+ sage: co.ehrhart_polynomial() # optional - latte_int
1152
+ 1/3*t^3 + t^2 + 5/3*t + 1
1153
+
1154
+ TESTS::
1155
+
1156
+ sage: t_norm = polytopes.tetrahedron(backend='normaliz') # optional - pynormaliz
1157
+ sage: TestSuite(t_norm).run() # optional - pynormaliz
1158
+ """
1159
+ v = [[0, 0, 0], [1, 0, 1], [1, 1, 0], [0, 1, 1]]
1160
+ return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
1161
+
1162
+ def truncated_tetrahedron(self, backend=None):
1163
+ r"""
1164
+ Return the truncated tetrahedron.
1165
+
1166
+ The truncated tetrahedron is an Archimedean solid with 12
1167
+ vertices and 8 faces. It can be defined as the convex hull off
1168
+ all the permutations of `(\pm 1, \pm 1, \pm 3)` with an even
1169
+ number of minus signs. For more information, see the
1170
+ :wikipedia:`Truncated_tetrahedron`.
1171
+
1172
+ INPUT:
1173
+
1174
+ - ``backend`` -- the backend to use to create the polytope
1175
+
1176
+ EXAMPLES::
1177
+
1178
+ sage: co = polytopes.truncated_tetrahedron()
1179
+ sage: co.f_vector()
1180
+ (1, 12, 18, 8, 1)
1181
+
1182
+ Its facets are 4 triangles and 4 hexagons::
1183
+
1184
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
1185
+ 4
1186
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 6)
1187
+ 4
1188
+
1189
+ Some more computation::
1190
+
1191
+ sage: co.volume()
1192
+ 184/3
1193
+ sage: co.ehrhart_polynomial() # optional - latte_int
1194
+ 184/3*t^3 + 28*t^2 + 26/3*t + 1
1195
+
1196
+ TESTS::
1197
+
1198
+ sage: tt_norm = polytopes.truncated_tetrahedron(backend='normaliz') # optional - pynormaliz
1199
+ sage: TestSuite(tt_norm).run() # optional - pynormaliz
1200
+ """
1201
+ v = [(3,1,1), (1,3,1), (1,1,3),
1202
+ (-3,-1,1), (-1,-3,1), (-1,-1,3),
1203
+ (-3,1,-1), (-1,3,-1), (-1,1,-3),
1204
+ (3,-1,-1), (1,-3,-1), (1,-1,-3)]
1205
+ return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
1206
+
1207
+ def truncated_octahedron(self, backend=None):
1208
+ r"""
1209
+ Return the truncated octahedron.
1210
+
1211
+ The truncated octahedron is an Archimedean solid with 24
1212
+ vertices and 14 faces. It can be defined as the convex hull
1213
+ off all the permutations of `(0, \pm 1, \pm 2)`. For more
1214
+ information, see the :wikipedia:`Truncated_octahedron`.
1215
+
1216
+ This is also known as the permutohedron of dimension 3.
1217
+
1218
+ INPUT:
1219
+
1220
+ - ``backend`` -- the backend to use to create the polytope
1221
+
1222
+ EXAMPLES::
1223
+
1224
+ sage: co = polytopes.truncated_octahedron()
1225
+ sage: co.f_vector()
1226
+ (1, 24, 36, 14, 1)
1227
+
1228
+ Its facets are 6 squares and 8 hexagons::
1229
+
1230
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 4)
1231
+ 6
1232
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 6)
1233
+ 8
1234
+
1235
+ Some more computation::
1236
+
1237
+ sage: co.volume()
1238
+ 32
1239
+ sage: co.ehrhart_polynomial() # optional - latte_int # needs sage.combinat
1240
+ 32*t^3 + 18*t^2 + 6*t + 1
1241
+
1242
+ TESTS::
1243
+
1244
+ sage: to_norm = polytopes.truncated_octahedron(backend='normaliz') # optional - pynormaliz, needs sage.combinat
1245
+ sage: TestSuite(to_norm).run() # optional - pynormaliz, needs sage.combinat
1246
+ """
1247
+ v = [(0, e, f) for e in [-1, 1] for f in [-2, 2]]
1248
+ v = [(xyz[sigma(1) - 1], xyz[sigma(2) - 1], xyz[sigma(3) - 1])
1249
+ for sigma in Permutations(3) for xyz in v]
1250
+ return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
1251
+
1252
+ def octahedron(self, backend=None):
1253
+ r"""
1254
+ Return the octahedron.
1255
+
1256
+ The octahedron is a Platonic solid with 6 vertices and 8 faces
1257
+ dual to the cube. It can be defined as the convex hull
1258
+ of the six vertices `(0, 0, \pm 1)`, `(\pm 1, 0, 0)` and
1259
+ `(0, \pm 1, 0)`. For more information, see the
1260
+ :wikipedia:`Octahedron`.
1261
+
1262
+ INPUT:
1263
+
1264
+ - ``backend`` -- the backend to use to create the polytope
1265
+
1266
+ EXAMPLES::
1267
+
1268
+ sage: co = polytopes.octahedron()
1269
+ sage: co.f_vector()
1270
+ (1, 6, 12, 8, 1)
1271
+
1272
+ Its facets are 8 triangles::
1273
+
1274
+ sage: sum(1 for f in co.facets() if len(f.vertices()) == 3)
1275
+ 8
1276
+
1277
+ Some more computation::
1278
+
1279
+ sage: co.volume()
1280
+ 4/3
1281
+ sage: co.ehrhart_polynomial() # optional - latte_int
1282
+ 4/3*t^3 + 2*t^2 + 8/3*t + 1
1283
+
1284
+ TESTS::
1285
+
1286
+ sage: o_norm = polytopes.octahedron(backend='normaliz') # optional - pynormaliz
1287
+ sage: TestSuite(o_norm).run() # optional - pynormaliz
1288
+ """
1289
+ v = [[0, 0, -1], [0, 0, 1], [1, 0, 0],
1290
+ [-1, 0, 0], [0, 1, 0], [0, -1, 0]]
1291
+ return Polyhedron(vertices=v, base_ring=ZZ, backend=backend)
1292
+
1293
+ def snub_cube(self, exact=False, base_ring=None, backend=None, verbose=False):
1294
+ """
1295
+ Return a snub cube.
1296
+
1297
+ The snub cube is an Archimedean solid. It has 24 vertices and 38 faces.
1298
+ For more information see the :wikipedia:`Snub_cube`.
1299
+
1300
+ The constant `z` used in constructing this polytope is the reciprocal
1301
+ of the tribonacci constant, that is, the solution of the equation
1302
+ `x^3 + x^2 + x - 1 = 0`.
1303
+ See :wikipedia:`Generalizations_of_Fibonacci_numbers#Tribonacci_numbers`.
1304
+
1305
+ INPUT:
1306
+
1307
+ - ``exact`` -- boolean (default: ``False``); if ``True`` use exact
1308
+ coordinates instead of floating point approximations
1309
+
1310
+ - ``base_ring`` -- the field to use; if ``None`` (the default),
1311
+ construct the exact number field needed (if ``exact`` is ``True``) or
1312
+ default to ``RDF`` (if ``exact`` is ``True``)
1313
+
1314
+ - ``backend`` -- the backend to use to create the polytope; if
1315
+ ``None`` (the default), the backend will be selected automatically
1316
+
1317
+ EXAMPLES::
1318
+
1319
+ sage: # needs sage.groups
1320
+ sage: sc_inexact = polytopes.snub_cube(exact=False); sc_inexact
1321
+ A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices
1322
+ sage: sc_inexact.f_vector()
1323
+ (1, 24, 60, 38, 1)
1324
+
1325
+ sage: # long time, needs sage.groups sage.rings.number_field
1326
+ sage: sc_exact = polytopes.snub_cube(exact=True)
1327
+ sage: sc_exact.f_vector()
1328
+ (1, 24, 60, 38, 1)
1329
+ sage: sorted(sc_exact.vertices())
1330
+ [A vertex at (-1, -z, -z^2),
1331
+ A vertex at (-1, -z^2, z),
1332
+ A vertex at (-1, z^2, -z),
1333
+ A vertex at (-1, z, z^2),
1334
+ A vertex at (-z, -1, z^2),
1335
+ A vertex at (-z, -z^2, -1),
1336
+ A vertex at (-z, z^2, 1),
1337
+ A vertex at (-z, 1, -z^2),
1338
+ A vertex at (-z^2, -1, -z),
1339
+ A vertex at (-z^2, -z, 1),
1340
+ A vertex at (-z^2, z, -1),
1341
+ A vertex at (-z^2, 1, z),
1342
+ A vertex at (z^2, -1, z),
1343
+ A vertex at (z^2, -z, -1),
1344
+ A vertex at (z^2, z, 1),
1345
+ A vertex at (z^2, 1, -z),
1346
+ A vertex at (z, -1, -z^2),
1347
+ A vertex at (z, -z^2, 1),
1348
+ A vertex at (z, z^2, -1),
1349
+ A vertex at (z, 1, z^2),
1350
+ A vertex at (1, -z, z^2),
1351
+ A vertex at (1, -z^2, -z),
1352
+ A vertex at (1, z^2, z),
1353
+ A vertex at (1, z, -z^2)]
1354
+ sage: sc_exact.is_combinatorially_isomorphic(sc_inexact)
1355
+ True
1356
+
1357
+ TESTS::
1358
+
1359
+ sage: sc = polytopes.snub_cube(exact=True, backend='normaliz') # optional - pynormaliz, needs sage.groups sage.rings.number_field
1360
+ sage: sc.f_vector() # optional - pynormaliz, needs sage.groups sage.rings.number_field
1361
+ (1, 24, 60, 38, 1)
1362
+ """
1363
+ def construct_z(field):
1364
+ # z here is the reciprocal of the tribonacci constant, that is, the
1365
+ # solution of the equation x^3 + x^2 + x - 1 = 0.
1366
+ tsqr33 = 3 * field(33).sqrt()
1367
+ return ((17 + tsqr33)**QQ((1, 3)) - (-17 + tsqr33)**QQ((1, 3)) - 1) / 3
1368
+
1369
+ if exact and base_ring is None:
1370
+ # construct the exact number field
1371
+ from sage.rings.qqbar import AA
1372
+ from sage.rings.number_field.number_field import NumberField
1373
+ R = QQ['x']
1374
+ f = R([-1, 1, 1, 1])
1375
+ embedding = construct_z(AA)
1376
+ base_ring = NumberField(f, name='z', embedding=embedding)
1377
+ z = base_ring.gen()
1378
+ else:
1379
+ if base_ring is None:
1380
+ from sage.rings.real_double import RDF as base_ring
1381
+ z = construct_z(base_ring)
1382
+
1383
+ verts = []
1384
+ z2 = z ** 2
1385
+ A3 = AlternatingGroup(3)
1386
+ for e in [-1, 1]:
1387
+ for f in [-1, 1]:
1388
+ for g in [-1, 1]:
1389
+ if e * f * g == -1:
1390
+ v = [e, f * z, g * z2]
1391
+ for p in A3:
1392
+ verts += [p(v)]
1393
+ else:
1394
+ v = [f * z, e, g * z2]
1395
+ for p in A3:
1396
+ verts += [p(v)]
1397
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
1398
+
1399
+ def buckyball(self, exact=True, base_ring=None, backend=None):
1400
+ r"""
1401
+ Return the bucky ball.
1402
+
1403
+ The bucky ball, also known as the truncated icosahedron is an
1404
+ Archimedean solid. It has 32 faces and 60 vertices.
1405
+
1406
+ .. SEEALSO::
1407
+
1408
+ :meth:`icosahedron`
1409
+
1410
+ INPUT:
1411
+
1412
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
1413
+ approximate ring for the coordinates
1414
+
1415
+ - ``base_ring`` -- the ring in which the coordinates will belong to. If
1416
+ it is not provided and ``exact=True`` it will be a the number field
1417
+ `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
1418
+ it will be the real double field.
1419
+
1420
+ - ``backend`` -- the backend to use to create the polytope
1421
+
1422
+ EXAMPLES::
1423
+
1424
+ sage: bb = polytopes.buckyball() # long time # needs sage.groups sage.rings.number_field
1425
+ sage: bb.f_vector() # long time # needs sage.groups sage.rings.number_field
1426
+ (1, 60, 90, 32, 1)
1427
+ sage: bb.base_ring() # long time # needs sage.groups sage.rings.number_field
1428
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1429
+ with sqrt5 = 2.236067977499790?
1430
+
1431
+ A much faster implementation using floating point approximations::
1432
+
1433
+ sage: bb = polytopes.buckyball(exact=False) # needs sage.groups
1434
+ sage: bb.f_vector() # needs sage.groups
1435
+ (1, 60, 90, 32, 1)
1436
+ sage: bb.base_ring() # needs sage.groups
1437
+ Real Double Field
1438
+
1439
+ Its facets are 5 regular pentagons and 6 regular hexagons::
1440
+
1441
+ sage: sum(1 for f in bb.facets() if len(f.vertices()) == 5) # needs sage.groups
1442
+ 12
1443
+ sage: sum(1 for f in bb.facets() if len(f.vertices()) == 6) # needs sage.groups
1444
+ 20
1445
+
1446
+ TESTS::
1447
+
1448
+ sage: # optional - pynormaliz, needs sage.groups sage.rings.number_field
1449
+ sage: bb = polytopes.buckyball(backend='normaliz')
1450
+ sage: bb.f_vector()
1451
+ (1, 60, 90, 32, 1)
1452
+ sage: bb.base_ring()
1453
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1454
+ with sqrt5 = 2.236067977499790?
1455
+ """
1456
+ return self.icosahedron(exact=exact, base_ring=base_ring, backend=backend).truncation()
1457
+
1458
+ def icosidodecahedron(self, exact=True, backend=None):
1459
+ """
1460
+ Return the icosidodecahedron.
1461
+
1462
+ The Icosidodecahedron is a polyhedron with twenty triangular faces and
1463
+ twelve pentagonal faces. For more information see the
1464
+ :wikipedia:`Icosidodecahedron`.
1465
+
1466
+ INPUT:
1467
+
1468
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
1469
+ approximate ring for the coordinates
1470
+
1471
+ - ``backend`` -- the backend to use to create the polytope
1472
+
1473
+ EXAMPLES::
1474
+
1475
+ sage: id = polytopes.icosidodecahedron() # needs sage.groups sage.rings.number_field
1476
+ sage: id.f_vector() # needs sage.groups sage.rings.number_field
1477
+ (1, 30, 60, 32, 1)
1478
+
1479
+ TESTS::
1480
+
1481
+ sage: id = polytopes.icosidodecahedron(exact=False); id # needs sage.groups sage.rings.number_field
1482
+ A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 30 vertices
1483
+ sage: TestSuite(id).run(skip=["_test_is_combinatorially_isomorphic", # needs sage.groups sage.rings.number_field
1484
+ ....: "_test_product",
1485
+ ....: "_test_pyramid",
1486
+ ....: "_test_lawrence"])
1487
+
1488
+ sage: # optional - pynormaliz, needs sage.groups sage.rings.number_field
1489
+ sage: id = polytopes.icosidodecahedron(backend='normaliz')
1490
+ sage: id.f_vector()
1491
+ (1, 30, 60, 32, 1)
1492
+ sage: id.base_ring()
1493
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1494
+ with sqrt5 = 2.236067977499790?
1495
+ sage: TestSuite(id).run() # long time
1496
+ """
1497
+ from sage.rings.number_field.number_field import QuadraticField
1498
+ from itertools import product
1499
+
1500
+ K = QuadraticField(5, 'sqrt5')
1501
+ one = K.one()
1502
+ phi = (one+K.gen())/2
1503
+
1504
+ gens = [((-1)**a*one/2, (-1)**b*phi/2, (-1)**c*(one+phi)/2)
1505
+ for a, b, c in product([0, 1], repeat=3)]
1506
+ gens.extend([(0, 0, phi), (0, 0, -phi)])
1507
+
1508
+ verts = []
1509
+ for p in AlternatingGroup(3):
1510
+ verts.extend(p(x) for x in gens)
1511
+
1512
+ if exact:
1513
+ return Polyhedron(vertices=verts, base_ring=K, backend=backend)
1514
+ else:
1515
+ from sage.rings.real_mpfr import RR
1516
+ verts = [(RR(x), RR(y), RR(z)) for x, y, z in verts]
1517
+ return Polyhedron(vertices=verts, backend=backend)
1518
+
1519
+ def icosidodecahedron_V2(self, exact=True, base_ring=None, backend=None):
1520
+ r"""
1521
+ Return the icosidodecahedron.
1522
+
1523
+ The icosidodecahedron is an Archimedean solid.
1524
+ It has 32 faces and 30 vertices. For more information, see the
1525
+ :wikipedia:`Icosidodecahedron`.
1526
+
1527
+ INPUT:
1528
+
1529
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
1530
+ approximate ring for the coordinates
1531
+
1532
+ - ``base_ring`` -- the ring in which the coordinates will belong to
1533
+ If it is not provided and ``exact=True`` it will be a the number
1534
+ field `\QQ[\phi]` where `\phi` is the golden ratio and if
1535
+ ``exact=False`` it will be the real double field.
1536
+
1537
+ - ``backend`` -- the backend to use to create the polytope
1538
+
1539
+ EXAMPLES::
1540
+
1541
+ sage: id = polytopes.icosidodecahedron_V2() # long time (6s)
1542
+ sage: id.f_vector() # long time
1543
+ (1, 30, 60, 32, 1)
1544
+ sage: id.base_ring() # long time
1545
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1546
+ with sqrt5 = 2.236067977499790?
1547
+
1548
+ A much faster implementation using floating point approximations::
1549
+
1550
+ sage: # needs cddexec
1551
+ sage: id = polytopes.icosidodecahedron_V2(exact=False)
1552
+ sage: id.f_vector()
1553
+ (1, 30, 60, 32, 1)
1554
+ sage: id.base_ring()
1555
+ Real Double Field
1556
+
1557
+ Its facets are 20 triangles and 12 regular pentagons::
1558
+
1559
+ sage: # needs cddexec
1560
+ sage: sum(1 for f in id.facets() if len(f.vertices()) == 3)
1561
+ 20
1562
+ sage: sum(1 for f in id.facets() if len(f.vertices()) == 5)
1563
+ 12
1564
+
1565
+ TESTS::
1566
+
1567
+ sage: # optional - pynormaliz, needs sage.rings.number_field
1568
+ sage: id = polytopes.icosidodecahedron_V2(backend='normaliz')
1569
+ sage: id.f_vector()
1570
+ (1, 30, 60, 32, 1)
1571
+ sage: id.base_ring()
1572
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1573
+ with sqrt5 = 2.236067977499790?
1574
+ sage: TestSuite(id).run() # long time
1575
+ """
1576
+ if base_ring is None and exact:
1577
+ from sage.rings.number_field.number_field import QuadraticField
1578
+ K = QuadraticField(5, 'sqrt5')
1579
+ sqrt5 = K.gen()
1580
+ g = (1 + sqrt5) / 2
1581
+ base_ring = K
1582
+ else:
1583
+ if base_ring is None:
1584
+ from sage.rings.real_double import RDF as base_ring
1585
+ g = (1 + base_ring(5).sqrt()) / 2
1586
+
1587
+ pts = [[g, 0, 0], [-g, 0, 0]]
1588
+ pts += [[s1 * base_ring.one() / 2, s2 * g / 2, s3 * (1 + g)/2]
1589
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1590
+ verts = pts
1591
+ verts += [[v[1], v[2], v[0]] for v in pts]
1592
+ verts += [[v[2], v[0], v[1]] for v in pts]
1593
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
1594
+
1595
+ def truncated_dodecahedron(self, exact=True, base_ring=None, backend=None):
1596
+ r"""
1597
+ Return the truncated dodecahedron.
1598
+
1599
+ The truncated dodecahedron is an Archimedean solid.
1600
+ It has 32 faces and 60 vertices. For more information, see the
1601
+ :wikipedia:`Truncated dodecahedron`.
1602
+
1603
+ INPUT:
1604
+
1605
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
1606
+ approximate ring for the coordinates
1607
+
1608
+ - ``base_ring`` -- the ring in which the coordinates will belong to. If
1609
+ it is not provided and ``exact=True`` it will be a the number field
1610
+ `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
1611
+ it will be the real double field.
1612
+
1613
+ - ``backend`` -- the backend to use to create the polytope
1614
+
1615
+ EXAMPLES::
1616
+
1617
+ sage: td = polytopes.truncated_dodecahedron() # needs sage.rings.number_field
1618
+ sage: td.f_vector() # needs sage.rings.number_field
1619
+ (1, 60, 90, 32, 1)
1620
+ sage: td.base_ring() # needs sage.rings.number_field
1621
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1622
+ with sqrt5 = 2.236067977499790?
1623
+
1624
+ Its facets are 20 triangles and 12 regular decagons::
1625
+
1626
+ sage: sum(1 for f in td.facets() if len(f.vertices()) == 3) # needs sage.rings.number_field
1627
+ 20
1628
+ sage: sum(1 for f in td.facets() if len(f.vertices()) == 10) # needs sage.rings.number_field
1629
+ 12
1630
+
1631
+ The faster implementation using floating point approximations does not
1632
+ fully work unfortunately, see https://github.com/cddlib/cddlib/pull/7
1633
+ for a detailed discussion of this case::
1634
+
1635
+ sage: # needs cddexec
1636
+ sage: td = polytopes.truncated_dodecahedron(exact=False) # random
1637
+ doctest:warning
1638
+ ...
1639
+ UserWarning: This polyhedron data is numerically complicated; cdd
1640
+ could not convert between the inexact V and H representation
1641
+ without loss of data. The resulting object might show
1642
+ inconsistencies.
1643
+ sage: td.f_vector()
1644
+ Traceback (most recent call last):
1645
+ ...
1646
+ ValueError: not all vertices are intersections of facets
1647
+ sage: td.base_ring()
1648
+ Real Double Field
1649
+
1650
+ TESTS::
1651
+
1652
+ sage: # optional - pynormaliz, needs sage.rings.number_field
1653
+ sage: td = polytopes.truncated_dodecahedron(backend='normaliz')
1654
+ sage: td.f_vector()
1655
+ (1, 60, 90, 32, 1)
1656
+ sage: td.base_ring()
1657
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1658
+ with sqrt5 = 2.236067977499790?
1659
+ """
1660
+ if base_ring is None and exact:
1661
+ from sage.rings.number_field.number_field import QuadraticField
1662
+ K = QuadraticField(5, 'sqrt5')
1663
+ sqrt5 = K.gen()
1664
+ g = (1 + sqrt5) / 2
1665
+ base_ring = K
1666
+ else:
1667
+ if base_ring is None:
1668
+ from sage.rings.real_double import RDF as base_ring
1669
+ g = (1 + base_ring(5).sqrt()) / 2
1670
+
1671
+ z = base_ring.zero()
1672
+ pts = [[z, s1 * base_ring.one() / g, s2 * (2 + g)]
1673
+ for s1, s2 in itertools.product([1, -1], repeat=2)]
1674
+ pts += [[s1 * base_ring.one() / g, s2 * g, s3 * (2 * g)]
1675
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1676
+ pts += [[s1 * g, s2 * base_ring(2), s3 * (g ** 2)]
1677
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1678
+ verts = pts
1679
+ verts += [[v[1], v[2], v[0]] for v in pts]
1680
+ verts += [[v[2], v[0], v[1]] for v in pts]
1681
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
1682
+
1683
+ def pentakis_dodecahedron(self, exact=True, base_ring=None, backend=None):
1684
+ r"""
1685
+ Return the pentakis dodecahedron.
1686
+
1687
+ The pentakis dodecahedron (orkisdodecahedron) is a face-regular,
1688
+ vertex-uniform polytope dual to the truncated icosahedron. It has 60
1689
+ facets and 32 vertices. See the :wikipedia:`Pentakis_dodecahedron` for more
1690
+ information.
1691
+
1692
+ INPUT:
1693
+
1694
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
1695
+ approximate ring for the coordinates
1696
+
1697
+ - ``base_ring`` -- the ring in which the coordinates will belong to. If
1698
+ it is not provided and ``exact=True`` it will be a the number field
1699
+ `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
1700
+ it will be the real double field.
1701
+
1702
+ - ``backend`` -- the backend to use to create the polytope
1703
+
1704
+ EXAMPLES::
1705
+
1706
+ sage: pd = polytopes.pentakis_dodecahedron() # long time (10s)
1707
+ sage: pd.n_vertices() # long time
1708
+ 32
1709
+ sage: pd.n_inequalities() # long time
1710
+ 60
1711
+
1712
+ A much faster implementation is obtained when setting ``exact=False``::
1713
+
1714
+ sage: pd = polytopes.pentakis_dodecahedron(exact=False) # needs sage.groups
1715
+ sage: pd.n_vertices() # needs sage.groups
1716
+ 32
1717
+ sage: pd.n_inequalities() # needs sage.groups
1718
+ 60
1719
+
1720
+ The 60 are triangles::
1721
+
1722
+ sage: all(len(f.vertices()) == 3 for f in pd.facets()) # needs sage.groups
1723
+ True
1724
+ """
1725
+ return self.buckyball(exact=exact, base_ring=base_ring, backend=backend).polar()
1726
+
1727
+ def Kirkman_icosahedron(self, backend=None):
1728
+ r"""
1729
+ Return the Kirkman icosahedron.
1730
+
1731
+ The Kirkman icosahedron is a 3-polytope with integer coordinates: `(\pm
1732
+ 9, \pm 6, \pm 6)`, `(\pm 12, \pm 4, 0)`, `(0, \pm 12, \pm 8)`, `(\pm 6,
1733
+ 0, \pm 12)`. See [Fe2012]_ for more information.
1734
+
1735
+ INPUT:
1736
+
1737
+ - ``backend`` -- the backend to use to create the polytope
1738
+
1739
+ EXAMPLES::
1740
+
1741
+ sage: ki = polytopes.Kirkman_icosahedron()
1742
+ sage: ki.f_vector()
1743
+ (1, 20, 38, 20, 1)
1744
+
1745
+ sage: ki.volume()
1746
+ 6528
1747
+
1748
+ sage: vertices = ki.vertices()
1749
+ sage: edges = [[vector(edge[0]),vector(edge[1])] for edge in ki.bounded_edges()]
1750
+ sage: edge_lengths = [norm(edge[0]-edge[1]) for edge in edges]
1751
+ sage: sorted(set(edge_lengths))
1752
+ [7, 8, 9, 11, 12, 14, 16]
1753
+
1754
+ TESTS::
1755
+
1756
+ sage: ki_norm = polytopes.Kirkman_icosahedron(backend='normaliz') # optional - pynormaliz
1757
+ sage: TestSuite(ki_norm).run() # optional - pynormaliz
1758
+ """
1759
+ vertices = [[9, 6, 6], [-9, 6, 6], [9, -6, 6], [9, 6, -6],
1760
+ [-9, -6, 6], [-9, 6, -6], [9, -6, -6], [-9, -6, -6],
1761
+ [12, 4, 0], [-12, 4, 0], [12, -4, 0], [-12, -4, 0],
1762
+ [0, 12, 8], [0, -12, 8], [0, 12, -8], [0, -12, -8],
1763
+ [6, 0, 12], [-6, 0, 12], [6, 0, -12], [-6, 0, -12]]
1764
+ return Polyhedron(vertices=vertices, base_ring=ZZ, backend=backend)
1765
+
1766
+ def rhombicosidodecahedron(self, exact=True, base_ring=None, backend=None):
1767
+ r"""
1768
+ Return the rhombicosidodecahedron.
1769
+
1770
+ The rhombicosidodecahedron is an Archimedean solid.
1771
+ It has 62 faces and 60 vertices. For more information, see the
1772
+ :wikipedia:`Rhombicosidodecahedron`.
1773
+
1774
+ INPUT:
1775
+
1776
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
1777
+ approximate ring for the coordinates
1778
+
1779
+ - ``base_ring`` -- the ring in which the coordinates will belong to. If
1780
+ it is not provided and ``exact=True`` it will be a the number field
1781
+ `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
1782
+ it will be the real double field.
1783
+
1784
+ - ``backend`` -- the backend to use to create the polytope
1785
+
1786
+ EXAMPLES::
1787
+
1788
+ sage: rid = polytopes.rhombicosidodecahedron() # long time (6secs)
1789
+ sage: rid.f_vector() # long time
1790
+ (1, 60, 120, 62, 1)
1791
+ sage: rid.base_ring() # long time
1792
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1793
+ with sqrt5 = 2.236067977499790?
1794
+
1795
+ A much faster implementation using floating point approximations::
1796
+
1797
+ sage: # needs cddexec
1798
+ sage: rid = polytopes.rhombicosidodecahedron(exact=False)
1799
+ sage: rid.f_vector()
1800
+ (1, 60, 120, 62, 1)
1801
+ sage: rid.base_ring()
1802
+ Real Double Field
1803
+
1804
+ Its facets are 20 triangles, 30 squares and 12 pentagons::
1805
+
1806
+ sage: # needs cddexec
1807
+ sage: sum(1 for f in rid.facets() if len(f.vertices()) == 3)
1808
+ 20
1809
+ sage: sum(1 for f in rid.facets() if len(f.vertices()) == 4)
1810
+ 30
1811
+ sage: sum(1 for f in rid.facets() if len(f.vertices()) == 5)
1812
+ 12
1813
+
1814
+ TESTS::
1815
+
1816
+ sage: # optional - pynormaliz
1817
+ sage: rid = polytopes.rhombicosidodecahedron(backend='normaliz')
1818
+ sage: rid.f_vector()
1819
+ (1, 60, 120, 62, 1)
1820
+ sage: rid.base_ring()
1821
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1822
+ with sqrt5 = 2.236067977499790?
1823
+ """
1824
+ if base_ring is None and exact:
1825
+ from sage.rings.number_field.number_field import QuadraticField
1826
+ K = QuadraticField(5, 'sqrt5')
1827
+ sqrt5 = K.gen()
1828
+ g = (1 + sqrt5) / 2
1829
+ base_ring = K
1830
+ else:
1831
+ if base_ring is None:
1832
+ from sage.rings.real_double import RDF as base_ring
1833
+ g = (1 + base_ring(5).sqrt()) / 2
1834
+
1835
+ pts = [[s1 * base_ring.one(), s2 * base_ring.one(), s3 * (g**3)]
1836
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1837
+ pts += [[s1 * (g**2), s2 * g, s3 * 2 * g]
1838
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1839
+ pts += [[s1 * (2 + g), 0, s2 * (g**2)]
1840
+ for s1, s2 in itertools.product([1, -1], repeat=2)]
1841
+ # the vertices are all even permutations of the lists in pts
1842
+ verts = pts
1843
+ verts += [[v[1], v[2], v[0]] for v in pts]
1844
+ verts += [[v[2], v[0], v[1]] for v in pts]
1845
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
1846
+
1847
+ def truncated_icosidodecahedron(self, exact=True, base_ring=None, backend=None):
1848
+ r"""
1849
+ Return the truncated icosidodecahedron.
1850
+
1851
+ The truncated icosidodecahedron is an Archimedean solid.
1852
+ It has 62 faces and 120 vertices. For more information, see the
1853
+ :wikipedia:`Truncated_icosidodecahedron`.
1854
+
1855
+ INPUT:
1856
+
1857
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use an
1858
+ approximate ring for the coordinates
1859
+
1860
+ - ``base_ring`` -- the ring in which the coordinates will belong to. If
1861
+ it is not provided and ``exact=True`` it will be a the number field
1862
+ `\QQ[\phi]` where `\phi` is the golden ratio and if ``exact=False``
1863
+ it will be the real double field.
1864
+
1865
+ - ``backend`` -- the backend to use to create the polytope
1866
+
1867
+ EXAMPLES::
1868
+
1869
+ sage: ti = polytopes.truncated_icosidodecahedron() # long time
1870
+ sage: ti.f_vector() # long time
1871
+ (1, 120, 180, 62, 1)
1872
+ sage: ti.base_ring() # long time
1873
+ Number Field in sqrt5 with defining polynomial x^2 - 5
1874
+ with sqrt5 = 2.236067977499790?
1875
+
1876
+ The implementation using floating point approximations is much faster::
1877
+
1878
+ sage: # needs cddexec
1879
+ sage: ti = polytopes.truncated_icosidodecahedron(exact=False) # random
1880
+ sage: ti.f_vector()
1881
+ (1, 120, 180, 62, 1)
1882
+ sage: ti.base_ring()
1883
+ Real Double Field
1884
+
1885
+ Its facets are 30 squares, 20 hexagons and 12 decagons::
1886
+
1887
+ sage: # needs cddexec
1888
+ sage: sum(1 for f in ti.facets() if len(f.vertices()) == 4)
1889
+ 30
1890
+ sage: sum(1 for f in ti.facets() if len(f.vertices()) == 6)
1891
+ 20
1892
+ sage: sum(1 for f in ti.facets() if len(f.vertices()) == 10)
1893
+ 12
1894
+
1895
+ TESTS::
1896
+
1897
+ sage: # optional - pynormaliz
1898
+ sage: ti = polytopes.truncated_icosidodecahedron(backend='normaliz')
1899
+ sage: ti.f_vector()
1900
+ (1, 120, 180, 62, 1)
1901
+ sage: ti.base_ring()
1902
+ Number Field in sqrt5 with defining polynomial x^2 - 5 with sqrt5 = 2.236067977499790?
1903
+ """
1904
+ if base_ring is None and exact:
1905
+ from sage.rings.number_field.number_field import QuadraticField
1906
+ K = QuadraticField(5, 'sqrt5')
1907
+ sqrt5 = K.gen()
1908
+ g = (1 + sqrt5) / 2
1909
+ base_ring = K
1910
+ else:
1911
+ if base_ring is None:
1912
+ from sage.rings.real_double import RDF as base_ring
1913
+ g = (1 + base_ring(5).sqrt()) / 2
1914
+
1915
+ pts = [[s1 * 1 / g, s2 * 1 / g, s3 * (3 + g)]
1916
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1917
+ pts += [[s1 * 2 / g, s2 * g, s3 * (1 + 2 * g)]
1918
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1919
+ pts += [[s1 * 1 / g, s2 * (g**2), s3 * (-1 + 3 * g)]
1920
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1921
+ pts += [[s1 * (-1 + 2 * g), s2 * 2 * base_ring.one(), s3 * (2 + g)]
1922
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1923
+ pts += [[s1 * g, s2 * 3 * base_ring.one(), s3 * 2 * g]
1924
+ for s1, s2, s3 in itertools.product([1, -1], repeat=3)]
1925
+ # the vertices are all ever permutations of the lists in pts
1926
+ verts = pts
1927
+ verts += [[v[1], v[2], v[0]] for v in pts]
1928
+ verts += [[v[2], v[0], v[1]] for v in pts]
1929
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
1930
+
1931
+ def snub_dodecahedron(self, base_ring=None, backend=None, verbose=False):
1932
+ """
1933
+ Return the snub dodecahedron.
1934
+
1935
+ The snub dodecahedron is an Archimedean solid.
1936
+ It has 92 faces and 60 vertices. For more information, see the
1937
+ :wikipedia:`Snub_dodecahedron`.
1938
+
1939
+ INPUT:
1940
+
1941
+ - ``base_ring`` -- the ring in which the coordinates will belong to; if
1942
+ it is not provided it will be the real double field
1943
+
1944
+ - ``backend`` -- the backend to use to create the polytope
1945
+
1946
+ EXAMPLES:
1947
+
1948
+ Only the backend using the optional normaliz package can construct
1949
+ the snub dodecahedron in reasonable time::
1950
+
1951
+ sage: sd = polytopes.snub_dodecahedron(base_ring=AA, # optional - pynormaliz, long time
1952
+ ....: backend='normaliz')
1953
+ sage: sd.f_vector() # optional - pynormaliz, long time
1954
+ (1, 60, 150, 92, 1)
1955
+ sage: sd.base_ring() # optional - pynormaliz, long time
1956
+ Algebraic Real Field
1957
+
1958
+ Its facets are 80 triangles and 12 pentagons::
1959
+
1960
+ sage: sum(1 for f in sd.facets() # optional - pynormaliz, long time
1961
+ ....: if len(f.vertices()) == 3)
1962
+ 80
1963
+ sage: sum(1 for f in sd.facets() # optional - pynormaliz, long time
1964
+ ....: if len(f.vertices()) == 5)
1965
+ 12
1966
+
1967
+ TESTS:
1968
+
1969
+ The cdd backend with floating point arithmetic fails for this
1970
+ polytope::
1971
+
1972
+ sage: sd = polytopes.snub_dodecahedron() # not tested
1973
+ sage: sd.f_vector() # not tested
1974
+ (1, 60, 150, 92, 1)
1975
+ sage: sd.base_ring() # not tested
1976
+ Real Double Field
1977
+ """
1978
+ if base_ring is None:
1979
+ from sage.rings.real_double import RDF as base_ring
1980
+ phi = (1 + base_ring(5).sqrt()) / 2
1981
+ xi = ((phi/2 + (phi - ZZ(5)/27).sqrt()/2)**(~ZZ(3)) +
1982
+ (phi/2 - (phi - ZZ(5)/27).sqrt()/2)**(~ZZ(3)))
1983
+
1984
+ alpha = xi - 1 / xi
1985
+ beta = xi * phi + phi**2 + phi / xi
1986
+ signs = [[-1,-1,-1], [-1,1,1], [1,-1,1], [1,1,-1]]
1987
+
1988
+ pts = [[s1 * 2 * alpha, s2 * 2 * base_ring.one(), s3 * 2 * beta]
1989
+ for s1, s2, s3 in signs]
1990
+ pts += [[s1 * (alpha + beta/phi + phi), s2 * (-alpha * phi + beta + 1/phi), s3 * (alpha/phi + beta * phi - 1)]
1991
+ for s1, s2, s3 in signs]
1992
+ pts += [[s1 * (alpha + beta/phi - phi), s2 * (alpha * phi - beta + 1/phi), s3 * (alpha/phi + beta * phi + 1)]
1993
+ for s1, s2, s3 in signs]
1994
+ pts += [[s1 * (-alpha/phi + beta * phi + 1), s2 * (-alpha + beta/phi - phi), s3 * (alpha * phi + beta - 1/phi)]
1995
+ for s1, s2, s3 in signs]
1996
+ pts += [[s1 * (-alpha/phi + beta * phi - 1), s2 * (alpha - beta/phi - phi), s3 * (alpha * phi + beta + 1/phi)]
1997
+ for s1, s2, s3 in signs]
1998
+
1999
+ # the vertices are all even permutations of the lists in pts
2000
+ verts = pts
2001
+ verts += [[v[1], v[2], v[0]] for v in pts]
2002
+ verts += [[v[2], v[0], v[1]] for v in pts]
2003
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend, verbose=verbose)
2004
+
2005
+ def twenty_four_cell(self, backend=None):
2006
+ """
2007
+ Return the standard 24-cell polytope.
2008
+
2009
+ The 24-cell polyhedron (also called icositetrachoron or octaplex) is a
2010
+ regular polyhedron in 4-dimension. For more information see
2011
+ the :wikipedia:`24-cell`.
2012
+
2013
+ INPUT:
2014
+
2015
+ - ``backend`` -- the backend to use to create the polytope
2016
+
2017
+ EXAMPLES::
2018
+
2019
+ sage: p24 = polytopes.twenty_four_cell()
2020
+ sage: p24.f_vector()
2021
+ (1, 24, 96, 96, 24, 1)
2022
+ sage: v = next(p24.vertex_generator())
2023
+ sage: for adj in v.neighbors(): print(adj)
2024
+ A vertex at (-1/2, -1/2, -1/2, 1/2)
2025
+ A vertex at (-1/2, -1/2, 1/2, -1/2)
2026
+ A vertex at (-1, 0, 0, 0)
2027
+ A vertex at (-1/2, 1/2, -1/2, -1/2)
2028
+ A vertex at (0, -1, 0, 0)
2029
+ A vertex at (0, 0, -1, 0)
2030
+ A vertex at (0, 0, 0, -1)
2031
+ A vertex at (1/2, -1/2, -1/2, -1/2)
2032
+
2033
+ sage: p24.volume()
2034
+ 2
2035
+
2036
+ TESTS::
2037
+
2038
+ sage: tfcell = polytopes.twenty_four_cell(backend='normaliz') # optional - pynormaliz
2039
+ sage: TestSuite(tfcell).run() # optional - pynormaliz
2040
+ """
2041
+ q12 = QQ((1, 2))
2042
+ verts = list(itertools.product([q12, -q12], repeat=4))
2043
+ B4 = (ZZ**4).basis()
2044
+ verts.extend(B4)
2045
+ verts.extend(-v for v in B4)
2046
+ return Polyhedron(vertices=verts, backend=backend)
2047
+
2048
+ def runcitruncated_six_hundred_cell(self, exact=True, backend=None):
2049
+ """
2050
+ Return the runcitruncated 600-cell.
2051
+
2052
+ The runcitruncated 600-cell is a 4-dimensional 4-uniform polytope in
2053
+ the `H_4` family. It has 7200 vertices. For more information see
2054
+ :wikipedia:`Runcitruncated 600-cell`.
2055
+
2056
+ .. WARNING::
2057
+
2058
+ The coordinates are exact by default. The computation with inexact
2059
+ coordinates (using the backend ``'cdd'``) returns a numerical
2060
+ inconsistency error, and thus cannot be computed.
2061
+
2062
+ INPUT:
2063
+
2064
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
2065
+ coordinates instead of floating point approximations
2066
+
2067
+ - ``backend`` -- the backend to use to create the polytope
2068
+
2069
+ EXAMPLES::
2070
+
2071
+ sage: polytopes.runcitruncated_six_hundred_cell(backend='normaliz') # not tested - very long time
2072
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of
2073
+ 7200 vertices
2074
+ """
2075
+ return self.generalized_permutahedron(['H', 4], point=[1, 1, 0, 1], exact=exact, backend=backend, regular=True)
2076
+
2077
+ def cantitruncated_six_hundred_cell(self, exact=True, backend=None):
2078
+ """
2079
+ Return the cantitruncated 600-cell.
2080
+
2081
+ The cantitruncated 600-cell is a 4-dimensional 4-uniform polytope in
2082
+ the `H_4` family. It has 7200 vertices. For more information see
2083
+ :wikipedia:`Cantitruncated 600-cell`.
2084
+
2085
+ .. WARNING::
2086
+
2087
+ The coordinates are exact by default. The computation with inexact
2088
+ coordinates (using the backend ``'cdd'``) returns a numerical
2089
+ inconsistency error, and thus cannot be computed.
2090
+
2091
+ INPUT:
2092
+
2093
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
2094
+ coordinates instead of floating point approximations
2095
+
2096
+ - ``backend`` -- the backend to use to create the polytope
2097
+
2098
+ EXAMPLES::
2099
+
2100
+ sage: polytopes.cantitruncated_six_hundred_cell(exact=True, # not tested - very long time
2101
+ ....: backend='normaliz')
2102
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
2103
+ """
2104
+ return self.generalized_permutahedron(['H', 4], point=[1, 1, 1, 0], exact=exact, backend=backend, regular=True)
2105
+
2106
+ def bitruncated_six_hundred_cell(self, exact=True, backend=None):
2107
+ """
2108
+ Return the bitruncated 600-cell.
2109
+
2110
+ The bitruncated 600-cell is a 4-dimensional 4-uniform polytope in the
2111
+ `H_4` family. It has 3600 vertices. For more information see
2112
+ :wikipedia:`Bitruncated 600-cell`.
2113
+
2114
+ .. WARNING::
2115
+
2116
+ The coordinates are exact by default. The computation with inexact
2117
+ coordinates (using the backend ``'cdd'``) returns a numerical
2118
+ inconsistency error, and thus cannot be computed.
2119
+
2120
+ INPUT:
2121
+
2122
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
2123
+ coordinates instead of floating point approximations
2124
+
2125
+ - ``backend`` -- the backend to use to create the polytope
2126
+
2127
+ EXAMPLES::
2128
+
2129
+ sage: polytopes.runcinated_six_hundred_cell(exact=True, # not tested - very long time
2130
+ ....: backend='normaliz')
2131
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
2132
+ """
2133
+ return self.generalized_permutahedron(['H', 4], point=[0, 1, 1, 0], exact=exact, backend=backend, regular=True)
2134
+
2135
+ def cantellated_six_hundred_cell(self, exact=False, backend=None):
2136
+ """
2137
+ Return the cantellated 600-cell.
2138
+
2139
+ The cantellated 600-cell is a 4-dimensional 4-uniform polytope in the
2140
+ `H_4` family. It has 3600 vertices. For more information see
2141
+ :wikipedia:`Cantellated 600-cell`.
2142
+
2143
+ .. WARNING::
2144
+
2145
+ The coordinates are inexact by default. The computation with
2146
+ inexact coordinates (using the backend ``'cdd'``) issues a
2147
+ UserWarning on inconsistencies.
2148
+
2149
+ INPUT:
2150
+
2151
+ - ``exact`` -- boolean (default: ``False``); if ``True`` use exact
2152
+ coordinates instead of floating point approximations
2153
+
2154
+ - ``backend`` -- the backend to use to create the polytope
2155
+
2156
+ EXAMPLES::
2157
+
2158
+ sage: polytopes.cantellated_six_hundred_cell() # not tested - very long time
2159
+ doctest:warning
2160
+ ...
2161
+ UserWarning: This polyhedron data is numerically complicated; cdd
2162
+ could not convert between the inexact V and H representation
2163
+ without loss of data. The resulting object might show
2164
+ inconsistencies.
2165
+ A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 3600 vertices
2166
+
2167
+ It is possible to use the backend ``'normaliz'`` to get an exact
2168
+ representation::
2169
+
2170
+ sage: polytopes.cantellated_six_hundred_cell(exact=True, # not tested - long time
2171
+ ....: backend='normaliz')
2172
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
2173
+ """
2174
+ return self.generalized_permutahedron(['H', 4], point=[1, 0, 1, 0], exact=exact, backend=backend, regular=True)
2175
+
2176
+ def truncated_six_hundred_cell(self, exact=False, backend=None):
2177
+ """
2178
+ Return the truncated 600-cell.
2179
+
2180
+ The truncated 600-cell is a 4-dimensional 4-uniform polytope in the
2181
+ `H_4` family. It has 1440 vertices. For more information see
2182
+ :wikipedia:`Truncated 600-cell`.
2183
+
2184
+ .. WARNING::
2185
+
2186
+ The coordinates are not exact by default. The computation with
2187
+ exact coordinates takes a huge amount of time.
2188
+
2189
+ INPUT:
2190
+
2191
+ - ``exact`` -- boolean (default: ``False``); if ``True`` use exact
2192
+ coordinates instead of floating point approximations
2193
+
2194
+ - ``backend`` -- the backend to use to create the polytope
2195
+
2196
+ EXAMPLES::
2197
+
2198
+ sage: polytopes.truncated_six_hundred_cell() # not tested - long time
2199
+ A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 1440 vertices
2200
+
2201
+ It is possible to use the backend ``'normaliz'`` to get an exact
2202
+ representation::
2203
+
2204
+ sage: polytopes.truncated_six_hundred_cell(exact=True,backend='normaliz') # not tested, long time (16s)
2205
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 1440 vertices
2206
+ """
2207
+ return self.generalized_permutahedron(['H', 4], point=[1, 1, 0, 0], exact=exact, backend=backend, regular=True)
2208
+
2209
+ def rectified_six_hundred_cell(self, exact=True, backend=None):
2210
+ """
2211
+ Return the rectified 600-cell.
2212
+
2213
+ The rectified 600-cell is a 4-dimensional 4-uniform polytope in the
2214
+ `H_4` family. It has 720 vertices. For more information see
2215
+ :wikipedia:`Rectified 600-cell`.
2216
+
2217
+ .. WARNING::
2218
+
2219
+ The coordinates are exact by default. The computation with inexact
2220
+ coordinates (using the backend ``'cdd'``) returns a numerical
2221
+ inconsistency error, and thus cannot be computed.
2222
+
2223
+ INPUT:
2224
+
2225
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
2226
+ coordinates instead of floating point approximations
2227
+
2228
+ - ``backend`` -- the backend to use to create the polytope
2229
+
2230
+ EXAMPLES::
2231
+
2232
+ sage: polytopes.rectified_six_hundred_cell(backend='normaliz') # not tested, long time (14s)
2233
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 720 vertices
2234
+ """
2235
+ return self.generalized_permutahedron(['H', 4], point=[0, 1, 0, 0], exact=exact, backend=backend, regular=True)
2236
+
2237
+ def six_hundred_cell(self, exact=False, backend=None):
2238
+ """
2239
+ Return the standard 600-cell polytope.
2240
+
2241
+ The 600-cell is a 4-dimensional regular polytope. In many ways this is
2242
+ an analogue of the icosahedron.
2243
+
2244
+ .. WARNING::
2245
+
2246
+ The coordinates are not exact by default. The computation with
2247
+ exact coordinates takes a huge amount of time.
2248
+
2249
+ INPUT:
2250
+
2251
+ - ``exact`` -- boolean (default: ``False``); if ``True`` use exact
2252
+ coordinates instead of floating point approximations
2253
+
2254
+ - ``backend`` -- the backend to use to create the polytope
2255
+
2256
+ EXAMPLES::
2257
+
2258
+ sage: p600 = polytopes.six_hundred_cell(); p600 # needs sage.groups
2259
+ A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 120 vertices
2260
+ sage: p600.f_vector() # long time (~2sec) # needs sage.groups
2261
+ (1, 120, 720, 1200, 600, 1)
2262
+
2263
+ Computation with exact coordinates is currently too long to be useful::
2264
+
2265
+ sage: p600 = polytopes.six_hundred_cell(exact=True) # long time, not tested, needs sage.groups
2266
+ sage: len(list(p600.bounded_edges())) # long time, not tested, needs sage.groups
2267
+ 720
2268
+
2269
+ TESTS::
2270
+
2271
+ sage: # optional - pynormaliz, needs sage.groups sage.rings.number_field
2272
+ sage: p600 = polytopes.six_hundred_cell(exact=True,
2273
+ ....: backend='normaliz')
2274
+ sage: len(list(p600.bounded_edges())) # long time
2275
+ 720
2276
+ """
2277
+ if exact:
2278
+ from sage.rings.number_field.number_field import QuadraticField
2279
+ K = QuadraticField(5, 'sqrt5')
2280
+ sqrt5 = K.gen()
2281
+ g = (1 + sqrt5) / 2
2282
+ base_ring = K
2283
+ else:
2284
+ from sage.rings.real_double import RDF as base_ring
2285
+ g = (1 + base_ring(5).sqrt()) / 2
2286
+
2287
+ q12 = base_ring(1) / base_ring(2)
2288
+ z = base_ring.zero()
2289
+ verts = [[s1*q12, s2*q12, s3*q12, s4*q12] for s1,s2,s3,s4 in itertools.product([1,-1], repeat=4)]
2290
+ V = (base_ring)**4
2291
+ verts.extend(V.basis())
2292
+ verts.extend(-v for v in V.basis())
2293
+ pts = [[s1 * q12, s2*g/2, s3/(2*g), z] for (s1,s2,s3) in itertools.product([1,-1], repeat=3)]
2294
+ for p in AlternatingGroup(4):
2295
+ verts.extend(p(x) for x in pts)
2296
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
2297
+
2298
+ def grand_antiprism(self, exact=True, backend=None, verbose=False):
2299
+ """
2300
+ Return the grand antiprism.
2301
+
2302
+ The grand antiprism is a 4-dimensional non-Wythoffian uniform polytope.
2303
+ The coordinates were taken from http://eusebeia.dyndns.org/4d/gap. For
2304
+ more information, see the :wikipedia:`Grand_antiprism`.
2305
+
2306
+ .. WARNING::
2307
+
2308
+ The coordinates are exact by default. The computation with exact
2309
+ coordinates is not as fast as with floating point approximations.
2310
+ If you find this method to be too slow, consider using floating
2311
+ point approximations
2312
+
2313
+ INPUT:
2314
+
2315
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use floating
2316
+ point approximations instead of exact coordinates
2317
+
2318
+ - ``backend`` -- the backend to use to create the polytope
2319
+
2320
+ EXAMPLES::
2321
+
2322
+ sage: gap = polytopes.grand_antiprism() # not tested - very long time
2323
+ sage: gap # not tested - very long time
2324
+ A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
2325
+ polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
2326
+ the convex hull of 100 vertices
2327
+
2328
+ Computation with the backend ``'normaliz'`` is instantaneous::
2329
+
2330
+ sage: gap_norm = polytopes.grand_antiprism(backend='normaliz') # optional - pynormaliz, needs sage.rings.number_field
2331
+ sage: gap_norm # optional - pynormaliz, needs sage.rings.number_field
2332
+ A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
2333
+ polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
2334
+ the convex hull of 100 vertices
2335
+
2336
+ Computation with approximated coordinates is also faster, but inexact::
2337
+
2338
+ sage: # needs cddexec
2339
+ sage: gap = polytopes.grand_antiprism(exact=False) # random
2340
+ sage: gap
2341
+ A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 100 vertices
2342
+ sage: gap.f_vector()
2343
+ (1, 100, 500, 720, 320, 1)
2344
+ sage: len(list(gap.bounded_edges()))
2345
+ 500
2346
+ """
2347
+ from itertools import product
2348
+
2349
+ if exact:
2350
+ from sage.rings.number_field.number_field import QuadraticField
2351
+ K = QuadraticField(5, 'sqrt5')
2352
+ sqrt5 = K.gen()
2353
+ g = (1 + sqrt5) / 2
2354
+ base_ring = K
2355
+ else:
2356
+ from sage.rings.real_double import RDF as base_ring
2357
+ g = (1 + base_ring(5).sqrt()) / 2
2358
+
2359
+ q12 = base_ring(1) / base_ring(2)
2360
+ z = base_ring.zero()
2361
+ verts = [[s1*q12, s2*q12, s3*q12, s4*q12] for s1,s2,s3,s4 in product([1,-1], repeat=4)]
2362
+ V = (base_ring)**4
2363
+ verts.extend(V.basis()[2:])
2364
+ verts.extend(-v for v in V.basis()[2:])
2365
+
2366
+ verts.extend([s1 * q12, s2/(2*g), s3*g/2, z] for (s1,s2,s3) in product([1,-1], repeat=3))
2367
+ verts.extend([s3*g/2, s1 * q12, s2/(2*g), z] for (s1,s2,s3) in product([1,-1], repeat=3))
2368
+ verts.extend([s2/(2*g), s3*g/2, s1 * q12, z] for (s1,s2,s3) in product([1,-1], repeat=3))
2369
+
2370
+ verts.extend([s1 * q12, s2*g/2, z, s3/(2*g)] for (s1,s2,s3) in product([1,-1], repeat=3))
2371
+ verts.extend([s3/(2*g), s1 * q12, z, s2*g/2] for (s1,s2,s3) in product([1,-1], repeat=3))
2372
+ verts.extend([s2*g/2, s3/(2*g), z, s1 * q12] for (s1,s2,s3) in product([1,-1], repeat=3))
2373
+
2374
+ verts.extend([s1 * q12, z, s2/(2*g), s3*g/2] for (s1,s2,s3) in product([1,-1], repeat=3))
2375
+
2376
+ verts.extend([z, s1 * q12, s2*g/2, s3/(2*g)] for (s1,s2,s3) in product([1,-1], repeat=3))
2377
+
2378
+ verts.extend([z, s1/(2*g), q12, g/2] for s1 in [1, -1])
2379
+ verts.extend([z, s1/(2*g), -q12, -g/2] for s1 in [1, -1])
2380
+
2381
+ verts.extend([z, s1*g/2, 1/(2*g), q12] for s1 in [1, -1])
2382
+ verts.extend([z, s1*g/2, -1/(2*g), -q12] for s1 in [1, -1])
2383
+
2384
+ verts.extend([s1*g/2, z, q12, -1/(2*g)] for s1 in [1, -1])
2385
+ verts.extend([s1*g/2, z, -q12, 1/(2*g)] for s1 in [1, -1])
2386
+
2387
+ verts.extend([s1/(2*g), z, g/2, -q12] for s1 in [1, -1])
2388
+ verts.extend([s1/(2*g), z, -g/2, q12] for s1 in [1, -1])
2389
+
2390
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend, verbose=verbose)
2391
+
2392
+ def Gosset_3_21(self, backend=None):
2393
+ r"""
2394
+ Return the Gosset `3_{21}` polytope.
2395
+
2396
+ The Gosset `3_{21}` polytope is a uniform 7-polytope. It has 56
2397
+ vertices, and 702 facets: `126` `3_{11}` and `576` `6`-simplex. For
2398
+ more information, see the :wikipedia:`3_21_polytope`.
2399
+
2400
+ INPUT:
2401
+
2402
+ - ``backend`` -- the backend to use to create the polytope
2403
+
2404
+ EXAMPLES::
2405
+
2406
+ sage: g = polytopes.Gosset_3_21(); g
2407
+ A 7-dimensional polyhedron in ZZ^8 defined as the convex hull of 56 vertices
2408
+ sage: g.f_vector() # not tested (~16s)
2409
+ (1, 56, 756, 4032, 10080, 12096, 6048, 702, 1)
2410
+
2411
+ TESTS::
2412
+
2413
+ sage: G321 = polytopes.Gosset_3_21(backend='normaliz') # optional - pynormaliz
2414
+ sage: TestSuite(G321).run() # optional - pynormaliz, long time
2415
+ """
2416
+ from itertools import combinations
2417
+ verts = []
2418
+ for i, j in combinations(range(8), 2):
2419
+ x = [1]*8
2420
+ x[i] = x[j] = -3
2421
+ verts.append(x)
2422
+ verts.append([-xx for xx in x])
2423
+
2424
+ return Polyhedron(vertices=verts, base_ring=ZZ, backend=backend)
2425
+
2426
+ def cyclic_polytope(self, dim, n, base_ring=QQ, backend=None):
2427
+ r"""
2428
+ Return a cyclic polytope.
2429
+
2430
+ A cyclic polytope of dimension ``dim`` with ``n`` vertices is the
2431
+ convex hull of the points ``(t,t^2,...,t^dim)`` with `t \in
2432
+ \{0,1,...,n-1\}` . For more information, see the
2433
+ :wikipedia:`Cyclic_polytope`.
2434
+
2435
+ INPUT:
2436
+
2437
+ - ``dim`` -- positive integer; the dimension of the polytope
2438
+
2439
+ - ``n`` -- positive integer; the number of vertices
2440
+
2441
+ - ``base_ring`` -- either ``QQ`` (default) or ``RDF``
2442
+
2443
+ - ``backend`` -- the backend to use to create the polytope
2444
+
2445
+ EXAMPLES::
2446
+
2447
+ sage: c = polytopes.cyclic_polytope(4,10)
2448
+ sage: c.f_vector()
2449
+ (1, 10, 45, 70, 35, 1)
2450
+
2451
+ TESTS::
2452
+
2453
+ sage: cp = polytopes.cyclic_polytope(4,10,backend='normaliz') # optional - pynormaliz
2454
+ sage: TestSuite(cp).run() # optional - pynormaliz
2455
+ """
2456
+ verts = [[t**i for i in range(1, dim+1)] for t in range(n)]
2457
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
2458
+
2459
+ def hypersimplex(self, dim, k, project=False, backend=None):
2460
+ r"""
2461
+ Return the hypersimplex in dimension ``dim`` and parameter ``k``.
2462
+
2463
+ The hypersimplex `\Delta_{d,k}` is the convex hull of the vertices made
2464
+ of `k` ones and `d-k` zeros. It lies in the `d-1` hyperplane of vectors
2465
+ of sum `k`. If you want a projected version to `\RR^{d-1}` (with
2466
+ floating point coordinates) then set ``project=True`` in the options.
2467
+
2468
+ .. SEEALSO::
2469
+
2470
+ :meth:`simplex`
2471
+
2472
+ INPUT:
2473
+
2474
+ - ``dim`` -- the dimension
2475
+
2476
+ - ``n`` -- the numbers ``(1,...,n)`` are permuted
2477
+
2478
+ - ``project`` -- boolean (default: ``False``); if ``True``, the polytope
2479
+ is (isometrically) projected to a vector space of dimension
2480
+ ``dim-1``. This operation turns the coordinates into floating point
2481
+ approximations and corresponds to the projection given by the matrix
2482
+ from :func:`zero_sum_projection`.
2483
+
2484
+ - ``backend`` -- the backend to use to create the polytope
2485
+
2486
+ EXAMPLES::
2487
+
2488
+ sage: # needs sage.combinat
2489
+ sage: h_4_2 = polytopes.hypersimplex(4, 2)
2490
+ sage: h_4_2
2491
+ A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 6 vertices
2492
+ sage: h_4_2.f_vector()
2493
+ (1, 6, 12, 8, 1)
2494
+ sage: h_4_2.ehrhart_polynomial() # optional - latte_int
2495
+ 2/3*t^3 + 2*t^2 + 7/3*t + 1
2496
+ sage: TestSuite(h_4_2).run()
2497
+
2498
+ sage: # needs sage.combinat
2499
+ sage: h_7_3 = polytopes.hypersimplex(7, 3, project=True)
2500
+ sage: h_7_3
2501
+ A 6-dimensional polyhedron in RDF^6 defined as the convex hull of 35 vertices
2502
+ sage: h_7_3.f_vector()
2503
+ (1, 35, 210, 350, 245, 84, 14, 1)
2504
+ sage: TestSuite(h_7_3).run(skip=["_test_pyramid", "_test_lawrence"])
2505
+ """
2506
+ verts = Permutations([0] * (dim - k) + [1] * k).list()
2507
+ if project:
2508
+ verts = project_points(*verts)
2509
+ return Polyhedron(vertices=verts, backend=backend)
2510
+
2511
+ def permutahedron(self, n, project=False, backend=None):
2512
+ r"""
2513
+ Return the standard permutahedron of (1,...,n).
2514
+
2515
+ The permutahedron (or permutohedron) is the convex hull of the
2516
+ permutations of `\{1,\ldots,n\}` seen as vectors. The edges
2517
+ between the permutations correspond to multiplication on the
2518
+ right by an elementary transposition in the
2519
+ :class:`~sage.groups.perm_gps.permgroup_named.SymmetricGroup`.
2520
+
2521
+ If we take the graph in which the vertices correspond to
2522
+ vertices of the polyhedron, and edges to edges, we get the
2523
+ :meth:`~sage.graphs.graph_generators.GraphGenerators.BubbleSortGraph`.
2524
+
2525
+ INPUT:
2526
+
2527
+ - ``n`` -- integer
2528
+
2529
+ - ``project`` -- boolean (default: ``False``); if ``True``, the polytope
2530
+ is (isometrically) projected to a vector space of dimension
2531
+ ``dim-1``. This operation turns the coordinates into floating point
2532
+ approximations and corresponds to the projection given by the matrix
2533
+ from :func:`zero_sum_projection`.
2534
+
2535
+ - ``backend`` -- the backend to use to create the polytope
2536
+
2537
+ EXAMPLES::
2538
+
2539
+ sage: perm4 = polytopes.permutahedron(4); perm4
2540
+ A 3-dimensional polyhedron in ZZ^4 defined as the convex hull of 24 vertices
2541
+ sage: perm4.is_lattice_polytope()
2542
+ True
2543
+ sage: perm4.ehrhart_polynomial() # optional - latte_int
2544
+ 16*t^3 + 15*t^2 + 6*t + 1
2545
+
2546
+ sage: # needs cddexec
2547
+ sage: perm4 = polytopes.permutahedron(4, project=True); perm4
2548
+ A 3-dimensional polyhedron in RDF^3 defined as the convex hull of 24 vertices
2549
+ sage: perm4.plot() # needs sage.plot
2550
+ Graphics3d Object
2551
+ sage: perm4.graph().is_isomorphic(graphs.BubbleSortGraph(4)) # needs sage.graphs
2552
+ True
2553
+
2554
+ As both Hrepresentation and Vrepresentation are known, the permutahedron can be set
2555
+ up with both using the backend ``field``. The following takes very very long time
2556
+ to recompute, e.g. with backend ``ppl``::
2557
+
2558
+ sage: polytopes.permutahedron(8, backend='field') # (~1s)
2559
+ A 7-dimensional polyhedron in QQ^8 defined as the convex hull of 40320 vertices
2560
+ sage: polytopes.permutahedron(9, backend='field') # not tested (memory consumption) # (~5s)
2561
+ A 8-dimensional polyhedron in QQ^9 defined as the convex hull of 362880 vertices
2562
+
2563
+ .. SEEALSO::
2564
+
2565
+ * :meth:`~sage.graphs.graph_generators.GraphGenerators.BubbleSortGraph`
2566
+
2567
+ TESTS::
2568
+
2569
+ sage: p4 = polytopes.permutahedron(4, backend='normaliz') # optional - pynormaliz
2570
+ sage: TestSuite(p4).run() # optional - pynormaliz
2571
+
2572
+ Check that precomputed data is correct::
2573
+
2574
+ sage: P = polytopes.permutahedron(5, backend='field')
2575
+ sage: TestSuite(P).run() # long time
2576
+ """
2577
+ verts = itertools.permutations(range(1, n + 1))
2578
+ if project:
2579
+ verts = project_points(*verts)
2580
+ return Polyhedron(vertices=verts, backend=backend)
2581
+ else:
2582
+ parent = Polyhedra(ZZ, n, backend=backend)
2583
+
2584
+ def tri(m):
2585
+ return (m * (m + 1)) // 2
2586
+
2587
+ # Each proper `S \subset [n]` corresponds exactly to
2588
+ # a facet that minimizes the coordinates in `S`.
2589
+ # The minimal sum for `m` coordinates is `(m*(m+1))/2`.
2590
+ ieqs = ((-tri(sum(x)),) + x
2591
+ for x in itertools.product([0,1], repeat=n)
2592
+ if 0 < sum(x) < n)
2593
+
2594
+ # Adding the defining equality.
2595
+ eqns = ((-tri(n),) + tuple(1 for _ in range(n)),)
2596
+
2597
+ return parent([verts, [], []], [ieqs, eqns],
2598
+ Vrep_minimal=True, Hrep_minimal=True, pref_rep='Hrep')
2599
+
2600
+ def generalized_permutahedron(self, coxeter_type, point=None, exact=True, regular=False, backend=None):
2601
+ r"""
2602
+ Return the generalized permutahedron of type ``coxeter_type`` as the
2603
+ convex hull of the orbit of ``point`` in the fundamental cone.
2604
+
2605
+ This generalized permutahedron lies in the vector space used in the
2606
+ geometric representation, that is, in the default case, the dimension
2607
+ of generalized permutahedron equals the dimension of the space.
2608
+
2609
+ INPUT:
2610
+
2611
+ - ``coxeter_type`` -- a Coxeter type; given as a pair [type,rank],
2612
+ where type is a letter and rank is the number of generators
2613
+
2614
+ - ``point`` -- list (default: ``None``); a point given by its
2615
+ coordinates in the weight basis. If ``None`` is given, the point
2616
+ `(1, 1, 1, \ldots)` is used.
2617
+
2618
+ - ``exact`` -- boolean (default: ``True``); if ``False`` use floating
2619
+ point approximations instead of exact coordinates
2620
+
2621
+ - ``regular`` -- boolean (default: ``False``); whether to apply a
2622
+ linear transformation making the vertex figures isometric
2623
+
2624
+ - ``backend`` -- backend to use to create the polytope; (default:
2625
+ ``None``)
2626
+
2627
+ EXAMPLES::
2628
+
2629
+ sage: perm_a3 = polytopes.generalized_permutahedron(['A',3]); perm_a3 # needs sage.combinat
2630
+ A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 24 vertices
2631
+
2632
+ You can put the starting point along the hyperplane of the first
2633
+ generator::
2634
+
2635
+ sage: # needs sage.combinat
2636
+ sage: perm_a3_011 = polytopes.generalized_permutahedron(['A',3], [0,1,1])
2637
+ sage: perm_a3_011
2638
+ A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
2639
+ sage: perm_a3_110 = polytopes.generalized_permutahedron(['A',3], [1,1,0])
2640
+ sage: perm_a3_110
2641
+ A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
2642
+ sage: perm_a3_110.is_combinatorially_isomorphic(perm_a3_011)
2643
+ True
2644
+ sage: perm_a3_101 = polytopes.generalized_permutahedron(['A',3], [1,0,1])
2645
+ sage: perm_a3_101
2646
+ A 3-dimensional polyhedron in QQ^3 defined as the convex hull of 12 vertices
2647
+ sage: perm_a3_110.is_combinatorially_isomorphic(perm_a3_101)
2648
+ False
2649
+ sage: perm_a3_011.f_vector()
2650
+ (1, 12, 18, 8, 1)
2651
+ sage: perm_a3_101.f_vector()
2652
+ (1, 12, 24, 14, 1)
2653
+
2654
+ The usual output does not necessarily give a polyhedron with isometric
2655
+ vertex figures::
2656
+
2657
+ sage: perm_a2 = polytopes.generalized_permutahedron(['A',2]) # needs sage.combinat
2658
+ sage: perm_a2.vertices() # needs sage.combinat
2659
+ (A vertex at (-1, -1),
2660
+ A vertex at (-1, 0),
2661
+ A vertex at (0, -1),
2662
+ A vertex at (0, 1),
2663
+ A vertex at (1, 0),
2664
+ A vertex at (1, 1))
2665
+
2666
+ It works also with Coxeter types that lead to non-rational coordinates::
2667
+
2668
+ sage: perm_b3 = polytopes.generalized_permutahedron(['B',3]) # long time, needs sage.combinat sage.rings.number_field
2669
+ sage: perm_b3 # long time, needs sage.combinat sage.rings.number_field
2670
+ A 3-dimensional polyhedron in
2671
+ (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^3
2672
+ defined as the convex hull of 48 vertices
2673
+
2674
+ Setting ``regular=True`` applies a linear transformation to get
2675
+ isometric vertex figures and the result is inscribed. This cannot be done using
2676
+ rational coordinates. We first do the computations using floating point
2677
+ approximations (``RDF``)::
2678
+
2679
+ sage: perm_a2_inexact = polytopes.generalized_permutahedron( # needs sage.combinat
2680
+ ....: ['A',2], exact=False)
2681
+ sage: sorted(perm_a2_inexact.vertices()) # needs sage.combinat
2682
+ [A vertex at (-1.0, -1.0),
2683
+ A vertex at (-1.0, 0.0),
2684
+ A vertex at (0.0, -1.0),
2685
+ A vertex at (0.0, 1.0),
2686
+ A vertex at (1.0, 0.0),
2687
+ A vertex at (1.0, 1.0)]
2688
+
2689
+ sage: perm_a2_inexact_reg = polytopes.generalized_permutahedron( # needs sage.combinat
2690
+ ....: ['A',2], exact=False, regular=True)
2691
+ sage: sorted(perm_a2_inexact_reg.vertices()) # needs sage.combinat
2692
+ [A vertex at (-1.0, 0.0),
2693
+ A vertex at (-0.5, -0.8660254038),
2694
+ A vertex at (-0.5, 0.8660254038),
2695
+ A vertex at (0.5, -0.8660254038),
2696
+ A vertex at (0.5, 0.8660254038),
2697
+ A vertex at (1.0, 0.0)]
2698
+
2699
+ We can do the same computation using exact arithmetic with the field ``AA``::
2700
+
2701
+ sage: perm_a2_reg = polytopes.generalized_permutahedron( # needs sage.combinat sage.rings.number_field
2702
+ ....: ['A',2], regular=True)
2703
+ sage: V = sorted(perm_a2_reg.vertices()); V # random # needs sage.combinat sage.rings.number_field
2704
+ [A vertex at (-1, 0),
2705
+ A vertex at (-1/2, -0.866025403784439?),
2706
+ A vertex at (-1/2, 0.866025403784439?),
2707
+ A vertex at (1/2, -0.866025403784439?),
2708
+ A vertex at (1/2, 0.866025403784439?),
2709
+ A vertex at (1.000000000000000?, 0.?e-18)]
2710
+
2711
+ Even though the numbers look like floating point approximations, the computation is
2712
+ actually exact. We can clean up the display a bit using ``exactify``::
2713
+
2714
+ sage: for v in V: # needs sage.combinat sage.rings.number_field
2715
+ ....: for x in v:
2716
+ ....: x.exactify()
2717
+ sage: V # needs sage.combinat sage.rings.number_field
2718
+ [A vertex at (-1, 0),
2719
+ A vertex at (-1/2, -0.866025403784439?),
2720
+ A vertex at (-1/2, 0.866025403784439?),
2721
+ A vertex at (1/2, -0.866025403784439?),
2722
+ A vertex at (1/2, 0.866025403784439?),
2723
+ A vertex at (1, 0)]
2724
+ sage: perm_a2_reg.is_inscribed() # needs sage.combinat sage.rings.number_field
2725
+ True
2726
+
2727
+ Larger examples take longer::
2728
+
2729
+ sage: # needs sage.combinat sage.rings.number_field
2730
+ sage: perm_a3_reg = polytopes.generalized_permutahedron( # long time
2731
+ ....: ['A',3], regular=True); perm_a3_reg
2732
+ A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices
2733
+ sage: perm_a3_reg.is_inscribed() # long time
2734
+ True
2735
+ sage: perm_b3_reg = polytopes.generalized_permutahedron( # long time (12sec on 64 bits), not tested
2736
+ ....: ['B',3], regular=True); perm_b3_reg
2737
+ A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices
2738
+
2739
+ It is faster with the backend ``'number_field'``, which internally uses an embedded
2740
+ number field instead of doing the computations directly with the base ring (``AA``)::
2741
+
2742
+ sage: # needs sage.combinat sage.rings.number_field
2743
+ sage: perm_a3_reg_nf = polytopes.generalized_permutahedron(
2744
+ ....: ['A',3], regular=True, backend='number_field'); perm_a3_reg_nf
2745
+ A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices
2746
+ sage: perm_a3_reg_nf.is_inscribed()
2747
+ True
2748
+ sage: perm_b3_reg_nf = polytopes.generalized_permutahedron( # long time
2749
+ ....: ['B',3], regular=True, backend='number_field'); perm_b3_reg_nf
2750
+ A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices
2751
+
2752
+ It is even faster with the backend ``'normaliz'``::
2753
+
2754
+ sage: # optional - pynormaliz, needs sage.combinat sage.rings.number_field
2755
+ sage: perm_a3_reg_norm = polytopes.generalized_permutahedron(
2756
+ ....: ['A',3], regular=True, backend='normaliz'); perm_a3_reg_norm
2757
+ A 3-dimensional polyhedron in AA^3 defined as the convex hull of 24 vertices
2758
+ sage: perm_a3_reg_norm.is_inscribed()
2759
+ True
2760
+ sage: perm_b3_reg_norm = polytopes.generalized_permutahedron(
2761
+ ....: ['B',3], regular=True, backend='normaliz'); perm_b3_reg_norm
2762
+ A 3-dimensional polyhedron in AA^3 defined as the convex hull of 48 vertices
2763
+
2764
+ The speedups from using backend ``'normaliz'`` allow us to go even further::
2765
+
2766
+ sage: # optional - pynormaliz, needs sage.combinat sage.rings.number_field
2767
+ sage: perm_h3 = polytopes.generalized_permutahedron(
2768
+ ....: ['H',3], backend='normaliz'); perm_h3
2769
+ A 3-dimensional polyhedron in
2770
+ (Number Field in a with defining polynomial x^2 - 5 with a = 2.236067977499790?)^3
2771
+ defined as the convex hull of 120 vertices
2772
+ sage: perm_f4 = polytopes.generalized_permutahedron( # long time
2773
+ ....: ['F',4], backend='normaliz'); perm_f4
2774
+ A 4-dimensional polyhedron
2775
+ in (Number Field in a with defining polynomial x^2 - 2 with a = 1.414213562373095?)^4
2776
+ defined as the convex hull of 1152 vertices
2777
+
2778
+ .. SEEALSO::
2779
+
2780
+ * :meth:`~sage.combinat.root_system.reflection_group_real.permutahedron`
2781
+ * :meth:`~sage.categories.finite_coxeter_groups.permutahedron`
2782
+
2783
+ TESTS::
2784
+
2785
+ sage: TestSuite(perm_h3).run() # optional - pynormaliz # needs sage.combinat sage.rings.number_field
2786
+ """
2787
+ from sage.combinat.root_system.coxeter_group import CoxeterGroup
2788
+ try:
2789
+ W = CoxeterGroup(coxeter_type)
2790
+ except (TypeError, ValueError):
2791
+ raise ValueError("cannot build a Coxeter group from {}".format(coxeter_type))
2792
+ n = W.one().canonical_matrix().rank()
2793
+ weights = W.fundamental_weights()
2794
+ if point is None:
2795
+ point = [ZZ.one()] * n
2796
+ apex = sum(point[i-1] * weights[i] for i in weights.keys())
2797
+ # Try to rationalize the starting point
2798
+ non_zero_index = list(apex).index([x for x in apex if x != 0][0])
2799
+ apex = (QQ(1)/apex[non_zero_index]) * apex
2800
+ apex.set_immutable()
2801
+ vertices = set()
2802
+ # This does not work well with UCF, so we set it to None:
2803
+ # br = apex.base_ring()
2804
+ br = None
2805
+ for w in W:
2806
+ # The apex is considered in the space on which it acts and not in
2807
+ # the weight space.
2808
+ new_point = w * apex
2809
+ new_point.set_immutable()
2810
+ vertices.add(new_point)
2811
+ if regular:
2812
+ from sage.rings.qqbar import AA
2813
+ from sage.matrix.constructor import matrix
2814
+ from sage.modules.free_module_element import vector
2815
+ # This transformation fixes the first root and adjust the other
2816
+ # roots to have the correct angles
2817
+ bf = W.bilinear_form()
2818
+ transf_col = [[1] + [0]*(n-1)]
2819
+ for i in range(1, n):
2820
+ new_col = [0]*i + [1] + [0]*(n-i-1)
2821
+ transf_col += [new_col]
2822
+ m = matrix(AA, transf_col)
2823
+ col = bf.column(i)
2824
+ rhs = vector(AA, list(col[:i+1]))
2825
+ adjusted_col = m.solve_right(rhs)
2826
+ # Then scales the images so that the polytope is inscribed
2827
+ c = 1 - sum(adjusted_col[j]**2 for j in range(n) if j != i)
2828
+ c = c.sqrt()
2829
+ adjusted_col[i] = c
2830
+ transf_col[-1] = adjusted_col
2831
+ # TODO: Make this matrix into the cyclotomics, the value of c is an
2832
+ # algebraic number not anymore in the cyclotomic field.
2833
+ transf = matrix(transf_col).transpose()
2834
+ vertices = [transf * v.change_ring(AA) for v in vertices]
2835
+ br = AA
2836
+ if not exact:
2837
+ from sage.rings.real_double import RDF
2838
+ vertices = [v.change_ring(RDF) for v in vertices]
2839
+ br = RDF
2840
+ return Polyhedron(vertices=vertices, backend=backend, base_ring=br)
2841
+
2842
+ def omnitruncated_one_hundred_twenty_cell(self, exact=True, backend=None):
2843
+ """
2844
+ Return the omnitruncated 120-cell.
2845
+
2846
+ The omnitruncated 120-cell is a 4-dimensional 4-uniform polytope in the
2847
+ `H_4` family. It has 14400 vertices. For more information see
2848
+ :wikipedia:`Omnitruncated 120-cell`.
2849
+
2850
+ .. WARNING::
2851
+
2852
+ The coordinates are exact by default. The computation with inexact
2853
+ coordinates (using the backend ``'cdd'``) returns a numerical
2854
+ inconsistency error, and thus cannot be computed.
2855
+
2856
+ INPUT:
2857
+
2858
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
2859
+ coordinates instead of floating point approximations
2860
+
2861
+ - ``backend`` -- the backend to use to create the polytope
2862
+
2863
+ EXAMPLES::
2864
+
2865
+ sage: polytopes.omnitruncated_one_hundred_twenty_cell(backend='normaliz') # not tested - very long time ~10min
2866
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 14400 vertices
2867
+ """
2868
+ if not exact:
2869
+ # cdd finds a numerical inconsistency.
2870
+ raise NotImplementedError("cannot compute the convex hull using floating points")
2871
+ return self.generalized_permutahedron(['H', 4], exact=exact, backend=backend, regular=True)
2872
+
2873
+ omnitruncated_six_hundred_cell = omnitruncated_one_hundred_twenty_cell
2874
+
2875
+ def runcitruncated_one_hundred_twenty_cell(self, exact=False, backend=None):
2876
+ """
2877
+ Return the runcitruncated 120-cell.
2878
+
2879
+ The runcitruncated 120-cell is a 4-dimensional 4-uniform polytope in
2880
+ the `H_4` family. It has 7200 vertices. For more information see
2881
+ :wikipedia:`Runcitruncated 120-cell`.
2882
+
2883
+ .. WARNING::
2884
+
2885
+ The coordinates are inexact by default. The computation with
2886
+ inexact coordinates (using the backend ``'cdd'``) issues a
2887
+ UserWarning on inconsistencies.
2888
+
2889
+ INPUT:
2890
+
2891
+ - ``exact`` -- boolean (default: ``False``); if ``True`` use exact
2892
+ coordinates instead of floating point approximations
2893
+
2894
+ - ``backend`` -- the backend to use to create the polytope
2895
+
2896
+ EXAMPLES::
2897
+
2898
+ sage: polytopes.runcitruncated_one_hundred_twenty_cell(exact=False) # not tested - very long time
2899
+ doctest:warning
2900
+ ...
2901
+ UserWarning: This polyhedron data is numerically complicated; cdd
2902
+ could not convert between the inexact V and H representation
2903
+ without loss of data. The resulting object might show
2904
+ inconsistencies.
2905
+
2906
+ It is possible to use the backend ``'normaliz'`` to get an exact
2907
+ representation::
2908
+
2909
+ sage: polytopes.runcitruncated_one_hundred_twenty_cell(exact=True, # not tested - very long time
2910
+ ....: backend='normaliz')
2911
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
2912
+ """
2913
+ return self.generalized_permutahedron(['H', 4], point=[1, 0, 1, 1], exact=exact, backend=backend, regular=True)
2914
+
2915
+ def cantitruncated_one_hundred_twenty_cell(self, exact=True, backend=None):
2916
+ """
2917
+ Return the cantitruncated 120-cell.
2918
+
2919
+ The cantitruncated 120-cell is a 4-dimensional 4-uniform polytope in
2920
+ the `H_4` family. It has 7200 vertices. For more information see
2921
+ :wikipedia:`Cantitruncated 120-cell`.
2922
+
2923
+ .. WARNING::
2924
+
2925
+ The coordinates are exact by default. The computation with inexact
2926
+ coordinates (using the backend ``'cdd'``) returns a numerical
2927
+ inconsistency error, and thus cannot be computed.
2928
+
2929
+ INPUT:
2930
+
2931
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
2932
+ coordinates instead of floating point approximations
2933
+
2934
+ - ``backend`` -- the backend to use to create the polytope
2935
+
2936
+ EXAMPLES::
2937
+
2938
+ sage: polytopes.cantitruncated_one_hundred_twenty_cell(exact=True, backend='normaliz') # not tested - very long time
2939
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 7200 vertices
2940
+ """
2941
+ return self.generalized_permutahedron(['H', 4], point=[0, 1, 1, 1], exact=exact, backend=backend, regular=True)
2942
+
2943
+ def runcinated_one_hundred_twenty_cell(self, exact=False, backend=None):
2944
+ """
2945
+ Return the runcinated 120-cell.
2946
+
2947
+ The runcinated 120-cell is a 4-dimensional 4-uniform polytope in the
2948
+ `H_4` family. It has 2400 vertices. For more information see
2949
+ :wikipedia:`Runcinated 120-cell`.
2950
+
2951
+ .. WARNING::
2952
+
2953
+ The coordinates are inexact by default. The computation with
2954
+ inexact coordinates (using the backend ``'cdd'``) issues a
2955
+ UserWarning on inconsistencies.
2956
+
2957
+ INPUT:
2958
+
2959
+ - ``exact`` -- boolean (default: ``False``); if ``True`` use exact
2960
+ coordinates instead of floating point approximations
2961
+
2962
+ - ``backend`` -- the backend to use to create the polytope
2963
+
2964
+ EXAMPLES::
2965
+
2966
+ sage: polytopes.runcinated_one_hundred_twenty_cell(exact=False) # not tested - very long time
2967
+ doctest:warning ... UserWarning: This polyhedron data is
2968
+ numerically complicated; cdd could not convert between the inexact
2969
+ V and H representation without loss of data. The resulting object
2970
+ might show inconsistencies.
2971
+ A 4-dimensional polyhedron in RDF^4 defined as the convex hull of 2400 vertices
2972
+
2973
+ It is possible to use the backend ``'normaliz'`` to get an exact
2974
+ representation::
2975
+
2976
+ sage: polytopes.runcinated_one_hundred_twenty_cell(exact=True, # not tested - very long time
2977
+ ....: backend='normaliz')
2978
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 2400 vertices
2979
+ """
2980
+ return self.generalized_permutahedron(['H', 4], point=[1, 0, 0, 1], exact=exact, backend=backend, regular=True)
2981
+
2982
+ def cantellated_one_hundred_twenty_cell(self, exact=True, backend=None):
2983
+ """
2984
+ Return the cantellated 120-cell.
2985
+
2986
+ The cantellated 120-cell is a 4-dimensional 4-uniform polytope in the
2987
+ `H_4` family. It has 3600 vertices. For more information see
2988
+ :wikipedia:`Cantellated 120-cell`.
2989
+
2990
+ .. WARNING::
2991
+
2992
+ The coordinates are exact by default. The computation with inexact
2993
+ coordinates (using the backend ``'cdd'``) returns a numerical
2994
+ inconsistency error, and thus cannot be computed.
2995
+
2996
+ INPUT:
2997
+
2998
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
2999
+ coordinates instead of floating point approximations
3000
+
3001
+ - ``backend`` -- the backend to use to create the polytope
3002
+
3003
+ EXAMPLES::
3004
+
3005
+ sage: polytopes.cantellated_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
3006
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 3600 vertices
3007
+ """
3008
+ return self.generalized_permutahedron(['H', 4], point=[0, 1, 0, 1], exact=exact, backend=backend, regular=True)
3009
+
3010
+ def truncated_one_hundred_twenty_cell(self, exact=True, backend=None):
3011
+ """
3012
+ Return the truncated 120-cell.
3013
+
3014
+ The truncated 120-cell is a 4-dimensional 4-uniform polytope in the
3015
+ `H_4` family. It has 2400 vertices. For more information see
3016
+ :wikipedia:`Truncated 120-cell`.
3017
+
3018
+ .. WARNING::
3019
+
3020
+ The coordinates are exact by default. The computation with inexact
3021
+ coordinates (using the backend ``'cdd'``) returns a numerical
3022
+ inconsistency error, and thus cannot be computed.
3023
+
3024
+ INPUT:
3025
+
3026
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
3027
+ coordinates instead of floating point approximations
3028
+
3029
+ - ``backend`` -- the backend to use to create the polytope
3030
+
3031
+ EXAMPLES::
3032
+
3033
+ sage: polytopes.truncated_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
3034
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 2400 vertices
3035
+ """
3036
+ return self.generalized_permutahedron(['H', 4], point=[0, 0, 1, 1], exact=exact, backend=backend, regular=True)
3037
+
3038
+ def rectified_one_hundred_twenty_cell(self, exact=True, backend=None):
3039
+ """
3040
+ Return the rectified 120-cell.
3041
+
3042
+ The rectified 120-cell is a 4-dimensional 4-uniform polytope in the
3043
+ `H_4` family. It has 1200 vertices. For more information see
3044
+ :wikipedia:`Rectified 120-cell`.
3045
+
3046
+ .. WARNING::
3047
+
3048
+ The coordinates are exact by default. The computation with inexact
3049
+ coordinates (using the backend ``'cdd'``) returns a numerical
3050
+ inconsistency error, and thus cannot be computed.
3051
+
3052
+ INPUT:
3053
+
3054
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
3055
+ coordinates instead of floating point approximations
3056
+
3057
+ - ``backend`` -- the backend to use to create the polytope
3058
+
3059
+ EXAMPLES::
3060
+
3061
+ sage: polytopes.rectified_one_hundred_twenty_cell(backend='normaliz') # not tested - long time
3062
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 1200 vertices
3063
+ """
3064
+ return self.generalized_permutahedron(['H', 4], point=[0, 0, 1, 0], exact=exact, backend=backend, regular=True)
3065
+
3066
+ def one_hundred_twenty_cell(self, exact=True, backend=None, construction='coxeter'):
3067
+ """
3068
+ Return the 120-cell.
3069
+
3070
+ The 120-cell is a 4-dimensional 4-uniform polytope in the `H_4` family.
3071
+ It has 600 vertices and 120 facets. For more information see
3072
+ :wikipedia:`120-cell`.
3073
+
3074
+ .. WARNING::
3075
+
3076
+ The coordinates are exact by default. The computation with inexact
3077
+ coordinates (using the backend ``'cdd'``) returns a numerical
3078
+ inconsistency error, and thus cannot be computed.
3079
+
3080
+ INPUT:
3081
+
3082
+ - ``exact`` -- boolean (default: ``True``); if ``True`` use exact
3083
+ coordinates instead of floating point approximations
3084
+
3085
+ - ``backend`` -- the backend to use to create the polytope
3086
+
3087
+ - ``construction`` -- string (default: ``'coxeter'``); the construction to use.
3088
+ The other possibility is 'as_permutahedron'.
3089
+
3090
+ EXAMPLES:
3091
+
3092
+ The classical construction given by Coxeter in [Cox1969]_ is given by::
3093
+
3094
+ sage: polytopes.one_hundred_twenty_cell() # not tested, long time (~15s)
3095
+ A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
3096
+ polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
3097
+ the convex hull of 600 vertices
3098
+
3099
+ The ``'normaliz'`` is faster::
3100
+
3101
+ sage: P = polytopes.one_hundred_twenty_cell(backend='normaliz'); P # optional - pynormaliz
3102
+ A 4-dimensional polyhedron in (Number Field in sqrt5 with defining
3103
+ polynomial x^2 - 5 with sqrt5 = 2.236067977499790?)^4 defined as
3104
+ the convex hull of 600 vertices
3105
+
3106
+ It is also possible to realize it using the generalized permutahedron
3107
+ of type `H_4`::
3108
+
3109
+ sage: polytopes.one_hundred_twenty_cell(backend='normaliz', # not tested - long time
3110
+ ....: construction='as_permutahedron')
3111
+ A 4-dimensional polyhedron in AA^4 defined as the convex hull of 600 vertices
3112
+
3113
+ TESTS::
3114
+
3115
+ sage: TestSuite(P).run() # long time, optional - pynormaliz
3116
+ """
3117
+ if construction == 'coxeter':
3118
+ if not exact:
3119
+ raise ValueError("The 'cdd' backend produces numerical inconsistencies, use 'exact=True'.")
3120
+ from sage.rings.number_field.number_field import QuadraticField
3121
+ base_ring = QuadraticField(5, 'sqrt5')
3122
+ sqrt5 = base_ring.gen()
3123
+ phi = (1 + sqrt5) / 2
3124
+ phi_inv = base_ring.one() / phi
3125
+
3126
+ # The 24 permutations of [0,0,±2,±2] (the ± are independent)
3127
+ verts = Permutations([0,0,2,2]).list() + Permutations([0,0,-2,-2]).list() + Permutations([0,0,2,-2]).list()
3128
+
3129
+ # The 64 permutations of the following vectors:
3130
+ # [±1,±1,±1,±sqrt(5)]
3131
+ # [±1/phi^2,±phi,±phi,±phi]
3132
+ # [±1/phi,±1/phi,±1/phi,±phi^2]
3133
+ from sage.categories.cartesian_product import cartesian_product
3134
+ full_perm_vectors = [[[1,-1],[1,-1],[1,-1],[-sqrt5,sqrt5]],
3135
+ [[phi_inv**2,-phi_inv**2],[phi,-phi],[phi,-phi],[-phi,phi]],
3136
+ [[phi_inv,-phi_inv],[phi_inv,-phi_inv],[phi_inv,-phi_inv],[-(phi**2),phi**2]]]
3137
+ for vect in full_perm_vectors:
3138
+ cp = cartesian_product(vect)
3139
+ # The group action creates duplicates, so we reduce it:
3140
+ verts += list({tuple(p) for c in cp
3141
+ for p in Permutations(list(c))})
3142
+
3143
+ # The 96 even permutations of [0,±1/phi^2,±1,±phi^2]
3144
+ # The 96 even permutations of [0,±1/phi,±phi,±sqrt(5)]
3145
+ # The 192 even permutations of [±1/phi,±1,±phi,±2]
3146
+ even_perm_vectors = [[[0],[phi_inv**2,-phi_inv**2],[1,-1],[-(phi**2),phi**2]],
3147
+ [[0],[phi_inv,-phi_inv],[phi,-phi],[-sqrt5,sqrt5]],
3148
+ [[phi_inv,-phi_inv],[1,-1],[phi,-phi],[-2,2]]]
3149
+ even_perm = AlternatingGroup(4)
3150
+ for vect in even_perm_vectors:
3151
+ cp = cartesian_product(vect)
3152
+ verts += [p(tuple(c)) for p in even_perm for c in cp]
3153
+
3154
+ return Polyhedron(vertices=verts, base_ring=base_ring, backend=backend)
3155
+
3156
+ elif construction == 'as_permutahedron':
3157
+ return self.generalized_permutahedron(['H', 4], point=[0, 0, 0, 1], exact=exact, backend=backend, regular=True)
3158
+ else:
3159
+ raise ValueError("construction (={}) must be either 'coxeter' or 'as_permutahedron' ".format(construction))
3160
+
3161
+ def hypercube(self, dim, intervals=None, backend=None):
3162
+ r"""
3163
+ Return a hypercube of the given dimension.
3164
+
3165
+ The ``dim``-dimensional hypercube is by default the convex hull of the
3166
+ `2^{\text{dim}}` `\pm 1` vectors of length ``dim``. Alternatively,
3167
+ it is the product of ``dim`` line segments given in the ``intervals``.
3168
+ For more information see the wikipedia article :wikipedia:`Hypercube`.
3169
+
3170
+ INPUT:
3171
+
3172
+ - ``dim`` -- integer; the dimension of the hypercube
3173
+
3174
+ - ``intervals`` -- (default: ``None``) it takes the following
3175
+ possible inputs:
3176
+
3177
+ - If ``None`` (the default), it returns the `\pm 1`-cube of
3178
+ dimension ``dim``.
3179
+
3180
+ - ``'zero_one'`` -- string; return the `0/1`-cube
3181
+
3182
+ - a list of length ``dim``. Its elements are pairs of
3183
+ numbers `(a,b)` with `a < b`. The cube will be the product of
3184
+ these intervals.
3185
+
3186
+ - ``backend`` -- the backend to use to create the polytope
3187
+
3188
+ EXAMPLES:
3189
+
3190
+ Create the `\pm 1`-hypercube of dimension 4::
3191
+
3192
+ sage: four_cube = polytopes.hypercube(4)
3193
+ sage: four_cube.is_simple()
3194
+ True
3195
+ sage: four_cube.base_ring()
3196
+ Integer Ring
3197
+ sage: four_cube.volume()
3198
+ 16
3199
+ sage: four_cube.ehrhart_polynomial() # optional - latte_int
3200
+ 16*t^4 + 32*t^3 + 24*t^2 + 8*t + 1
3201
+
3202
+ Return the `0/1`-hypercube of dimension 4::
3203
+
3204
+ sage: z_cube = polytopes.hypercube(4, intervals='zero_one')
3205
+ sage: z_cube.vertices()[0]
3206
+ A vertex at (1, 0, 1, 1)
3207
+ sage: z_cube.is_simple()
3208
+ True
3209
+ sage: z_cube.base_ring()
3210
+ Integer Ring
3211
+ sage: z_cube.volume()
3212
+ 1
3213
+ sage: z_cube.ehrhart_polynomial() # optional - latte_int
3214
+ t^4 + 4*t^3 + 6*t^2 + 4*t + 1
3215
+
3216
+ Return the 4-dimensional combinatorial cube that is the product of
3217
+ [0,3]^4::
3218
+
3219
+ sage: t_cube = polytopes.hypercube(4, intervals=[[0,3]]*4)
3220
+
3221
+ Checking that t_cube is three times the previous `0/1`-cube::
3222
+
3223
+ sage: t_cube == 3 * z_cube
3224
+ True
3225
+
3226
+ TESTS::
3227
+
3228
+ sage: fc = polytopes.hypercube(4,backend='normaliz') # optional - pynormaliz
3229
+ sage: TestSuite(fc).run() # optional - pynormaliz
3230
+
3231
+ ::
3232
+
3233
+ sage: ls = [randint(-100,100) for _ in range(4)]
3234
+ sage: intervals = [[x, x+randint(1,50)] for x in ls]
3235
+ sage: P = polytopes.hypercube(4, intervals, backend='field')
3236
+ sage: TestSuite(P).run()
3237
+
3238
+ Check that :issue:`29904` is fixed::
3239
+
3240
+ sage: intervals = [[-2,2]]
3241
+ sage: P = polytopes.hypercube(1, intervals, 'field')
3242
+ sage: TestSuite(P).run()
3243
+
3244
+ If the dimension ``dim`` is not equal to the length of intervals, an
3245
+ error is raised::
3246
+
3247
+ sage: u_cube = polytopes.hypercube(2, intervals=[[0,1],[0,2],[0,3]])
3248
+ Traceback (most recent call last):
3249
+ ...
3250
+ ValueError: the dimension of the hypercube must match the number of intervals
3251
+
3252
+ The intervals must be pairs `(a, b)` with `a < b`::
3253
+
3254
+ sage: w_cube = polytopes.hypercube(3, intervals=[[0,1],[3,2],[0,3]])
3255
+ Traceback (most recent call last):
3256
+ ...
3257
+ ValueError: each interval must be a pair `(a, b)` with `a < b`
3258
+
3259
+ If a string besides 'zero_one' is passed to ``intervals``, return an
3260
+ error::
3261
+
3262
+ sage: v_cube = polytopes.hypercube(3, intervals='a_string')
3263
+ Traceback (most recent call last):
3264
+ ...
3265
+ ValueError: the only allowed string is 'zero_one'
3266
+
3267
+ Check that we set up the hypercube correctly::
3268
+
3269
+ sage: ls = [randint(-100,100) for _ in range(4)]
3270
+ sage: intervals = [[x, x+randint(1,50)] for x in ls]
3271
+ sage: P = polytopes.hypercube(4, intervals, backend='field')
3272
+ sage: P1 = polytopes.hypercube(4, intervals, backend='ppl') # needs pplpy
3273
+ sage: assert P == P1 # needs pplpy
3274
+
3275
+ Check that coercion for input intervals is handled correctly::
3276
+
3277
+ sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1]])
3278
+ sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1.0]]) # needs cddexec
3279
+ sage: P = polytopes.hypercube(2, [[1/2, 2], [0, AA(2).sqrt()]]) # needs sage.rings.number_field
3280
+ sage: P = polytopes.hypercube(2, [[1/2, 2], [0, 1.0]], backend='ppl') # needs pplpy
3281
+ Traceback (most recent call last):
3282
+ ...
3283
+ ValueError: specified backend ppl cannot handle the intervals
3284
+ """
3285
+ parent = Polyhedra(ZZ, dim, backend=backend)
3286
+ convert = False
3287
+
3288
+ # If the intervals are (a_1,b_1), ..., (a_dim, b_dim),
3289
+ # then the inequalities correspond to
3290
+ # b_1,b_2,...,b_dim, a_1,a_2,...,a_dim
3291
+ # in that order.
3292
+
3293
+ if intervals is None:
3294
+ cp = itertools.product((-1, 1), repeat=dim)
3295
+
3296
+ # An inequality -x_i + 1 >= 0 for i < dim
3297
+ # resp. x_{dim-i} + 1 >= 0 for i >= dim
3298
+ ieq_b = lambda i: 1
3299
+
3300
+ elif isinstance(intervals, str):
3301
+ if intervals == 'zero_one':
3302
+ cp = itertools.product((0,1), repeat=dim)
3303
+
3304
+ # An inequality -x_i + 1 >= 0 for i < dim
3305
+ # resp. x_{dim-i} + 0 >= 0 for i >= dim
3306
+ ieq_b = lambda i: 1 if i < dim else 0
3307
+ else:
3308
+ raise ValueError("the only allowed string is 'zero_one'")
3309
+ elif len(intervals) == dim:
3310
+ if not all(a < b for a,b in intervals):
3311
+ raise ValueError("each interval must be a pair `(a, b)` with `a < b`")
3312
+ parent = parent.base_extend(sum(a + b for a,b in intervals))
3313
+ if parent.base_ring() not in (ZZ, QQ):
3314
+ convert = True
3315
+ if backend and parent.backend() is not backend:
3316
+ # If the parent changed backends, but a backend was specified,
3317
+ # the specified backend cannot handle the intervals.
3318
+ raise ValueError("specified backend {} cannot handle the intervals".format(backend))
3319
+
3320
+ cp = itertools.product(*intervals)
3321
+
3322
+ # An inequality -x_i + b_i >= 0 for i < dim
3323
+ # resp. x_{dim-i} - a_i >= 0 for i >= dim
3324
+ ieq_b = lambda i: intervals[i][1] if i < dim \
3325
+ else -intervals[i-dim][0]
3326
+ else:
3327
+ raise ValueError("the dimension of the hypercube must match the number of intervals")
3328
+
3329
+ # An inequality -x_i + ieq_b(i) >= 0 for i < dim
3330
+ # resp. x_{dim-i} + ieq_b(i-dim) >= 0 for i >= dim
3331
+ ieq_A = lambda i, pos: -1 if i == pos \
3332
+ else 1 if i == pos + dim \
3333
+ else 0
3334
+ ieqs = (tuple(ieq_b(i) if pos == 0 else ieq_A(i, pos-1)
3335
+ for pos in range(dim+1))
3336
+ for i in range(2*dim))
3337
+
3338
+ return parent([cp, [], []], [ieqs, []], convert=convert, Vrep_minimal=True, Hrep_minimal=True, pref_rep='Hrep')
3339
+
3340
+ def cube(self, intervals=None, backend=None):
3341
+ r"""
3342
+ Return the cube.
3343
+
3344
+ The cube is the Platonic solid that is obtained as the convex hull of
3345
+ the eight `\pm 1` vectors of length 3 (by default). Alternatively, the
3346
+ cube is the product of three intervals from ``intervals``.
3347
+
3348
+ .. SEEALSO::
3349
+
3350
+ :meth:`hypercube`
3351
+
3352
+ INPUT:
3353
+
3354
+ - ``intervals`` -- list (default=None). It takes the following
3355
+ possible inputs:
3356
+
3357
+ - If the input is ``None`` (the default), returns the convex hull of
3358
+ the eight `\pm 1` vectors of length three.
3359
+
3360
+ - ``'zero_one'`` -- string; return the `0/1`-cube
3361
+
3362
+ - a list of 3 lists of length 2. The cube will be a product of
3363
+ these three intervals.
3364
+
3365
+ - ``backend`` -- the backend to use to create the polytope
3366
+
3367
+ OUTPUT: a cube as a polyhedron object
3368
+
3369
+ EXAMPLES:
3370
+
3371
+ Return the `\pm 1`-cube::
3372
+
3373
+ sage: c = polytopes.cube()
3374
+ sage: c
3375
+ A 3-dimensional polyhedron in ZZ^3 defined as the convex hull of 8 vertices
3376
+ sage: c.f_vector()
3377
+ (1, 8, 12, 6, 1)
3378
+ sage: c.volume()
3379
+ 8
3380
+ sage: c.plot() # needs sage.plot
3381
+ Graphics3d Object
3382
+
3383
+ Return the `0/1`-cube::
3384
+
3385
+ sage: cc = polytopes.cube(intervals ='zero_one')
3386
+ sage: cc.vertices_list()
3387
+ [[1, 0, 0],
3388
+ [1, 1, 0],
3389
+ [1, 1, 1],
3390
+ [1, 0, 1],
3391
+ [0, 0, 1],
3392
+ [0, 0, 0],
3393
+ [0, 1, 0],
3394
+ [0, 1, 1]]
3395
+ """
3396
+ return self.hypercube(3, backend=backend, intervals=intervals)
3397
+
3398
+ def cross_polytope(self, dim, backend=None):
3399
+ r"""
3400
+ Return a cross-polytope in dimension ``dim``.
3401
+
3402
+ A cross-polytope is a higher dimensional generalization of the
3403
+ octahedron. It is the convex hull of the `2d` points `(\pm 1, 0,
3404
+ \ldots, 0)`, `(0, \pm 1, \ldots, 0)`, \ldots, `(0, 0, \ldots, \pm 1)`.
3405
+ See the :wikipedia:`Cross-polytope` for more information.
3406
+
3407
+ INPUT:
3408
+
3409
+ - ``dim`` -- integer; the dimension of the cross-polytope
3410
+
3411
+ - ``backend`` -- the backend to use to create the polytope
3412
+
3413
+ EXAMPLES::
3414
+
3415
+ sage: four_cross = polytopes.cross_polytope(4)
3416
+ sage: four_cross.f_vector()
3417
+ (1, 8, 24, 32, 16, 1)
3418
+ sage: four_cross.is_simple()
3419
+ False
3420
+
3421
+ TESTS::
3422
+
3423
+ sage: cp = polytopes.cross_polytope(4, backend='normaliz') # optional - pynormaliz
3424
+ sage: TestSuite(cp).run() # optional - pynormaliz
3425
+
3426
+ ::
3427
+
3428
+ sage: P = polytopes.cross_polytope(6, backend='field')
3429
+ sage: TestSuite(P).run() # long time
3430
+
3431
+ Check that double description is set up correctly::
3432
+
3433
+ sage: P = polytopes.cross_polytope(6, backend='ppl')
3434
+ sage: Q = polytopes.cross_polytope(6, backend='ppl')
3435
+ sage: P == Q
3436
+ True
3437
+ """
3438
+ verts = tuple((ZZ**dim).basis())
3439
+ verts += tuple(-v for v in verts)
3440
+ ieqs = ((1,) + x for x in itertools.product((-1,1), repeat=dim))
3441
+ parent = Polyhedra(ZZ, dim, backend=backend)
3442
+ return parent([verts, [], []], [ieqs, []], Vrep_minimal=True, Hrep_minimal=True, pref_rep='Vrep')
3443
+
3444
+ def parallelotope(self, generators, backend=None):
3445
+ r"""
3446
+ Return the zonotope, or parallelotope, spanned by the generators.
3447
+
3448
+ The parallelotope is the multi-dimensional generalization of a
3449
+ parallelogram (2 generators) and a parallelepiped (3 generators).
3450
+
3451
+ INPUT:
3452
+
3453
+ - ``generators`` -- list of vectors of same dimension
3454
+
3455
+ - ``backend`` -- the backend to use to create the polytope
3456
+
3457
+ EXAMPLES::
3458
+
3459
+ sage: polytopes.parallelotope([ (1,0), (0,1) ])
3460
+ A 2-dimensional polyhedron in ZZ^2 defined as the convex hull of 4 vertices
3461
+ sage: polytopes.parallelotope([[1,2,3,4], [0,1,0,7], [3,1,0,2], [0,0,1,0]])
3462
+ A 4-dimensional polyhedron in ZZ^4 defined as the convex hull of 16 vertices
3463
+
3464
+ sage: K = QuadraticField(2, 'sqrt2') # needs sage.rings.number_field
3465
+ sage: sqrt2 = K.gen() # needs sage.rings.number_field
3466
+ sage: P = polytopes.parallelotope([(1, sqrt2), (1, -1)]); P # needs sage.rings.number_field
3467
+ A 2-dimensional polyhedron in (Number Field in sqrt2 with defining
3468
+ polynomial x^2 - 2 with sqrt2 = 1.414213562373095?)^2 defined as
3469
+ the convex hull of 4 vertices
3470
+
3471
+ TESTS::
3472
+
3473
+ sage: TestSuite(P).run() # needs sage.rings.number_field
3474
+ """
3475
+ from sage.modules.free_module_element import vector
3476
+ generators = [vector(v) for v in generators]
3477
+ if not generators:
3478
+ return Polyhedron(backend=backend)
3479
+
3480
+ zero = generators[0] - generators[0]
3481
+ intervals = [Polyhedron([zero, gen], backend=backend) for gen in generators]
3482
+ return sum(intervals)
3483
+
3484
+ zonotope = parallelotope
3485
+
3486
+ # --------------------------------------------------------
3487
+ # imports from other files
3488
+ # --------------------------------------------------------
3489
+ try:
3490
+ associahedron = staticmethod(Associahedron)
3491
+ except ImportError:
3492
+ pass
3493
+
3494
+ try:
3495
+ flow_polytope = staticmethod(DiGraph.flow_polytope)
3496
+ edge_polytope = staticmethod(Graph.edge_polytope)
3497
+ symmetric_edge_polytope = staticmethod(Graph.symmetric_edge_polytope)
3498
+ except ImportError:
3499
+ pass
3500
+
3501
+
3502
+ polytopes = Polytopes()